前几天网友chanfish 给我抛出了一个问题,笼统地讲就是如何能细粒度地控制MyBatis的二级缓存问题,酝酿了几天,觉得可以写个插件来实现这个这一功能。本文就是从问题入手,一步步分析现存的MyBatis的二级缓存的不足之处,探讨一点可以改进的地方,并且对不足之处开发一个插件进行弥补。
本文如下组织结构:
- 一个关于MyBatis的二级缓存的实际问题
- 当前MyBatis二级缓存的工作机制
- mybatis-enhanced-cache插件的设计和工作原理
- mybatis-enhanced-cache 插件的使用实例
1.一个关于MyBatis的二级缓存的实际问题
2. 当前MyBatis二级缓存的工作机制:
3.mybatis-enhanced-cache插件的设计和工作原理
4. mybatis-enhanced-cache 插件的使用实例:
1. 下载 mybatis-enhanced-cache.rar压缩包,解压,将其内的mybatis-enhanced-cache-0.0.1-SNAPSHOT.jar添加到项目的classpath下
2. 配置MyBatis配置文件如下:
View Code1 <plugins> 2 <plugin interceptor="org.luanlouis.mybatis.plugin.cache.EnhancedCachingExecutor"> 3 <property name="dependency" value="dependencys.xml"/> 4 <property name="cacheEnabled" value="true"/> 5 </plugin> 6 </plugins>其中,<property name="dependency"> 中的value属性是 StatementId之间的依赖关系的配置文件路径。
3. 配置StatementId之间的依赖关系
View Code1 <?xml version="1.0" encoding="UTF-8"?> 2 <dependencies> 3 <statements> 4 <statement id="com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey"> 5 <observer id="com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments" /> 6 </statement> 7 </statements> 8 </dependencies><statement>节点配置的是更新语句的statementId,其内的子节点<observer> 配置的是当更新语句执行后,应当清空缓存的查询语句的StatementId。子节点<observer>可以有多个。
如上的配置,则说明,如果"com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey" 更新语句执行后,由 “com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments” 语句所产生的放置在Cache缓存中的数据都都会被清空。
4. 配置DepartmentsMapper.xml 和EmployeesMapper.xml
View Code1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > 3 <mapper namespace="com.louis.mybatis.dao.DepartmentsMapper" > 4 5 <cache></cache> 6 7 <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Department" > 8 <id column="DEPARTMENT_ID" property="departmentId" jdbcType="DECIMAL" /> 9 <result column="DEPARTMENT_NAME" property="departmentName" jdbcType="VARCHAR" /> 10 <result column="MANAGER_ID" property="managerId" jdbcType="DECIMAL" /> 11 <result column="LOCATION_ID" property="locationId" jdbcType="DECIMAL" /> 12 </resultMap> 13 14 15 <sql id="Base_Column_List" > 16 DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID 17 </sql> 18 19 <update id="updateByPrimaryKey" parameterType="com.louis.mybatis.model.Department" > 20 update HR.DEPARTMENTS 21 set DEPARTMENT_NAME = #{departmentName,jdbcType=VARCHAR}, 22 MANAGER_ID = #{managerId,jdbcType=DECIMAL}, 23 LOCATION_ID = #{locationId,jdbcType=DECIMAL} 24 where DEPARTMENT_ID = #{departmentId,jdbcType=DECIMAL} 25 </update> 26 <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > 27 select 28 <include refid="Base_Column_List" /> 29 from HR.DEPARTMENTS 30 where DEPARTMENT_ID = #{departmentId,jdbcType=DECIMAL} 31 </select> 32 </mapper>View Code1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 3 <mapper namespace="com.louis.mybatis.dao.EmployeesMapper"> 4 5 <cache eviction="LRU" flushInterval="100000" size="10000"/> 6 7 <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Employee"> 8 <id column="EMPLOYEE_ID" jdbcType="DECIMAL" property="employeeId" /> 9 <result column="FIRST_NAME" jdbcType="VARCHAR" property="firstName" /> 10 <result column="LAST_NAME" jdbcType="VARCHAR" property="lastName" /> 11 <result column="EMAIL" jdbcType="VARCHAR" property="email" /> 12 <result column="PHONE_NUMBER" jdbcType="VARCHAR" property="phoneNumber" /> 13 <result column="HIRE_DATE" jdbcType="DATE" property="hireDate" /> 14 <result column="JOB_ID" jdbcType="VARCHAR" property="jobId" /> 15 <result column="SALARY" jdbcType="DECIMAL" property="salary" /> 16 <result column="COMMISSION_PCT" jdbcType="DECIMAL" property="commissionPct" /> 17 <result column="MANAGER_ID" jdbcType="DECIMAL" property="managerId" /> 18 <result column="DEPARTMENT_ID" jdbcType="DECIMAL" property="departmentId" /> 19 </resultMap> 20 21 <sql id="Base_Column_List"> 22 EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, 23 COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID 24 </sql> 25 26 <select id="selectWithDepartments" parameterType="java.lang.Integer" resultMap="BaseResultMap" useCache="true" > 27 select 28 * 29 from HR.EMPLOYEES t left join HR.DEPARTMENTS S ON T.DEPARTMENT_ID = S.DEPARTMENT_ID 30 where EMPLOYEE_ID = #{employeeId,jdbcType=DECIMAL} 31 </select> 32 33 </mapper>5. 测试代码:
结果输出:View Code1 package com.louis.mybatis.test; 2 3 import java.io.InputStream; 4 import java.util.Date; 5 import java.util.HashMap; 6 import java.util.Iterator; 7 import java.util.List; 8 import java.util.Map; 9 10 import org.apache.ibatis.io.Resources; 11 import org.apache.ibatis.session.SqlSession; 12 import org.apache.ibatis.session.SqlSessionFactory; 13 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 14 import org.apache.log4j.Logger; 15 16 import com.louis.mybatis.model.Department; 17 import com.louis.mybatis.model.Employee; 18 19 /** 20 * SqlSession 简单查询演示类 21 * @author louluan 22 */ 23 public class SelectDemo3 { 24 25 private static final Logger loger = Logger.getLogger(SelectDemo3.class); 26 27 public static void main(String[] args) throws Exception { 28 InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml"); 29 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 30 SqlSessionFactory factory = builder.build(inputStream); 31 32 SqlSession sqlSession = factory.openSession(true); 33 SqlSession sqlSession2 = factory.openSession(true); 34 //3.使用SqlSession查询 35 Map<String,Object> params = new HashMap<String,Object>(); 36 params.put("employeeId",10); 37 //a.查询工资低于10000的员工 38 Date first = new Date(); 39 //第一次查询 40 List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); 41 sqlSession.commit(); 42 checkCacheStatus(sqlSession); 43 params.put("employeeId", 11); 44 result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); 45 sqlSession.commit(); 46 checkCacheStatus(sqlSession); 47 params.put("employeeId", 12); 48 result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); 49 sqlSession.commit(); 50 checkCacheStatus(sqlSession); 51 params.put("employeeId", 13); 52 result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); 53 sqlSession.commit(); 54 checkCacheStatus(sqlSession); 55 Department department = sqlSession.selectOne("com.louis.mybatis.dao.DepartmentsMapper.selectByPrimaryKey",10); 56 department.setDepartmentName("updated"); 57 sqlSession2.update("com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey", department); 58 sqlSession.commit(); 59 checkCacheStatus(sqlSession); 60 } 61 62 63 public static void checkCacheStatus(SqlSession sqlSession) 64 { 65 loger.info("------------Cache Status------------"); 66 Iterator<String> iter = sqlSession.getConfiguration().getCacheNames().iterator(); 67 while(iter.hasNext()) 68 { 69 String it = iter.next(); 70 loger.info(it+":"+sqlSession.getConfiguration().getCache(it).getSize()); 71 } 72 loger.info("------------------------------------"); 73 74 } 75 76 }
结果分析:
从上述的结果可以看出,前四次执行了“com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments”语句,EmployeesMapper对应的Cache缓存中存储的结果缓存有1个增加到4个。
当执行了"com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey"后,EmployeeMapper对应的缓存Cache结果被清空了,即"com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey"更新语句引起了EmployeeMapper中的"com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments"缓存的清空。
转自:http://blog.csdn.net/luanlouis/article/details/41800511