【java总结】框架之MyBatis
MyBatis基础
什么是MyBatis?
MyBatis 本是 apache 的一个开源项目 iBatis,Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。
Mybaits的优点?
(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
(4)能够与Spring很好的集成;
(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
缺点:
(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis的缓存
mybatis提供查询缓存,用于减轻数据库压力,提高数据库性能
MyBatis的缓存分为一级缓存和二级缓存,分别如下:
- 一级缓存放在session里面,默认就有。
- 二级缓存放在它的命名空间里,默认是打开的。使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置
在config.xml设置二级缓存开关 , 还要在具体的mapper.xml开启二级缓存
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
需要将映射的javapojo类实现序列化
<cache eviction="LRU" flushInterval="10000"/>
MyBatis使用
创建流程
1.创建mybatis-config.xml;
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置-->
<configuration>
<!--environments可以配置多个环境,default选择默认环境-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="guo"/>
<property name="password" value="guo"/>
</dataSource>
</environment>
</environments>
</configuration>
2.编辑mybatis工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//第一步!
// /*使用mybatis获取sqlSessionFactory对象*/
String resource = "mybatis-config.xml";
/*获取配置文件resource*/
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**第二步!!
* 从sqlSessionFactory中获取sqlsession
* sqlsession完全包含了面向数据库执行sql命令所需的所有方法
* */
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
3.创建mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--接口实现类由原来的userdaoimpl转变为一个mapper配置文件-->
<mapper namespace="cn.jimu98.dao.UserDao">
<select id="getUserList" resultType="cn.jimu98.pojo.User">
select * from user
</select>
</mapper>
4.测试
public class UserDaoTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
全局配置
引入外部配置文件
db.properties
db.username=root
db.password=123
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql:///test01?serverTimezone=Asia/Shanghai
mybatis-config.xml
<configuration>
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="cn.jimu98.mybatis.mapper"/>
</mappers>
</configuration>
settings
Setting(设置) | Description(描述) | Valid Values(验证值组) | Default(默认值) |
---|---|---|---|
cacheEnabled | 在全局范围内启用或禁用缓存配置任何映射器在此配置下 | true or false | TRUE |
lazyLoadingEnabled | 在全局范围内启用或禁用延迟加载。禁用时,所有查询将热加载 | true or false | TRUE |
aggressiveLazyLoading | 启用时,有延迟加载属性的对象将被完全加载后调用懒惰的任何属性。否则,每一个属性是按需加载。 | true or false | TRUE |
multipleResultSetsEnabled | 允许或不允许从一个单独的语句(需要兼容的驱动程序)要返回多个结果集。 | true or false | TRUE |
useColumnLabel | 使用列标签,而不是列名。在这方面,不同的驱动有不同的行为。参考驱动文档或测试两种方法来决定你的驱动程序的行为如何。 | true or false | TRUE |
useGeneratedKeys | 允许 JDBC 支持生成的密钥。兼容的驱动程序是必需的。此设置强制生成的键被使用,如果设置为 true,一些驱动会不兼容性,但仍然可以工作。 | true or false | FALSE |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段/属性。NONE自动映射。 PARTIAL 只会自动映射结果没有嵌套结果映射定义里面。 FULL 会自动映射的结果映射任何复杂的(包含嵌套或其他)。 | NONE, PARTIAL, FULL | PARTIAL |
defaultExecutorType | 配置默认执行人。SIMPLE执行人确实没有什么特别的。 REUSE执行器重用准备好的语句。 BATCH执行器重用语句和批处理更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置驱动程序等待一个数据库响应的秒数。 | Any positive integer | Not Set (null) |
safeRowBoundsEnabled | 允许使用嵌套的语句RowBounds。 | true or false | FALSE |
mapUnderscoreToCamelCase | 从经典的数据库列名 A_COLUMN 启用自动映射到骆驼标识的经典的 Java 属性名 aColumn。 | true or false | FALSE |
localCacheScope | MyBatis的使用本地缓存,以防止循环引用,并加快反复嵌套查询。默认情况下(SESSION)会话期间执行的所有查询缓存。如果 localCacheScope=STATMENT 本地会话将被用于语句的执行,只是没有将数据共享之间的两个不同的调用相同的 SqlSession。 | SESSION or STATEMENT | SESSION |
dbcTypeForNull | 指定为空值时,没有特定的JDBC类型的参数的 JDBC 类型。有些驱动需要指定列的 JDBC 类型,但其他像 NULL,VARCHAR 或 OTHER 的工作与通用值。 | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定触发延迟加载的对象的方法。 | A method name list separated by commas | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定所使用的语言默认为动态SQL生成。 | A type alias or fully qualified class name. | org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver |
callSettersOnNulls | 指定如果setter方法或地图的put方法时,将调用检索到的值是null。它是有用的,当你依靠Map.keySet()或null初始化。注意原语(如整型,布尔等)不会被设置为null。 | true or false | FALSE |
logPrefix | 指定的前缀字串,MyBatis将会增加记录器的名称。 | Any String | Not set |
logImpl | 指定MyBatis的日志实现使用。如果此设置是不存在的记录的实施将自动查找。 | SLF4J or LOG4J or LOG4J2 or JDK_LOGGING or COMMONS_LOGGING or STDOUT_LOGGING or NO_LOGGING | Not set |
proxyFactory | 指定代理工具,MyBatis将会使用创建懒加载能力的对象。 | CGLIB | JAVASSIST |
typeAliases
这个是 MyBatis 中定义的别名,分两种,一种是 MyBatis 自带的别名,另一种是我们自定义的别名。
本来,我们在 Mapper 中定义数据类型时,需要写全路径,如下:
<select id="getUserCount" resultType="java.lang.Integer">
select count(*) from user ;
</select>
但是,每次写全路径比较麻烦。这种时候,我们可以用类型的别名来代替,例如用 int 做 Integer 的别名:
<select id="getUserCount" resultType="int">
select count(*) from user ;
</select>
自定义别名
我们自己的对象,在 Mapper 中定义的时候,也是需要写全路径:
<select id="getAllUser" resultType="cn.jimu98.mybatis.model.User">
select * from user;
</select>
这种情况下,写全路径也比较麻烦,我们可以给我们自己的 User 对象取一个别名,在 mybatis-config.xml 中添加 typeAliases 节点:
<configuration>
<properties resource="db.properties"></properties>
<typeAliases>
<typeAlias type="cn.jimu98.mybatis.model.User" alias="java"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="cn.jimu98.mybatis.mapper"/>
</mappers>
</configuration>
这里,我们给 User 对象取了一个别名叫 jimu98,然后,我们就可以在 Mapper 中直接使用 jimu98来代替 User 对象了:
<select id="getAllUser" resultType="jimu98">
select * from user;
</select>
但是,这种一个一个去枚举对象的过程非常麻烦,我们还可以批量给对象定义别名,批量定义主要是利用包扫描来做,批量定义默认的类的别名,是类名首字母小写,例如如下配置:
<typeAliases>
<package name="cn.jimu98.mybatis.model"/>
</typeAliases>
这个配置就表示给 cn.jimu98.mybatis.model 包下的所有类取别名,默认的别名就是类名首字母小写。这个时候,我们在 Mapper 中,就可以利用 user 代替 User 全路径了:
<select id="getAllUser" resultType="user">
select * from user;
</select>
在最新版中,批量定义的别名,类名首字母也可以不用小写,在实际开发中,我们一般使用第二种方式(批量定义的方式)
Mapper 映射文件
parameterType
这个表示输入的参数类型。
在 MyBatis 中,我们在 mapper 引用变量时,默认使用的是 #
,像下面这样:
<select id="getUserById" resultType="cn.jimu98.mybatis.model.User">
select * from user where id=#{id};
</select>
除了使用 #
之外,我们也可以使用 $
来引用一个变量:
<select id="getUserById" resultType="cn.jimu98.mybatis.model.User">
select * from user where id=${id};
</select>
$
相当于是参数拼接的方式,而 #
则相当于是占位符的方式。
对象参数
例如添加一个用户:
Integer addUser(User user);
对应的 mapper 文件如下:
<insert id="addUser" parameterType="cn.jimu98.mybatis.model.User">
insert into user (username,address,favorites) values (#{username},#{address},#{favorites,typeHandler=cn.jimu98.mybatis.typehandler.List2VarcharHandler});
</insert>
我们在引用的时候,直接使用属性名就能够定位到对象了。如果对象存在多个,我们也需要给对象添加 @Param 注解,如果给对象添加了 @Param 注解,那么对象属性的引用,会有一些变化。如下:
Integer addUser(@Param("user") User user);
如果对象参数添加了 @Param 注解,Mapper 中的写法就会发生变化:
<insert id="addUser" parameterType="cn.jimu98.mybatis.model.User">
insert into user (username,address,favorites) values (#{user.username},#{user.address},#{user.favorites,typeHandler=cn.jimu98.mybatis.typehandler.List2VarcharHandler});
</insert>
注意多了一个前缀,这个前缀不是变量名,而是 @Param 注解中定义名称。
Map 参数
如果想要使用 Map 传递参数,技术上来说,肯定是没有问题的。
Integer updateUsernameById(HashMap<String,Object> map);
XML 文件写法如下:
<update id="updateUsernameById">
update user set username = #{username} where id=#{id};
</update>
引用的变量名,就是 map 中的 key。基本上和实体类是一样的,如果给 map 取了别名,那么在引用的时候,也要将别名作为前缀加上,这一点和实体类也是一样的。
resultType
resultType 是返回类型,在实际开发中,如果返回的数据类型比较复杂,一般我们使用 resultMap,但是,对于一些简单的返回,使用 resultType 就够用了。
resultType 返回的类型可以是简单类型,可以是对象,可以是集合,也可以是一个 hashmap,如果是 hashmap,map 中的 key 就是字段名,value 就是字段的值。
resultMap
先来看一个基本用法:
首先在 mapper.xml 中定义一个 resultMap:
<resultMap id="MyResultMap" type="org.javaboy.mybatis.model.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
</resultMap>
在这个 resultMap 中,id 用来描述主键,column 是数据库查询出来的列名,property 则是对象中的属性名。
然后在查询结果中,定义返回值时使用这个 ResultMap:
<select id="getUserById" resultMap="MyResultMap">
select * from user where id=#{id};
</select>
动态 SQL
动态 SQL 是 MyBatis 中非常强大的一个功能。例如一些常见的查询场景:
- 查询条件不确定
- 批量插入
![image-20200809003512530](https://i-blog.csdnimg.cn/blog_migrate/e9513d3bccda8659640d1d21d835aa2c.png)
if
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。
如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员挑选的 Blog)。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
trim、where、set
前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
注意,我们覆盖了后缀值设置,并且自定义了前缀值。
foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
至此,我们已经完成了与 XML 配置及映射文件相关的讨论。下一章将详细探讨 Java API,以便你能充分利用已经创建的映射配置。
script
要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:
@Update({"<script>",
"update Author",
" <set>",
" </set>",
"where id=#{id}",
"</script>"})
void updateAuthorValues(Author author);
bind
bind
元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
模糊查询like的三种方式:
1、直接在参数上拼接%
2、使用concat()函数做字符串拼接
select * from orders where number like concat(concat(‘%’, #{number}),’%’)
3、bind标签
select * from orders where number like #{ll}
当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
写sql语句时起别名
在MyBatis的全局配置文件中开启驼峰命名规则
<configuration> <settings> <!-- 开启驼峰命名规则,可以将数据库中的下划线映射为驼峰命名 --> <!-- 例如:dept_id 可以映射为deptId --> <setting name="mapUndercoreToCamelCase" value="true" /></setting> </settings> </configuration> 1234567
在Mapper映射文件中使用
resultMap
来自定义映射规则
<select id="getOrder" parameterType="int" resultMap="orderresultmap">
select * from orders where order_id=#{id}
</select>
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
<!–用id属性来映射主键字段–>
<id property=”id” column=”order_id”>
<!–用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性–>
<result property = “orderno” column =”order_no”/>
<result property=”price” column=”order_price” />
</reslutMap>
#{}和${}的区别是什么?
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
MyBatis其他问题
MyBatis与Hibernate有哪些不同?
(1)Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。
(2)Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。
(3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。
什么是MyBatis的接口绑定,有什么好处
答:接口映射就是在IBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
接口绑定有几种实现方式,分别是怎么实现的?
答:接口绑定有两种实现方式:
- 一种是通过注解绑定,就是在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定。
- 另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。
什么情况下用注解绑定,什么情况下用xml绑定
当Sql语句比较简单时候,用注解绑定。
当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
Mybatis是如何进行分页的?分页插件的原理是什么?
答:Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10
简述Mybatis的插件运行原理,以及如何编写一个插件。
答:Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。