一、简介
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
二、启动
第一步、引入maven依赖
| |
第二步、构建 SqlSessionFactory
MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。
| |
简单的mybatis-config.xml
| |
第三步、SqlSessionFactory 中获取 SqlSession
| |
也可以使用接口的方式调用
| |
作用域(Scope)和生命周期
SqlSessionFactoryBuilder:一旦创建了 SqlSessionFactory,就不再需要它,即会被丢弃。SqlSessionFactory:SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建,应用中应该只存在一个。SqlSession:每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域,不可以放在ThreadLocal当中Mapper Instances:用过之后即可废弃,并不需要显式地关闭映射器实例。
三、XML 配置文件
常用的可能修改默认值的settings
| 参数 | 描述 | 默认值 |
|---|---|---|
| lazyLoadingEnabled | 懒加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 | false |
| aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载 | false (true in ≤3.4.1) |
| mapUnderscoreToCamelCase | 否开启自动驼峰命名规则(camel case)映射 | False |
| lazyLoadTriggerMethods | 指定对象的哪个方法触发延迟加载 | equals,clone,hashCode,toString |
| defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。 (从3.4.5开始) | org.apache.ibatis.type.EnumTypeHandler |
typeAliases设置
类型别名是为 java 类型设置一个短的名字,也可以在类上使用@Alias("xxx")。它只和 XML 配置有关。例如:
| |
可能需要更换的默认typeHandlers
| 类型处理器 | Java 类型 | JDBC 类型 |
|---|---|---|
| EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引) |
| EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)。 |
自定义枚举处理器:
枚举类接口,定义统一规范
1 2 3 4public interface BaseEnum<E extends Enum<?>,T> { T getCode(); String getMessage(); }自定义枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35public enum InfoEnum implements BaseEnum<InfoEnum,String>{ SUCCESS("200","成功"), FAILD("500","错误"); private String code; private String message; static Map<String,InfoEnum> enumMap=new HashMap<String, InfoEnum>(); static{ (InfoEnum type:InfoEnum.values()){ enumMap.put(type.getCode(), type); } } public static Map<String,InfoEnum> getAll() { return enumMap; } private InfoEnum(String code, String message) { this.code = code; this.message = message; } @Override public String getCode() { return this.code; } @Override public String getMessage() { return this.message; } }自定义枚举类型处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26public class InfoEnumTypeHandler extends BaseTypeHandler<InfoEnum>{ @Override public void setNonNullParameter(PreparedStatement ps, int i, InfoEnum parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter.getCode()); } @Override public InfoEnum getNullableResult(ResultSet rs, String columnName) throws SQLException { String columnValue = rs.getString(columnName); return InfoEnum.getAll().get(columnValue); } @Override public InfoEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String columnValue = rs.getString(columnIndex); return InfoEnum.getAll().get(columnValue); } @Override public InfoEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String columnValue = cs.getString(columnIndex); return InfoEnum.getAll().get(columnValue); } }配置自定义类型处理器
1 2 3 4<!-- mybatis-config.xml --> <typeHandlers> <typeHandler handler="com.kun.start.typehandler.InfoEnumTypeHandler"/> </typeHandlers>
插件(plugins)
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
例自定义插件:
| |
四、XML映射文件
SQL 映射文件几个顶级元素
cache– 给定命名空间的缓存配置。cache-ref– 其他命名空间缓存配置的引用。resultMap– 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。sql– 可被其他语句引用的可重用语句块。insert– 映射插入语句update– 映射更新语句delete– 映射删除语句select– 映射查询语句
select
基本,
#{id}会在预编译语中被替换为?,当只有一个参数时候,#{param}可以是任意名称,当参数个数大于1时,使用#{arg0},#{arg1},#{arg2}...或者#{param1},#{param2},#{param3}...,此时习惯使用@Param("[参数名称]")来指定参数的名称。1 2 3<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id} </select>常用可选属性
属性 描述 parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用 resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用 flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false useCache 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED
insert, update 和 delete
常用可选属性(insert、update、delete)
属性 描述 parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认: unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表 有些数据库(例如Oracle)主键生成策略是序列化表,所以
select引入子标签selectKey1 2 3 4 5 6<insert id="insertUser" > <selectKey resultType="int" keyProperty="id" order="BEFORE"> select SEQ_USER_ID.nextval as id from dual </selectKey> insert into user (id,name,password) values (#{id},#{name},#{password}) </insert>selectKey相关属性
属性 描述 keyProperty selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表 resultType 结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。 order 这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用 statementType 与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型
sql
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:
| |
参数
使用 #{} 格式的语法会导致 MyBatis 创建 PreparedStatement 参数并安全地设置参数(使用 ? )。不过有时需要在 SQL 语句中插入一个不转义的字符串。比如 ORDER BY 就需要使用${}
缓存
Mybatis默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强处理循环。开启二级缓存需要加入cache标签
cache的效果如下:
- 映射语句文件中的所有 select 语句将会被缓存。
- 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
| |
收回策略有:
LRU– 最近最少使用的:移除最长时间不被使用的对象。FIFO– 先进先出:按对象进入缓存的顺序来移除它们。SOFT– 软引用:移除基于垃圾回收器状态和软引用规则的对象。WEAK– 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
使用cache-ref标签贡献缓存配置实例
| |
第三方缓存框架ehcache的使用
第一步:引入maven依赖
| |
第二步:配置ehcache配置文件
| |
第三步:在mybatis配置文件中配置别名
| |
第四步:在需要引用缓存的Mapper文件中引用
| |
五、结果集
resultMap子元素
- constructor - 用于在实例化类时,注入结果到构造方法中
- idArg- ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg - 将被注入到构造方法的一个普通结果
- id – 标记出作为 ID 的结果可以帮助提高整体性能
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个复杂类型的关联;许多结果将包装成这种类型
- collection – 一个复杂类型的集合
- discriminator – 使用结果值来决定使用哪个
- case – 基于某些值的结果映射
使用constructor子标签 ,注意多个参数,需要在POJO类构造函数上使用@Param指定名称
| |
使用association 子标签,进行多对一或者一对一关联查询,直接映射结构
| |
使用association 子标签,进行多对一或者一对一关联的嵌套查询,这种写法可能引起“N+1”问题,可以使用懒加载或关联的嵌套结果查询
| |
使用association 子标签,进行多对一或者一对一关联的嵌套结果查询,避免了“N+1"问题
| |
使用collection子标签,进行集合的嵌套查询,注意ofType指的是元素类型,javaType能推测出来可以不写
| |
使用collection子标签,进行集合的嵌套结果查询,避免多次发送SQL,提高效率
| |
使用discriminator 子标签完成鉴别器工作,相当于java中的switch语句。一般用于同表描述继承关系(不建议使用)
| |
六、动态 SQL
if,用于条件判断
| |
choose, choose-when, choose-otherwise
有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
| |
trim, where, set
用于解决where后跟无意义条件例如where 1=1问题
| |
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除
类似于where解决select语句问题,set解决update语句问题
| |
这里,set元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号
where和set底层都是使用trim。prefix/suffix-加入的前(后)缀,prefixOverrides/suffixOverrides-前后覆盖的字符
| |
foreach
动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:
| |
item-变量集合去除的元素,index-元素的索引,collection-集合参数名称。
集合类型可以是list、set、map的key、map的value。【index 作为map 的key。item为map的值】,例如:
| |
bind
可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值。
| |