本文最后更新于:4 个月前
Mybatis-day04 第一章 Mybatis的缓存机制 第一节 缓存机制的概述 1. 什么是缓存
2. 一级缓存和二级缓存的对比 2.1 使用顺序
查询的顺序是:
先查询二级缓存,因为二级缓存中可能会有其他SqlSession已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之前,一级缓存中的数据会写入二级缓存
2.2 作用范围
一级缓存:SqlSession级别
二级缓存:SqlSessionFactory级别
它们之间范围的大小参考下面图:
第二节 一级缓存 1 代码验证一级缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void testFirstLevelCache () { SqlSession sqlSession = sessionFactory.openSession(); EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class); Employee employee1 = employeeMapper.selectEmployee(7 ); System.out.println(employee1); SqlSession sqlSession2 = sessionFactory.openSession(); EmployeeMapper employeeMapper2 = sqlSession2.getMapper(EmployeeMapper.class); employee1.setEmpName("zs" ); employeeMapper2.updateEmployee(employee1); sqlSession2.commit(); Employee employee2 = employeeMapper.selectEmployee(7 ); System.out.println(employee2); }
一共只打印了一条SQL语句。
2 一级缓存失效的情况
不是同一个SqlSession(因为一级缓存只能用在同一个SqlSession中)
同一个SqlSession但是查询条件发生了变化
同一个SqlSession两次查询期间执行了任何一次增删改操作,那么会改变缓存的数据
同一个SqlSession两次查询期间手动清空了缓存: 调用sqlSession的clearCache()方法
同一个SqlSession两次查询期间提交了事务: 调用sqlSession的commit()方法
第三节 二级缓存 1 代码测试二级缓存 1.1 开启二级缓存功能 在想要使用二级缓存的Mapper配置文件中加入cache标签
1.2 让实体类支持序列化 1 2 public class Employee implements Serializable { }
1.3 junit测试 这个功能的测试操作需要将SqlSessionFactory对象设置为成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void testSecondCacheLevel () { SqlSession sqlSession1 = sessionFactory.openSession(); EmployeeMapper employeeMapper1 = sqlSession1.getMapper(EmployeeMapper.class); SqlSession sqlSession2 = sessionFactory.openSession(); EmployeeMapper employeeMapper2 = sqlSession2.getMapper(EmployeeMapper.class); Employee employee1 = employeeMapper1.selectEmployee(7 ); System.out.println(employee1); sqlSession1.close(); Employee employee2 = employeeMapper2.selectEmployee(7 ); System.out.println(employee2); sqlSession2.close(); }
1.4 缓存命中率 日志中打印的Cache Hit Ratio叫做缓存命中率
1 2 3 4 5 Cache Hit Ratio [com .atguigu.mybatis.EmployeeMapper]: 0.0 (0 /1 ) Cache Hit Ratio [com .atguigu.mybatis.EmployeeMapper]: 0.5 (1 /2 ) Cache Hit Ratio [com .atguigu.mybatis.EmployeeMapper]: 0.6666666666666666 (2 /3 ) Cache Hit Ratio [com .atguigu.mybatis.EmployeeMapper]: 0.75 (3 /4 ) Cache Hit Ratio [com .atguigu.mybatis.EmployeeMapper]: 0.8 (4 /5 )
缓存命中率=命中缓存的次数/查询的总次数
2 查询结果存入二级缓存的时机 结论:SqlSession关闭的时候,一级缓存中的内容会被存入二级缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 SqlSession session01 = factory.openSession(); SqlSession session02 = factory.openSession(); EmployeeMapper employeeMapper01 = session01.getMapper(EmployeeMapper.class); EmployeeMapper employeeMapper02 = session02.getMapper(EmployeeMapper.class); Employee employee01 = employeeMapper01.selectEmployeeById(2 ); Employee employee02 = employeeMapper02.selectEmployeeById(2 ); System.out.println("employee02.equals(employee01) = " + employee02.equals(employee01));
上面代码打印的结果是:
1 2 3 4 5 6 7 8 9 DEBUG 12-01 10:10:32,209 Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.0 (LoggingCache.java:62) DEBUG 12-01 10:10:32,570 ==> Preparing: select emp_id,emp_name,emp_salary,emp_gender,emp_age from t_emp where emp_id =? (BaseJdbcLogger.java:145) DEBUG 12-01 10:10:32,624 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:145) DEBUG 12-01 10:10:32,643 <== Total: 1 (BaseJdbcLogger.java:145) DEBUG 12-01 10:10:32,644 Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.0 (LoggingCache.java:62) DEBUG 12-01 10:10:32,661 ==> Preparing: select emp_id,emp_name,emp_salary,emp_gender,emp_age from t_emp where emp_id =? (BaseJdbcLogger.java:145) DEBUG 12-01 10:10:32,662 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:145) DEBUG 12-01 10:10:32,665 <== Total: 1 (BaseJdbcLogger.java:145) employee02.equals(employee01) = false
修改代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 SqlSession session01 = factory.openSession(); SqlSession session02 = factory.openSession(); EmployeeMapper employeeMapper01 = session01.getMapper(EmployeeMapper.class); EmployeeMapper employeeMapper02 = session02.getMapper(EmployeeMapper.class); Employee employee01 = employeeMapper01.selectEmployeeById(2 ); session01.close(); Employee employee02 = employeeMapper02.selectEmployeeById(2 ); System.out.println("employee02.equals(employee01) = " + employee02.equals(employee01)); session02.close();
打印结果:
1 2 3 4 5 6 DEBUG 12-01 10:14:06,804 Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.0 (LoggingCache.java:62) DEBUG 12-01 10:14:07,135 ==> Preparing: select emp_id,emp_name,emp_salary,emp_gender,emp_age from t_emp where emp_id =? (BaseJdbcLogger.java:145) DEBUG 12-01 10:14:07,202 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:145) DEBUG 12-01 10:14:07,224 <== Total: 1 (BaseJdbcLogger.java:145) DEBUG 12-01 10:14:07,308 Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.5 (LoggingCache.java:62) employee02.equals(employee01) = false
3 二级缓存相关配置(了解) 在Mapper配置文件中添加的cache标签可以设置一些属性:
eviction属性:缓存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
第四节 整合EHCache 1. EHCache简介 Ehcache 是一种开源的、基于标准的缓存,可提高性能、卸载数据库并简化可扩展性。它是最广泛使用的基于 Java 的缓存,因为它健壮、经过验证、功能齐全,并且与其他流行的库和框架集成。 Ehcache 从进程内缓存一直扩展到具有 TB 级缓存的混合进程内/进程外部署。 官网地址为: https://www.ehcache.org/
2. Mybatis整合操作 2.1 添加依赖 1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.1</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.3</version > </dependency >
2.2 创建EHCache配置文件 ehcache.xml
文件内容
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 <?xml version="1.0" encoding="utf-8" ?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="http://ehcache.org/ehcache.xsd" > <diskStore path ="D:\atguigu\ehcache" /> <defaultCache maxElementsInMemory ="1000" maxElementsOnDisk ="10000000" eternal ="false" overflowToDisk ="true" timeToIdleSeconds ="120" timeToLiveSeconds ="120" diskExpiryThreadIntervalSeconds ="120" memoryStoreEvictionPolicy ="LRU" > </defaultCache > </ehcache >
引入第三方框架或工具时,配置文件的文件名可以自定义吗?
可以自定义:文件名是由我告诉其他环境
不能自定义:文件名是框架内置的、约定好的,就不能自定义,以避免框架无法加载这个文件
2.3 指定缓存管理器的具体类型 还是到查询操作所的Mapper配置文件中,找到之前设置的cache标签:
1 <cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
2.4 加入logback日志 存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。
2.4.1 各种Java日志框架简介 门面:
名称
说明
JCL(Jakarta Commons Logging)
陈旧
SLF4J(Simple Logging Facade for Java)★
适合
jboss-logging
特殊专业领域使用
实现:
名称
说明
log4j★
最初版
JUL(java.util.logging)
JDK自带
log4j2
Apache收购log4j后全面重构,内部实现和log4j完全不同
logback★
优雅、强大
注:标记★的技术是同一作者。
2.4.2 logback配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="UTF-8"?> <configuration debug ="true" > <appender name ="STDOUT" class ="ch.qos.logback.core.ConsoleAppender" > <encoder > <pattern > [%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern > </encoder > </appender > <root level ="DEBUG" > <appender-ref ref ="STDOUT" /> </root > <logger name ="com.atguigu.crowd.mapper" level ="DEBUG" /> </configuration >
2.4.3 junit测试 正常按照二级缓存的方式测试即可。因为整合EHCache后,其实就是使用EHCache代替了Mybatis自带的二级缓存。
2.4.4 EHCache配置文件说明 当借助CacheManager.add(“缓存名称”)创建Cache时,EhCache便会采用指定的的管理策略。
defaultCache标签各属性说明:
属性名
是否必须
作用
maxElementsInMemory
是
在内存中缓存的element的最大数目
maxElementsOnDisk
是
在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal
是
设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断
overflowToDisk
是
设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds
否
当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds
否
缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB
否
DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskPersistent
否
在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds
否
磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy
否
当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。 默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
第五节 缓存的原理 1. Cache接口 1.1 Cache接口的重要地位 org.apache.ibatis.cache.Cache接口:所有缓存都必须实现的顶级接口
1.2 Cache接口中的方法
方法名
作用
putObject()
将对象存入缓存
getObject()
从缓存中取出对象
removeObject()
从缓存中删除对象
1.3 缓存的本质 根据Cache接口中方法的声明我们能够看到,缓存的本质是一个Map 。
2. PerpetualCache类
org.apache.ibatis.cache.impl.PerpetualCache是Mybatis的默认缓存,也是Cache接口的默认实现。Mybatis一级缓存和自带的二级缓存都是通过PerpetualCache来操作缓存数据的。但是这就奇怪了,同样是PerpetualCache这个类,怎么能区分出来两种不同级别的缓存呢?
其实很简单,调用者不同。
一级缓存:由BaseExecutor调用PerpetualCache
二级缓存:由CachingExecutor调用PerpetualCache,而CachingExecutor可以看做是对BaseExecutor的装饰
3. 一级缓存机制
org.apache.ibatis.executor.BaseExecutor类中的关键方法:
3.1 query()方法 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 35 36 37 38 public <E> List<E> query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query" ).object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed." ); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null ; if (list != null ) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0 ) { for (org.apache.ibatis.executor.BaseExecutor.DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); } } return list; }
3.2 queryFromDatabase()方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private <E> List<E> queryFromDatabase (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
4. 二级缓存机制
下面我们来看看CachingExecutor类中的query()方法在不同情况下使用的具体缓存对象:
4.1 未开启二级缓存
4.1 使用Mybatis自带的二级缓存
4.1 使用EHCache
第二章 逆向工程 第一节 概念
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类
Mapper接口
Mapper配置文件
第二节 基本原理
第三节 逆向工程的具体操作 1. 配置POM 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.7</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.3</version > <scope > runtime</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.8</version > <scope > provided</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.3.0</version > <dependencies > <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.3.2</version > </dependency > <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.8</version > </dependency > </dependencies > </plugin > </plugins > </build >
2. MBG配置文件 文件名必须是:generatorConfig.xml
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 35 36 37 38 39 40 41 42 43 44 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration > <context id ="DB2Tables" targetRuntime ="MyBatis3" > <jdbcConnection driverClass ="com.mysql.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/mybatis-example" userId ="root" password ="123456" > </jdbcConnection > <javaModelGenerator targetPackage ="com.atguigu.pojo" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.atguigu.mapper" targetProject =".\src\main\resources" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.atguigu.mapper" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="t_emp" domainObjectName ="Employee" /> <table tableName ="t_customer" domainObjectName ="Customer" /> <table tableName ="t_order" domainObjectName ="Order" /> </context > </generatorConfiguration >
3. 执行MBG插件的generate目标
4. 效果
5. 测试代码 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 package com.atguigu;import com.atguigu.mapper.EmployeeMapper;import com.atguigu.pojo.Employee;import com.atguigu.pojo.EmployeeExample;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestMybatis { private SqlSession sqlSession; private EmployeeMapper employeeMapper; private InputStream is; @Before public void init () throws IOException { is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); sqlSession = sqlSessionFactory.openSession(); employeeMapper = sqlSession.getMapper(EmployeeMapper.class); } @After public void destroy () throws IOException { sqlSession.commit(); is.close(); sqlSession.close(); } @Test public void testInsertEmployee () { employeeMapper.insert(new Employee(null ,"王五" ,3000d )); } @Test public void testDeleteEmployeeByPrimaryKey () { employeeMapper.deleteByPrimaryKey(15014 ); } @Test public void testDeleteByExample () { EmployeeExample employeeExample = new EmployeeExample(); EmployeeExample.Criteria criteria = employeeExample.createCriteria(); criteria.andEmpIdBetween(3000 ,8000 ); employeeMapper.deleteByExample(employeeExample); } @Test public void testUpdateEmployee () { employeeMapper.updateByPrimaryKeySelective(new Employee(4 ,null ,2000d )); } @Test public void testQueryByPrimaryKey () { Employee employee = employeeMapper.selectByPrimaryKey(4 ); System.out.println(employee); } @Test public void testQueryAll () { EmployeeExample employeeExample = new EmployeeExample(); List<Employee> employeeList = employeeMapper.selectByExample(employeeExample); } @Test public void testQueryByExample () { EmployeeExample employeeExample = new EmployeeExample(); EmployeeExample.Criteria criteria1 = employeeExample.createCriteria(); criteria1.andEmpNameLike("%s%" ) .andEmpSalaryGreaterThan(3000d ); EmployeeExample.Criteria criteria2 = employeeExample.or(); criteria2.andEmpIdBetween(8001 ,8234 ) .andEmpNameLike("%3%" ); employeeMapper.selectByExample(employeeExample); } }
第四节 QBC查询 1. 概念 QBC:Query By Criteria , 最大的特点就是将SQL语句中的WHERE子句进行了组件化的封装,让我们可以通过调用Criteria对象的方法自由的拼装查询条件。
2. 例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void testQueryByExample () { EmployeeExample employeeExample = new EmployeeExample(); EmployeeExample.Criteria criteria1 = employeeExample.createCriteria(); criteria1.andEmpNameLike("%s%" ) .andEmpSalaryGreaterThan(3000d ); EmployeeExample.Criteria criteria2 = employeeExample.or(); criteria2.andEmpIdBetween(8001 ,8234 ) .andEmpNameLike("%3%" ); employeeMapper.selectByExample(employeeExample); }
第三章 Mybatis的其它补充内容(了解) 第一节 插件机制 1. Mybatis四大对象 1.1 Executor
1.2 ParameterHandler
2.3 ResultSetHandler
2.4 StatementHandler
2. Mybatis插件机制的作用 插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理 机制,可以介入四大对象的任何一个方法的执行。著名的Mybatis插件包括 PageHelper(分页插件)、通用 Mapper(SQL生成插件)等。
如果想编写自己的Mybatis插件可以通过实现org.apache.ibatis.plugin.Interceptor接口来完成,表示对Mybatis常规操作进行拦截,加入自定义逻辑。
但是由于插件涉及到Mybatis底层工作机制,在没有足够把握时不要轻易尝试。
第二节 Mybatis底层的JDBC封装 org.apache.ibatis.executor.statement.PreparedStatementHandler类:
查找上面目标时,Debug查看源码的切入点是:
org.apache.ibatis.session.defaults.DefaultSqlSession类的update()方法
然后在分析this.executor.update()方法