MyBatis从入门到精通读书笔记

本文详细介绍了MyBatis的XML和注解方式的基本用法,包括SQL查询、插入、更新、删除操作,以及参数处理和结果映射。讨论了动态SQL的if、choose、where、set、trim和foreach标签的使用,解释了如何处理多数据库支持和OGNL表达式。此外,还涵盖了MyBatis的一级缓存和二级缓存机制,以及插件开发和Spring集成的相关内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第 1 章 MyBatis 入门
第 2 章 MyBatis XML方式的基本用法
权限控制需求
采用 RBAC (Role-Based Access Control,基于角色 的访问控制)方式
一个用户拥有若干角色, 一个角色拥有若干权限, 权限就是对某个资源(模块〉的某种操作(增、删、改、查),这样就构成了“用户·角色-权 限”的授权模型

实体类中byte[]这个类型一般对应数据库中的 BLOB、 LONGVARBINARY 以及一些 和二进制流有关的字段类型,

在实体类中不要使用基本类型。
2.2 使用 XML 万式
2.3 select 用法
<select>: 映射查询语句使用的标签。
• id:命名空间中的唯一标识符,可用来代表这条语句。
resultMap:用于设置返回值的类型和映射关系。

{id} : MyBatis SQL 中使用预编译参数的一种方式,大括号中的 id 是传入的参数名。

通过 resultMap 中配置的 column 和 property 可以将查询列的值映射到 type 对象的属性上

resultMap 包含的所有属性如下
id:必填,并且唯一
type:必填,用于配置查询列所映射到的 Java 对象类型。
extends :选填,可以配置当前的 resultMap 继承自其他的 resultMap,属性值为 继承 resultMap 的 id。
autoMapping:选填,可选值为 true 或 false,用于配置是否启用非映射字段(没 有在 resultMap 中配置的字段〉的自动映射功能 , 该配置可以覆盖全局的 autoMappingBehavior 配置。

resultMap 包含的所有标签如下。
constructor: 配置使用构造方法注入结果
result: 注入到 Java 对象属性的普通结果。
association: 一个复杂的类型关联,许多结果将包成这种类型。
collection: 复杂类型的集合。
discriminator:根据结果值来决定使用哪个结果映射。
case: 基于某些值的结果映射。

可以通过在 resultMap 中配直 property 属性和 column 属性的映射,或者在 SQL 中 设直别名这两种方式实现将查询列映射到对象属性的目的

property 属性或别名和对象中属性的名字相同,但是实际匹配时, MyBatis 会先将两者都转换为大写形式,然后再判断是否相同, 所以只要字母对应即可

当存在对象嵌套时候, sql别名使用
u.user name as ”user.userName”
user为嵌套对象的参数名, 这样可以设置到user对象的userName属性中
2.4 insert 用法
<insert>元素,这个标签包含如下属性。
id:命名空间中的唯一标识符,可用来代表这条语句。

parameterType:即将传入的语句参数的完全限定类名或别名

flushCache:默认值为 true,任何时候只要语句被调用,都会清空一级缓存和二级缓存。

timeout:设置在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。

statementType:对于 STATEMENT、 PREPARED、 CALLABLE, MyBatis 会分别使用 对应的 Statement 、 PreparedStatement 、 CallableStatement ,默认值为PREPARED。

useGeneratedKeys:默认值为 false。 如果设置为 true, MyBatis 会使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键。

keyProperty: MyBatis 通过 getGeneratedKeys 获取主键值后将要赋值的属性名 。 如果希望得到多个数据库自动生成的列,属性值也可以是以逗号分隔的属性名称列表。

keyColumn:仅对时SERT 和 UPDATE 有用 。 通过生成的键值设置表中的列名, 这个 设置仅在某些数据库(如 PostgreSQL)中是必须的, 当主键列不是表中的第一列时需 要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。

database Id:如果配置了 databaseidProvider (4.6 节有详细配置方法), MyBatis 会加载所有的不带 databaseid 的或匹配当前 databaseid 的语句。 如果同时存在带 databaseid 和不带 databaseid 的语句,后者会被忽略。

为了防止类型错误,对于一些特殊的数据类型,建议指定具体的 jdbcType 值。
例如 headimg 指定 BLOB 类型, createTime 指定 TIMESTAMP 类型。

BLOB 对应的类型是 ByteArrayinputStream,就是二进制数据流。
由于数据库区分 date、 time、 datetime 类型,但是 Java 中一般都使用Date 类型。
因此为了保证数据类型的正确,需要手动指定日期类型, date、 time、 datetime 对应的 JDBC 类型分别为 DATE、 TIME、 TIMESTAMP
2.4.2 使用 JDBC 方式返回主键自增的值
然后在 XML 中新增一个 insert2 方法。
<insert i d=” insert2” useGeneratedKeys=” true ” keyProperty=” id” >

insert 标签上配置了如下两个属性。
useGeneratedKeys=” true”
keyProperty=” id”

useGeneratedKeys 设置为 true 后, MyBatis 会使用 JDBC 的 getGeneratedKeys 方法来取出 由数据库内部生成的主键。获得主键值后将其赋值给 keyProperty 配置的 id 属性。 当需要设置多个属性时,使用逗号隔开,这种情况下通常还需要设置 keyColumn 属性, 按顺 序指定数据库的列,这里列的值会和 keyProperty 配置的属性一一对应。
2.4.3 使用 selectKey 返回主键的值
2.4.2这种回写主键的方法只适用于支持主键自增的数据库。

Oracle这种不支持自增的可以使用<selectKey>标签来获取主键的值, 这种也适用mysql

insert into sys_user… SELECT LAST_INSERT_ID()

selectKey 标签的 keyColumn、 keyProperty 和上面 useGeneratedKeys 的用法含 义相同,这里的 resultType 用于设置返回值类型。
order 属性的设置和使用的数据库有关, 是执行前获取还是执行后获取
在 MySQL 数据库中, order 属性设置的值是 AFTER,因为当前记录的主键值在 insert 语句 执行成功后才能获取到。
而在 Oracle 数据库中, order 的值要设置为 BEFORE,这是因为 Oracle 中需要先从序列获取值,
2.5 update 用法
Update标签
2.6 delete 用法
Delete标签
2.7 多个参数的用法
使用 Map 类型作为参数或使用@Param 注解。

不使用@Param 注解时候xml可用的参数为0、1或paraml, param2这种, 不能自定义名称

@Param (”userId”) Long userId

给参数配置@ Param 注解后, MyBatis 就会自动将参数封装成 Map 类型,@Param 注解值 会作为 Map 中的 key,因此在 SQL 部分就可以通过配置的注解值来使用参数。

将接口方法中的参数换成 JavaBean 类型并适用@Param
@Param (”user”) SysUser user
使用#{user.id}和#{user. enabled}这种方式取值
2.8 Mapper 接口动态代理实现原理
JDK代理
当调用一个接口的方法时,会先通过接口的全限定名称和当前调用的方法名的组合得到一个方法 id,这个 id 的值就是映射 XML 中口arnespace 和具体方法 id 的组合。

所以可以在代理方法中使用 sqlSession 以命名空间的方式调用方法。通过这种方式可以将接口和 XML 文件中的方法关联起来。这种代理方式和常规代理的不同之处在于,这里没有对某个具体类进行代理,而是通过代理转化成了对其他代码的调用。

第 3 章 MyBatis注解方式的基本用法
My Batis 注解方式就是将 SQL 语句直接写在接口上。
这种方式的优点是,对于需求比较简单的系统,效率较高。缺点是,当 SQL 有变化时都需要重新编译代码,一般情况下不建议使用注解方式。
在 MyBatis 注解 SQL 中,最基本的就是@ Select、@ Insert、@Update 和@Delete 四种
第4章 MyBatis动态SQL
以下是MyBatis的动态SQL在XML中支持的几种标签。
if
choose (when、oterwise)
trim (where、 set)
foreach
bind
本章除了讲解这几种标签的用法外,还会介绍如何在一个XML中针对不同的数据库编写不同的SQL语句,另外会对这5种标签中必须要用到的OGNL表达式进行一个简单的介绍
4.1 if用法
4.1.1 在WHERE条件中使用if
<if test=” userEmail != null and userEmail !=””>
判断条件 property ! =null 或 property == null:
判断条件 property != ” 或 property !=”:仅适用于 String 类型的宇段
• and 和 or: 当有多个判断条件时,使用 and 或 or 进行连接,嵌套的判断可以使用小括号分组, and 相当于 Java 中的与(&&), or 相当于 Java 中的或 (||)
4.1.2 在 UPDATE 更新列中使用 if
只更新有变化的字段 为null的属性不更新
update sys user set
<if test=” userName != null and userName !=””>
user name= #{userName},

4.1.3 在INSERT动态插入列中使用if
insert into sys user( user_name, user_password,
<if test=”userEmail != null and userEmail != ””>
user email,

在 INSERT 中使用时要注意,若在列的部分增加 if 条件,
则 values 的部分也要增加相 同的 if 条件,必须保证上下可以互相对应,完全匹配。
4.2 choose 用法
实现 if. … else、 if … else .
就需要用到 choose when otherwise 标签
choose 元素 中包含 when和 otherwise 两个标签,一个 choose 中至少有一个 when,有 0 个或者 l 个 otherwise。
Select * from sys_user where 1 = 1

<when test=” id != nul l ” > and id= #{id}
<when test=” userName != null> and user name = #{userName}
and 1 = 2

4.3 where、 set、 trim 用法
4.3.1 where 用法
where 标签的作用:如果该标签包含的元素中有返回值,就插入一个 where;
如果 where 后面的字符串是以 AND 和 OR 开头的,就将它们剔除。
4.3.2 set 用法
用在update语句中
set 标签的作用:如果该标签包含的元素中有返回值,就插入一个 set:
如果 set 后面的 字符串是以逗号结尾的,就将这个逗号剔除。

<if test=” userName != null> user name= #{userName} ,

4.3.3 trim 用法
where 和 set 标签 的功能都可以用 trim标签来实现
4.4 foreach 用法
<foreach collection=” list ” open=” (” close=” )” separator=”,” item=” id” index=”i” >
#{id}

foreach 包含以下属性
collection: 必填,值为要选代循环的属性名。@Param设置 或者 使用默认情况list为集合array为数组
item:变量名,值为从法代对象中取出的每一个值。
index:索引的属性名,在集合数组情况下值为当前索引值,当选代循环的对象是 Map 类型时,这个值为 Map 的 key (键值)。
open:整个循环内容开头的字符串 。
close: 整个循环内容结尾的字符串。
separator:每次循环的分隔符。
4.4.2 foreach 实现批量插入
Values地方循环即可
4.4.3 foreach 实现动态 UPDATE
当参数是 Map 类型的时候, foreach 标签的index 属性值对应的不是索引值,而是 Map 中的 key,利用这个 key 可以实现动态 UPDATE
4.5 bind 用法
bind 标签可以使用 OGNL 表达式创建一个变量井将其绑定到上下文中。
<if test=” userNarne != null and userNarne !=””>
<bind name= "userNarneLike” value=”’ % ’+ userNarne + ’ %’”/>
and user name like #{userNarneLike}

bind 标签的两个属性都是必选项, name 为绑定到上下文的变量名, value 为 OGNL 表 达式。
创建一个 bind 标签的变量后, 就可以在下面直接使用,使用 bind 拼接字符串不仅可 以避免因更换数据库而修改 SQL,也能预防 SQL 注入
4.6 多数据库支持
通过在select标签中配置database Id=” mysql” 这种即可以区分不同数据库的sql
4.7 OGNL 用法
MyBatis 常用的 OGNL 表达式如下。

  1. e1 or e2
  2. e1 and e2
  3. e1 == e2 或 e1 eq e2
  4. e1 ! = e2 或 e1 neq e2
  5. e1 lt e2:小于
  6. e1 lte e2:小于等于,其他表示为 gt (大于)、 gte (大于等于)
  7. e1 + e2 、 e1 * e2 、 e1/e2 、 e1 - e2 、 e1 % e2
  8. !e 或 not e:非,取反
  9. e.method(args ) : 调用对象方法
  10. e.property: 对象属性值
  11. el[ e2 ] : 按索引取值( List、数组和 Map)
  12. @class@method(args ):调用类的静态方法
  13. @class@field:调用类的静态字段值
    第 5 章 Mybatis代码生成器
    代码生成器一一MyBatis Generator
    第6章MyBatis高级查询
    本章主要包含的内容为 MyBatis 的高级结果映射,主要处理数据库一对一、 一对多的查询, 另外就是在 MyBatis 中使用存储过程的方法,处理存储过程的入参和出参方法,最后会介绍 Java 中的枚举方法和数据库表宇段的处理方法。
    6.1 高级结果映射
    6.1.1 一对一映射
    6.1 .1.1 使用自动映射处理一对一关系
    SysUser 类中增加 SysRole role字段
    使用自动映射就是通过别名让 MyBatis 自动将值匹配到对应的宇段上,
    select …
    r.role_name” role.roleName”, 使用别名方式
    通过这种方式将 role 的属性都映射到了SysUser 的 role 属性上。
    MyBatis 会先查找 role 属性,如果存 在 role 属性就创建 role 对象,然后在 role 对象中继续查找 roleName
    6.1 .1.2 使用 resultMap 配置一对一映射
    6.1.1.1中的业务的resultMap方式, 只要property有role前缀
    <result property=” role.id” column=” role_id”/>

MyBatis 是支持 resultMap 映射继承的, 继承之后自动拥有父类的全部映射
<resultMap id=” userRoleMap” extends=”userMap” type=” tk.mybatis . simple . model .
SysUser"> <result property="role . id” column=” role_id” />
6.1.1.3使用resultMap的association标签配置一对一映射
association标签用于和一个复杂的类型进行关联,即用于一对一的关联配置。
在6.1.1.2的基础上,再做修改,改成 association 标签的配置方式,
<resultMap id=” userRoleMap” extends=”userMap” type=” tk.mybatis.simple. model.SysUser”> <association property=”role ” columnPrefix=”role_” javaType=”tk .mybatis. simple.model . SysRole”>
<result property=” id” column=” id” />

association 标签包含以下属性。
property:对应实体类中的属性名,必填项。
javaType: 属性对应的 Java 类型。
resultMap: 可以直接使用现有的 resultMap,而不需要在这里配置。
columnPrefix:查询列的前缀,配置前缀后,在子标签配置 result 的 .column 时可以省略前缀。Sql中映射到此map的列都要有role_前缀
除了这些属性外,还有其他属性,

使用 association 配置时还可以使用 resultMap 属性配置成一个已经存在的 resultMap 映射,也就是说可以将列的那些映射单独提取出去
<resultMap id=”userRoleMap” extends=” userMap” type=” tk.mybatis . simple . model .
SysUser” > <association property=” role ” columnPrefix=” role ” resultMap=” roleMap” />

<resultMap id=” roleMap” type=” tk.mybatis.s工mple . model.SysRole ”>

这个和上association标签对应

6.1.1.4association标签的嵌套查询
利用简单的 SQL 通过多次查询转换 为我们需要的结果,

association 标签的嵌套查询常用的属性如下。
select:另一个映射查询的 id, MyBatis 会额外执行这个查询获取嵌套对象的结果,是个dao中的方法
column :列名(或别名),将主查询中列的结果作为嵌套查询的参数,配置方式如 column={propl=coll , prop2=col2}, propl 和 prop2 将作为嵌套查询的参数。
fetchType:数据加载方式,可选值为 lazy 和 eager,分别为延迟加载和积极加载, lazy时候当调用相应的获取方法时候才会执行sql, 比如属性的get方法, 这个配置会覆盖全局的 lazyLoadingEnabled 配置。这个配置需要和一些全局的配置一起使用才能生效

案例
<resultMap id=” userRoleMapSelect” extends=” userMap”
type=”tk .mybatis. simple.model . SysUser”>
<association property=”role” fetchType=”lazy"
select=”tk.mybatis . simple.mapper .RoleMapper. selectRoleByid” column=” {id=role id}” />
6.1.2 一对多映射
一对多映射只有两种配置 方式,都是使用 collection 标签进行的,
6.1.2.1 collection集合的嵌套结果映射
配置在resultMap中的子集合, 在collection标签中配置自己的属性
<collection property=”roleList” columnPrefix=”role_”
javaType=”tk . mybatis.simple.model.SysRole”>
<id property=” id” column=” id” />
<result property=” roleName” column=” role_name ” />

对于collection的查询MyBatis 在处理结果的时候,会判断结果是否相同,如果是相同的结果, 则只会保留第一个结果,
MyBatis 判断结果是否相同时,最简单的情况就是在映射配置中至少有一个 id 标签, 在 userMap 中配置如下。<id property=” id” column=” id” />
所以当子数据的父id都一样时候, 会只有一条

没有配置 id 时, MyBatis 就会把 resultMap 中配置的所有字段进行比较,如果所有字段的值都相同就合并,只要有一个字段值不同,就不合井。

在嵌套结果配直 id 属性时 如果查询语句中没有查询 id 属性配直的列,就会导致 id 对应的值为 null。
这种情况下,所有值的 id 都相同,因此会使嵌套的集合中只有一条数据。 所以在配直 id时,查询语句中必须包含该列 。
6.1.2.2 collection集合的嵌套查询


</ resultMap>
6.1.3 鉴别器映射
鉴别器非常容易理解,因为它很像 Java 语言中的 switch 语句。

<resultMap id= ” rolePrivilegeListMapChoose ” type=” tk . odel . SysRole” >
<discriminator column=”enabled” javaType=” int” >
<case value=” 1” resultMap=” rolePrivilegeListMapSelect ” / >
<case value=”0 ” resultMap=” roleMap” />
< discriminator >

角色的属性 enable 值为 1 的时候表示状态可用 , 为 0 的时候表示状态不可用。当角色可用时, 使用 rolePrivilegeListMapSelect 映射,
当角色被禁用时,只能获取角色的基本信息,不能获得角色的权限信息。

discriminator 标签常用的两个属性如下。
column: 该属性用于设置要进行鉴别比较值的列。
javaType: 该属性用于指定列的类型,保证使用相同的 Java 类型来比较值。

discriminator 标签可以有 1个或多个 case 标签,
case 标签包含以下三个属性。
value: 该值为 discriminator 指定 column 用来匹配的值。
resultMap: 当 column 的值和 value 的值匹配时,可以配置使用 resultMap 指定 的映射, resultMap 优先级高于 resultType。
resultType: 当 column 的值和 value 的值匹配时,用于配置使用 resultTyp e指定的映射。

case 标签下面可以包含的标签和 resultMap 一样,用法也一样

鉴别器是一种很少使用的方式, 在使用前一定要完全掌握,没有把握的情况下要尽可能避 免使用。
6.2 存储过程***
6.3 使用枚举或其他对象
属性为枚举类型时候需要使用枚举处理器, 提供了两个, 也可以自定义处理器
6.3.3 对 Java 8 日期( JSR-310 )的支持
6.4 本章小结
在本章中,我们通过一个循序渐进的过程学习了复杂的高级结果映射方法,虽然高级结果 映射比较复杂,但是了解了整个过程中一步一步的变化后便可以很容易地掌握其用法。全面地 学习存储过程后, 大家应该能了解 MyBatis 在存储过程方面的不同用法。最后通过枚举和日期 类型,简单介绍了与 TypeHandler 相关的用法和配置。经过这一章, MyBatis 中的各种不同用法就基本介绍完了。
第7章MyBatis缓存配置
7.1 一级缓存
一级缓存(也叫本地缓存〉默认会启用,

MyBatis的一级缓存存在于SqlSession 的生命周期中,在同一个 SqlSession 中查询时, MyBatis 会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个 Map 对象中。如果同一个 SqlSession 中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当 Map 缓存对象中己经存在该键值时,则会返回缓存中的对象

flushCache=" true” 这个属性配置为 true 后 会在查询数据前清空当前的一级缓

一级缓存是和 SqlSession 绑定的,只存在于 SqlSession的生命周期中。
任何的 INSERT、 UPDATE、 DELETE 操作都会清空一级缓存,
7.2 二级缓存
存在于 SqlSessionFactory 的生命周期中
7.2.1 配置二级缓存
在 MyBatis 的全局配置 settings 中有 一个参数 cacheEnabled,这个参数是二级缓存的全局开关,默认值是 true ,初始状态为启用状态。 如果把这个参数设置为 false,即使有后面的二级缓存配置,也不会生效。 由于这个参数值默认为 true ,所以不必配置,如果想要配置,可以在 mybatis-config.xml 中添加如下代码。

<setting name=” cacheEnabled” value=” true” />

MyBatis 的二级缓存是和命名空间绑定的,即二级缓存需要配置在 Mapper.xml 映射文件中 ,或者配置在 Mapper.java接口中。
在映射文件中 命名空间就是 XML 根节点 mapper 的 namespace 属性。
在 Mapper 接口中命名空间就是接口的全限定名称。
7.2.1.1 Mapper.xml 中配置二级缓存
在保证二级缓存的全局配置开启的情况下
开启 二级缓存只需要在 UserMapper.xml 中添加<cache/>元素即可,
<mapper namespace=” t k . mybatis . s 工mple.mapper . RoleMapper”〉

默认的二级缓存会有如下效果。
• 映射语句文件中的所有 SELECT 语句将会被缓存。
• 映射语句文件中的所有 时INSERT、 UPDATE、 DELETE 语句会刷新缓存。 缓存会使用 Least Recently Used (LRU,最近最少使用的)算法来收回。
• 根据时间表(如 no Flush Interval,没有刷新间隔),缓存不会以任何时间顺序来刷新。
•缓存会存储集合或对象(无论查询方法返回什么类型的值)的 1024 个引用。
• 缓存会被视为 read/write (可读/可写)的, 意味着对象检索不是共享的,而且可以安全 地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

所有的这些属性都可以通过缓存元素的属性来修改,示例如下。
<cache eviction=” FIFO” flushlnterval=” 60000” size=” 512” readOnly=” true" />
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新一次,存储集合或对象的 512 个引用 , 而且返回的对象被认为是只读的, 因此在不同线程中的调用者之间修改它们会导致冲 突

cache 可以配置的属性如下。
eviction (收回策略)
LRU (最近最少使用的): 移除最长时间不被使用的对象,这是默认值。
FIFO (先进先出〉: 按对象进入缓存的顺序来移除它们。
SOFT (软引用): 移除基于垃圾回收器状态和软引用规则的对象。
WEAK (弱引用): 更积极地移除基于垃圾收集器状态和弱引用规则的对象。

flushinterval (刷新间隔〉。可以被设置为任意的正整数, 而且它们代表一个合理的毫秒形式的时间段。 默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。

size (引用数目)。 可以被设置为任意正整数,要记住缓存的对象数目和运行环境的可 用内存资源数目。默认值是 1024。

readOnly (只读)。属性可以被设置为 true 或 false。只读的缓存会给所有调用者 返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。可读写的缓存会通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全 因此默认是false。
7.2.1.2 Mapper接口中配置二级缓存

7.2.2 使用二级缓存
使用 SerializedCache序列化缓存来实现可读写缓存类,井通过序列化和反序列化来保证通过缓存获取数据时,得到的是一个新的实例, 这个要求DO对象必须实现Serializable接口

如果配置为只读缓存, MyBatis 就会使用 Map 来存储缓存值, 这种情况下从缓存中获取的对象就是同一个实例。

当调用 close 方法关闭 SqlSession 时,SqlSession 才会保存查询数据到二级缓存中
在这之后二级缓存才有了缓存数据

MyBatis 默认提供的缓存实现是基于 Map 实现的内存缓存
7.3 集成 EhCache 缓存
同7.4
7.4 集成 Redis 缓存

  1. 添加项目依赖 在 pom.xml 中添加如下依赖。
  2. 配置 Redis 连接等
  3. 修改 RoleMapper.xml 中的缓存配置, 指定了type
    <cache type=” org.mybatis .caches . redis.RedisCache” />
    7.5 脏数据的产生和避免
    MyBatis 的二级缓存是和命名空间绑定的,所以通常情况下每一个 Mapper 映射文件都拥有 自己的二级缓存,不同 Mapper 的二级缓存互不影响

关联多表查询时肯定会将该查询放到某个命名空间下的映射文件中,这样一个多表的查询就会缓存在该命名空间的二级缓存中。涉及这些表的增、删、改操作通常不在一个映射文件中, 可能在其他文件中进行, 它们的命名空间不同, 因此当有数据变化时,多表查询的缓存未必会被清空,这种情况下就会产生脏数据。同一个Mapper下的insert等才会刷缓存

解决这个问题需要用到参照缓存了
当某几个表可以作为一个业务整体时,通常是让几个会关联的 ER 表同时使用同一个二级缓存,这样就能解决脏数据问题
<mapper namespace= ” tk.mybatis.simple.mapper. UserMapper” >
<cache-ref namespace= ” tk .mybatis . simple.mapper .RoleMapper” />

虽然这样可以解决脏数据的问题,但是并 不是所有的关联查询都可以这么解决,如果有几十个表甚至所有表都以不同的关联关系存在于各自的映射文件中时,使用参照缓存显然没有意义。
7.6 二级缓存适用场景
二级缓存虽然好处很多,但并不是什么时候都可以使用。
在以下场景中,推荐使用二级缓存。
以查询为主的应用中,只有尽可能少的增、删、改操作。
绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据。
可以按业务划分对表进行分组时, 如关联的表比较少,可以通过参照缓存进行配置。
除了推荐使用的情况,如果脏读对系统没有影响,也可以考虑使用。 在无法保证数据不出 现脏读的情况下, 建议在业务层使用可控制的缓存代替二级缓存。
7.7 本章小结
通过本章的学习,我们知道了一级缓存和二级缓存的区别,学会了如何配置二级缓存,除 了 MyBatis 默认提供的缓存外,还学会了如何集成 EhCache 和 Redis 缓存。 另外,我们认识到 了二级缓存可能带来的脏读问题,也学会了特定情况下解决脏读的办法。 MyBatis 的二级缓存 需要在特定的场景下才会适用,在选择使用二级缓存前一定要认真考虑脏读对系统的影响。在任何情况下,都可以考虑在业务层使用可控制的缓存来代替二级缓存。
第8章MyBatis插件开发
MyBatis 允许在己映射语句执行过程中的某一点进行拦截调用。
就是使用拦截器来处理mybatis调用数据库中的一些操作, 比如拦截query操作加入分页
PageHelper原理就是拦截器, 也叫插件

默认情况下,MyBatis 允许使用插件来拦截的接口和方法包括以下几个。
• Executor ( update 、 query 、 flushStatements 、 commit 、 rollback 、 get Transaction、 close、isClosed)
• ParameterHandler ( getParameterObj ect、 setParameters)
•ResultSetHandler ( handleResultSets 、handleCursorResultSets 、 handleOutputParameters)
• StatementHandler (prepare、 parameterize、 batch、 update、 query) 这 4 个接口及
8.1 拦截器接口介绍
MyBatis插件可以用来实现拦截器接口Interceptor (org.apache.ibatis.plugin.Interceptor),在实现类中对拦截对象和方法进行处理。

Interceptor 接口代码如下。
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target) ;
void setProperties(Properties properties);

setProperties 方法,这个方法用来传递插件的参数,可以通过参数来改变插件的行为。
参数值在拦截器的配置方法。在 mybatis-config.xml 中,一般情况下,拦截器的配置如下。

<plugin interceptor=” tk.mybatis . simple.plugin . XXXInterceptor">
<property name=”propl ” value=”valuel ”/>
<property name=”prop2 ” value=”value2 ”/>


plugin 的 interceptor 属性为拦截器实现类的全限定名称,如果需要参数,可以在 plugin 标签内通过 property 标签进行配置,配置后的参数在拦截器初始化时会通过 setProperties 方法传递给拦截器。在拦截器中可以很方便地通过 Properties 取得配置的参数值。

plugin方法。这个方法的参数 target 就是拦截器要拦截的对象,该方法会在创建被拦截的接口实现类时被调用。该方法的实现很简单 ,只需要调用 MyBatis 提供的 Plugin (org . apache. ibatis. plugin. Plugin)类的 wrap 静态方法就可以通过 Java 的动态代理拦截目标对象。
这个接口方法通常的实现代码如下。
@Override
public Object plugin(Object target)
{ return Plugin .wrap(target , this) ;
Plugin .wrap 方法会自动判断拦截器的签名和被拦截对象的接口是否匹配,只有匹配的情况下才会使用动态代理拦截目标对象,因此在上面的实现方法中不必做额外的逻辑判断。

intercept 方法
是 MyBatis 运行时要执行的拦截方法。通过该方法的参数 invocation 可以得到很多有用的信息,该参数的常用方法如下。
@Override
public Object intercept(Invocation invocation) throws Throwable
{ Object target =invocation.getTarget ();
Method method= invocation.getMethod() ;
Object[] args = invocation.getArgs() ;
Object result =invocation .proceed ();
return result;
使用 getTarget()方法可以获取当前被拦截的对象,
使用 getMethod ()可以获取当前被拦截的方法,
使用 getArgs ()方法可以返回被拦截方法中的参数。
通过调用 invocation.proceed(); 可以执行被拦截对象真正的方法,
proceed ()方法实际上执行了 method.invoke(target, args )方法,
上面的代码中没有做任何特殊处理,直接返回了执行的结果。

当配置多个拦截器时, MyBatis 会遍历所有拦截器,按顺序执行拦截器的 plugi口方法, 被拦截的对象就会被层层代理。在执行拦截对象的方法时,会一层层地调用拦截器,拦截器通 过 invocation . proceed ()调用下一层的方法,直到真正的方法被执行。方法执行的结果会 从最里面开始向外一层层返回,所以如果存在按顺序配置的 A、 B、 C 三个签名相同的拦截器, MyBaits 会按照 C>B>A>target.proceed()>A>B>C 的顺序执行。如果 A、 B、 C 签名不同, 就会按照 MyBatis 拦截对象的逻辑执行。
8.2 拦截器签名介绍
除了需要实现拦截器接口外, 还需要给实现类配置以下的拦截器注解。
@Intercepts(org . apache . ibatis . plugin . Intercepts )和
签名注解@Signature Corg . apache . ibatis . plugin . Signature ),
这两个注解用来配置拦截器要拦截的接口 的方法。

@ Intercepts 注解中的属性是一个@Signature (签名)数组,可以在同一个拦截器中 同时拦截不同的接口和方法。 以拦截 ResultSetHandler 接口的 handleResultSets 方法为例,配置签名如下。
@Intercepts({ @Signature ( type = ResultSetHandler.class,
method = ” handleResultSets”,
args = {Statement . class})
})
public class ResultSetinterceptor implements Interceptor

@Signature 注解包含以下三个属性。
type: 设置拦截的接口,可选值是前面提到的 4 个接口 。
method: 设置拦截接口中的方法名可选值是前面 4 个接口对应的方法,需要和接口匹配。
args: 设置拦截方法的参数类型数组, 通过方法名和参数类型可以确定唯一一个方法。
8.2.1 Executort接口
MyBatis 允许使用插件来拦截的接口和方法
详细见文章
8.2.2 ParameterHandler接口
MyBatis 允许使用插件来拦截的接口和方法
详细见文章
8.2.4 StatementHandler接口
MyBatis 允许使用插件来拦截的接口和方法
详细见文章
8.3 下面线键值转小写驼峰形式插件
8.4 分页插件
分页插件的核心部分由两个类组成, PageInterceptor 拦截器类和数据库方言接口 Dialect
8.4.1 PageInterceptor 拦截器类
8.4.2 Dialect 接口
8.4.3 MySqlDialect 实现
8.5 本章小结
本章对 MyBatis 拦截器中可以拦截的每一个对象的每一个方法都进行了简单的介绍,通过 本章的学习,希望大家能根据自己的需要选择合适的方法进行拦截。本章以两个插件为例,对 最常用的两个拦截方法进行了演示,希望这两个例子能够引领大家入门。 如果对插件有兴趣,可以参考 http://mybatis. 上面提供的插件进行深入学习。想要灵活地运 用 MyBatis 中的对象实现插件,需要对 MyBatis 中的对象有所了解。在本书最后一章中,我们会对 MyBatis 源码进行简单的介绍,让大家能更好地理解 MyBatis,进而开发自己需要的插件。
第9章Spring集成MyBatis
第10章Spring Boot集成 MyBatis
第11章MyBatis开源项目
11.3 MyBatis 源码讲解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值