如何细粒度地控制你的MyBatis二级缓存(mybatis-enhanced-cache插件实现)
前几天网友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配置文件如下:
<plugins> <plugin interceptor="org.luanlouis.mybatis.plugin.cache.EnhancedCachingExecutor"> <property name="dependency" value="dependencys.xml"/> <property name="cacheEnabled" value="true"/> </plugin> </plugins>其中,<property name="dependency"> 中的value属性是 StatementId之间的依赖关系的配置文件路径。
3. 配置StatementId之间的依赖关系
<?xml version="1.0" encoding="UTF-8"?> <dependencies> <statements> <statement id="com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey"> <observer id="com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments" /> </statement> </statements> </dependencies><statement>节点配置的是更新语句的statementId,其内的子节点<observer> 配置的是当更新语句执行后,应当清空缓存的查询语句的StatementId。子节点<observer>可以有多个。
如上的配置,则说明,如果"com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey" 更新语句执行后,由 “com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments” 语句所产生的放置在Cache缓存中的数据都都会被清空。
4. 配置DepartmentsMapper.xml 和EmployeesMapper.xml
<?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" > <mapper namespace="com.louis.mybatis.dao.DepartmentsMapper" > <cache></cache> <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Department" > <id column="DEPARTMENT_ID" property="departmentId" jdbcType="DECIMAL" /> <result column="DEPARTMENT_NAME" property="departmentName" jdbcType="VARCHAR" /> <result column="MANAGER_ID" property="managerId" jdbcType="DECIMAL" /> <result column="LOCATION_ID" property="locationId" jdbcType="DECIMAL" /> </resultMap> <sql id="Base_Column_List" > DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID </sql> <update id="updateByPrimaryKey" parameterType="com.louis.mybatis.model.Department" > update HR.DEPARTMENTS set DEPARTMENT_NAME = #{departmentName,jdbcType=VARCHAR}, MANAGER_ID = #{managerId,jdbcType=DECIMAL}, LOCATION_ID = #{locationId,jdbcType=DECIMAL} where DEPARTMENT_ID = #{departmentId,jdbcType=DECIMAL} </update> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from HR.DEPARTMENTS where DEPARTMENT_ID = #{departmentId,jdbcType=DECIMAL} </select> </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"> <mapper namespace="com.louis.mybatis.dao.EmployeesMapper"> <cache eviction="LRU" flushInterval="100000" size="10000"/> <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Employee"> <id column="EMPLOYEE_ID" jdbcType="DECIMAL" property="employeeId" /> <result column="FIRST_NAME" jdbcType="VARCHAR" property="firstName" /> <result column="LAST_NAME" jdbcType="VARCHAR" property="lastName" /> <result column="EMAIL" jdbcType="VARCHAR" property="email" /> <result column="PHONE_NUMBER" jdbcType="VARCHAR" property="phoneNumber" /> <result column="HIRE_DATE" jdbcType="DATE" property="hireDate" /> <result column="JOB_ID" jdbcType="VARCHAR" property="jobId" /> <result column="SALARY" jdbcType="DECIMAL" property="salary" /> <result column="COMMISSION_PCT" jdbcType="DECIMAL" property="commissionPct" /> <result column="MANAGER_ID" jdbcType="DECIMAL" property="managerId" /> <result column="DEPARTMENT_ID" jdbcType="DECIMAL" property="departmentId" /> </resultMap> <sql id="Base_Column_List"> EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID </sql> <select id="selectWithDepartments" parameterType="java.lang.Integer" resultMap="BaseResultMap" useCache="true" > select * from HR.EMPLOYEES t left join HR.DEPARTMENTS S ON T.DEPARTMENT_ID = S.DEPARTMENT_ID where EMPLOYEE_ID = #{employeeId,jdbcType=DECIMAL} </select> </mapper>
5. 测试代码:
package com.louis.mybatis.test; import java.io.InputStream; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; 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.apache.log4j.Logger; import com.louis.mybatis.model.Department; import com.louis.mybatis.model.Employee; /** * SqlSession 简单查询演示类 * @author louluan */ public class SelectDemo3 { private static final Logger loger = Logger.getLogger(SelectDemo3.class); public static void main(String[] args) throws Exception { InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(inputStream); SqlSession sqlSession = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); //3.使用SqlSession查询 Map<String,Object> params = new HashMap<String,Object>(); params.put("employeeId",10); //a.查询工资低于10000的员工 Date first = new Date(); //第一次查询 List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); params.put("employeeId", 11); result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); params.put("employeeId", 12); result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); params.put("employeeId", 13); result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); Department department = sqlSession.selectOne("com.louis.mybatis.dao.DepartmentsMapper.selectByPrimaryKey",10); department.setDepartmentName("updated"); sqlSession2.update("com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey", department); sqlSession.commit(); checkCacheStatus(sqlSession); } public static void checkCacheStatus(SqlSession sqlSession) { loger.info("------------Cache Status------------"); Iterator<String> iter = sqlSession.getConfiguration().getCacheNames().iterator(); while(iter.hasNext()) { String it = iter.next(); loger.info(it+":"+sqlSession.getConfiguration().getCache(it).getSize()); } loger.info("------------------------------------"); } }
结果输出:
结果分析:
从上述的结果可以看出,前四次执行了“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"缓存的清空。
该插件的实现周期比较短,尚未经过性能方面的测试,如果果您对此插件有任何意见或者看法,可以留言一起交流和探讨。
该插件源码已经放到了Github上,可供大家自由修改,github地址:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。