01-Mybatis篇(新版)

概述

image-20210708172411945

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。2013年11月迁移到Github

Mybatis特性

  • MyBatis 是一款优秀的持久层框架

  • 支持自定义 SQL、存储过程以及高级映射。

  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

什么是持久层

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程

  • 内存:断电即失

  • 数据库(jdbc),io文件持久化。

为什么需要持久化?

  • 有一些数据,不能丢失

  • 内存太贵了

持久层

持久层:完成持久化工作的代码块

搭建MyBatis

相关依赖

创建MyBatis的核心配置文件

  • 习惯:一般命名为 mybatis-config.xml

  • 作用:配置链接数据库的环境、配置MyBatis

  • 放置位置:src/main/resources

创建mapper接口

  • 命名规则:POJO实体类的名字 + Mapper。

  • 相当于DAO,但是不用创建实现类,MyBatis会创建代理类,并执行映射文件当中的SQL。

创建MyBatis的映射文件

  • Mapper 接口当中的一个抽象方法对应映射文件当中的一个SQL语句。

  • 起名规则:POJO名字 + Mapper.xml

  • 放置位置:src/main/resources/UserMapper.xml

通过junit测试功能

从开始到创建 SqlSessionFactory 只用创建一次即可,因此可以单独封装即可。

openSession() 获得 SqlSession 默认是不自动提交事务,因此需要自己手动提交。

加入log4j日志功能

日志级别:FATAL(致命)> ERROR(错误)>WARN(警告)INFO(信息)> DEBUG(调试)。

  • 相关依赖

  • 放置位置:resources/log4j.xml

核心配置文件详解

  • mybatis-config.xml

  • mybatis的配置文件包含了会深深影响mybatis行为的设置和属性信息

    • configuration(配置)

      1. properties(属性)

      2. settings(设置)

      3. typeAliases(类型别名)

      4. typeHandlers(类型处理器)

      5. objectFactory(对象工厂)

      6. plugins(插件)

      7. environments(环境配置)

      8. environment(环境变量)

        • transactionManager(事务管理器)

        • dataSource(数据源)

      9. databaseIdProvider(数据库厂商标识)

      10. mappers(映射器)

  • mybatis-config.xml

environments

  • 可以配置多个环境,比如测试环境和开发环境 ;

  • 使用id区分,不能重复

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:

提示 如果正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

  • POOLED:使用数据库连接池

  • UNPOOLED:不使用数据库连接池,链接直接重新创建

  • JNDI:表示使用上下文当中的数据源(了解下)

属性(properties)

  • 可以通过properties属性来实现引用配置文件

  • 这些属性都是可外部配置且可动态替换的,既可以在典型的java属性文件中配置,亦可通过properties的元素子元素来传递。

比如,引入jdbc.properties文件

在核心配置文件中,必须按照顺序进行配置

  • 重写核心配置文件mybatis-config.xml

如果两个文件有同一个字段,优先使用外部配置文件

类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。

  • 仅用于 XML 配置,意在降低冗余的全限定类名书写。

在 Mapper.xml 文件中使用

设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置名
描述
有效值
默认值

cacheEnabled

全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。

true | false

true

lazyLoadingEnabled

延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。

true | false

false

aggressiveLazyLoading

开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。

true | false

false (在 3.4.1 及之前的版本中默认为 true)

multipleResultSetsEnabled

是否允许单个语句返回多结果集(需要数据库驱动支持)。

true | false

true

useColumnLabel

使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。

true | false

true

useGeneratedKeys

允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。

true | false

False

autoMappingBehavior

指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。

NONE, PARTIAL, FULL

PARTIAL

autoMappingUnknownColumnBehavior

指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)

NONE, WARNING, FAILING

NONE

defaultExecutorType

配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。

SIMPLE REUSE BATCH

SIMPLE

defaultStatementTimeout

设置超时时间,它决定数据库驱动等待数据库响应的秒数。

任意正整数

未设置 (null)

defaultFetchSize

为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。

任意正整数

未设置 (null)

defaultResultSetType

指定语句默认的滚动策略。(新增于 3.5.2)

FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)

未设置 (null)

safeRowBoundsEnabled

是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。

true | false

False

safeResultHandlerEnabled

是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。

true | false

True

mapUnderscoreToCamelCase

是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。

true | false

False

localCacheScope

MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。

SESSION | STATEMENT

SESSION

jdbcTypeForNull

当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。

JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。

OTHER

lazyLoadTriggerMethods

指定对象的哪些方法触发一次延迟加载。

用逗号分隔的方法列表。

equals,clone,hashCode,toString

defaultScriptingLanguage

指定动态 SQL 生成使用的默认脚本语言。

一个类型别名或全限定类名。

org.apache.ibatis.scripting.xmltags.XMLLanguageDriver

defaultEnumTypeHandler

指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)

一个类型别名或全限定类名。

org.apache.ibatis.type.EnumTypeHandler

callSettersOnNulls

指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。

true | false

false

returnInstanceForEmptyRow

当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)

true | false

false

logPrefix

指定 MyBatis 增加到日志名称的前缀。

任何字符串

未设置

logImpl

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

未设置

proxyFactory

指定 Mybatis 创建可延迟加载对象所用到的代理工具。

CGLIB | JAVASSIST

JAVASSIST (MyBatis 3.3 以上)

vfsImpl

指定 VFS 的实现

自定义 VFS 的实现的类全限定名,以逗号分隔。

未设置

useActualParamName

允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)

true | false

true

configurationFactory

指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)

一个类型别名或完全限定类名。

未设置

shrinkWhitespacesInSql

从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)

true | false

false

defaultSqlProviderType

Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.

A type alias or fully qualified class name

Not set

一个配置完整的 settings 元素的示例如下:

其他配置

映射器(Mappers)

image-20210809125937789

MapperRegistry:注册绑定我们的Mapper文件;

  • 方式一

  • 方式二:使用class进行注册

  1. 接口和配置文件必须同名

  2. 接口和配置文件必须在同一个包下

  • 方式三:使用扫描包进行注册绑定,与上相同,但是能够注册多个

接口和配置文件,同名,就基本不会有问题。

生命周期与作用域

image-20210711172856278

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder

  • 一旦创建了 SqlSessionFactory,就不再需要它了

  • 局部变量

SqlSessionFactory

  • 说白了就是可以想象为︰数据库连接池

  • sqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例

  • 因此 SqlSessionFactory 的最佳作用域是应用作用域。

  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession

  • 连接到连接池的一个请求!

  • sqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

  • 用完之后需要赶紧关闭,否则资源被占用

image-20210711173745239

这里面的每一个Mapper,就代表一个具体的业务!

获取参数值的两种方式

绑定namespace

namespace中的包名和Dao/Mapper接口中的包名要一致。

单个字面量类型的参数

dao层

UserMapper.xml

‼️ 注意 ${} + 单引号的问题。其中传入的参数通过#{param} 或者 '${param}'传入,并且param的定义名称与Dao层的参数名称一样。

  • id:对应的namespace中的方法名

  • resultType:Sql语句执行的返回值

  • parameterType:指定传入的参数类型

注意,#{}是预编译处理,可以防止SQL注入;${}仅仅是字符串替换。

多个字面量类型的参数

当 Mapper 接口中有多个参数的时候,MyBatis 会创建Map ,并使用 paramX 或 argX 为Key,参数值为 Value , 且两者均可混合使用。

dao层

UserMapper.xml

map集合类型的参数

dao层

UserMapper.xml

实体类类型的参数

必须有 getter / setter;单个POJO会形成一个Map,属性名作为Key,getter 后的值作为 value。

!!属性只跟 getter/setter 有关系。

dao层

UserMapper.xml

使用@Param标识参数

其会替换argXX,即只能用 {username, param1, password, param2 } 这四个在配置文件当中。

dao层

UserMapper.xml

List<POJO>

详见动态SQL - foreach。

MyBatis的各种查询功能

查询一个实体类对象

  • 注意设置 resultType 即可,可以使用别名。

  • 当SQL语句返回多条记录的时候,会报:TooManyResultException 的错误。

dao层

UserMapper.xml

查询一个List集合

注意设置 resultType 依然为 POJO 的类型。

dao层

UserMapper.xml

查询单个数据

dao层

UserMapper.xml

查询一条数据为map集合

  • 查询的结果没有对应的实体类的时候,就可以使用Map集合。

  • resultType 设置成 map 即可。

注意:查询为null 的字段是不会放到Map集合里面。

dao层

UserMapper.xml

  • 也可以使用 Map 存放多条记录,需要使用到 @MapKey 注解。

dao层

UserMapper.xml

数据

查询多条数据为map集合

dao层

UserMapper.xml

特殊SQL的执行

模糊查询

dao层

UserMapper.xml

批量删除 in

即传入的参数数量不确定,这里直接先构成一个字符串,再使用拼接方式传入即可。

dao层

UserMapper.xml

动态设置表名

表查询的字段相同,但是表名称不相同。

dao层

UserMapper.xml

获取自增的主键

场景:比如一个班级表和一个学生表,在一个事务当中先创建一个班级(使用的是自增的主键)然后在这个班级里面添加若干的学生。

dao层

UserMapper.xml

自定义映射resultMap

  • 字段名 和 属性名不一致(可以尝试别名处理)

  • 一对一 或 一对多 的关系查询

处理字段和属性的映射关系

开启下划线→小驼峰的配置,在 mybatis-config.xml 文件当中添加如下配置。

接下来使用,使用 ResltMap , 注意 resultMap 和 resultType 是二选一的。

多对一映射处理

建立数据库

例子:多个学生对应一个老师

  • 测试环境搭建

  1. 导入lombok

  2. 新建实体类 Teacher、Student

Pojo实体类

Teacher

Student

建立Mapper接口

Teacher

Student

建立 StudentMapper.xml 文件

按照结果嵌套处理

一对多映射处理

Pojo实体类

Student

Teacher

建立 TeacherMapper.xml

  • 按照结果嵌套处理

  • 按照查询嵌套处理

小结

  1. 关联- association【多对一】

  2. 集合- collection 【一对多】

  3. javaType & ofType

    • JavaType 用来指定实体类中属性的类型

    • ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!

注意点

  • 保证SQL的可读性,尽量保证通俗易懂

  • 注意一对多和多对一中,属性名和字段的问题!

  • 如果问题不好排查错误,可以使用日志,建议使用Log4j

动态SQL

根据传入的参数动态的决定最后执行的SQL语句。

if

choose, when, otherwise (switch)

MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

foreach(批量插删,IN)

经常用于批量添加 和 批量删除。

  • 批量插入

  • 批量删除

SQL片段

将重复的SQL代码抽取出来被多个语句重复使用。

MyBatis的缓存

MyBatis的一级缓存

一级缓存是SqlSession级别的缓存(即同一个链接),通过同一个SqlSession查询的数据。

以下四种情况会导致一级缓存失效:

  • 不同SqlSession对应不同一级缓存。(即不同SqlSession,即使是相同查询条件也无用)。

  • 同一个SqlSession但是查询条件不同。

  • 同一个SqlSession 两次查询期间执行了任何一次针对此表增删改操作。

  • 同一个SqlSession两次查询期间手动清空了缓存。

MyBatis的二级缓存

开启的四个条件

SqlSessionFactory 级别的,多个SqlSession可以共享。

核心配置文件,设置全局属性配置 cacheEnabled="true", 默认为 true,不需要设置。

在映射文件中设置<cache/>

二级缓存必须是在 SQLSession 关闭或提交之后有效。(即SQLSession关闭或提交后,一级缓存当中的数据才会保存到一级缓存中)。

实体类必须实现 Serializable 接口。

失效

任意一次增删改会清空二级缓存。(唯一情况)。

二级缓存的相关配置

在mapper配置文件中添加的cache标签可以设置一些属性:

  • eviction属性:缓存回收策略,默认的是LRU

    • LRU - 最近最少使用的:移除最长时间不被使用的对象

    • FIFO - 先进先出:按对象进入缓存的顺序来移除它们。

    • SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象

    • WEAK - 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

  • flushInterval属性:刷新间隔,单位毫秒

    • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

  • size属性:引用数目,正整数

    • 代表缓存最多可以存储多少哥对象,太大容易导致内存溢出

  • readOnly属性:只读,true/false

    • true:只读缓存,会给所有调用则返回缓存对象的相同实例。因此这些对象不能被修改,这提供了很重要的性能优势。

MyBatis缓存查询的顺序

  1. 先查询二级缓存,因为二级缓存当中可能会有其他线程已经查询出来的数据。

  2. 二级缓存没有命中,则再查询一级缓存。

  3. 一级缓存也没有命中,则执行查询数据库。

  4. SQLSession关闭之后,一级缓存当中的数据会写入到二级缓存。

整合第三方缓存EHCache(了解)

Ehcache是一种广泛使用的开源Java分布式缓存。

要在程序中使用encache,导入包:

mybatis-encache

在指定mapper中指定实现ehcache缓存

分页功能

使用Limit分页

使用mybatis实现分页:

  • dao层

UserMapper.java

UserMapper.xml

  • 测试

RowBounds分页

  • dao层

UserMapper.java

UserMapper.xml

  • 测试

分页插件

PageHelper

image-20210809133611851

插件地址:https://pagehelper.github.io/

LomBok

Project Lombok 是一个 Java 库,可自动插入您的编辑器并构建工具,为您的 Java 增添趣味。 永远不要再编写另一个 getter 或 equals 方法,通过一个注释,您的类就有一个功能齐全的构建器,自动化您的日志变量等等。

使用步骤

  • 在IDEA中安装Lombok插件

  • 在项目中导入lombok的jar包

image-20210907174010532

@Data:无参构造,get、set、tostring、hashcode、equals

如果使用lombok会导致代码可读性低。

使用注解开发

案例

dao层

mybatis-config.xml

但是在处理复杂的语句上,就有点力不从心了。

CRUD

我们可以在工具类

  • 查询

UserMapper.java

  • 添加

UserMapper.java

注意:insert into 表名括号中的字段与数据库中的相同,values中的字段与实体类的字段相同。

  • 删除

UserMapper.java

  • 更新

UserMapper.java

拓展:#{}能够很大程度上防止sql注入,${}无法阻止

关于@Param()注解

  • 基本类型的参数或者String类型,需要加上

  • 引用类型不需要加

  • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上!

  • 我们在SQL中引用的就是我们这里的@Param()中设定的属性名!

最后更新于

这有帮助吗?