mybatis  中应用二级缓存(使用框架本身实现的缓存机制)

1. 在mybatis的配置文件中,开启二级缓存(cacheEnabled设置为 true).
在mybatis_config.xml文件中,设置如下:

<settings>
	<!-- 开启二级缓存,默认为true(默认二级缓存是开启的) -->
	<setting name="cacheEnabled" value="true"/>
</settings>



2. 在实体映射文件中,应用二级缓存:


<cache eviction='FIFO' flushInterval='60000' size='512' readOnly='true'/>

其中各项属性代表的含义为:


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


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


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


eviction配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有, 默认的是 LRU:


1.      LRU – 最近最少使用的:移除最长时间不被使用的对象。


2.      FIFO – 先进先出:按对象进入缓存的顺序来移除它们。


3.      SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。


4.      WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。




3. 缓存对象执行序列化


由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。


如果该类存在父类,那么父类也要实现序列化,类似如下写法:


public class Emp implements Serializable {


}


到这里,二级缓存配置完成。


在应用二级缓存过程中,我们还可以禁用二级缓存:


<!-- 查询全部员工信息 -->
<select id="findAllEmp" resultType="cn.sz.hcq.pojo.Emp" useCache="false">
	select empno, ename, job, mgr, hiredate, sal, comm from emp
</select>



该statement中设置userCache=false可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询,默认情况下是true,即该statement使用二级缓存.


还可以刷新二级缓存:


<!-- 查询全部员工信息 -->
<select id="findAllEmp" resultType="cn.sz.hcq.pojo.Emp" flushCache="true">
	select empno, ename, job, mgr, hiredate, sal, comm from emp
</select>


下面是演示案例:

在mysql数据库中建一张emp表,表的字段如下:

redis mybatis二级缓存 实现类 mybatis二级缓存配置_缓存机制

本人演示的项目是Maven项目:

项目结构如下:

redis mybatis二级缓存 实现类 mybatis二级缓存配置_mybatis_02

IEmpDAO.java为接口,提供查询emp的方法,EmpDAOImpl.java为接口的实现类,MybatisSqlSessionFactory.java为本人创建的获取sqlSession的工具类,Emp.java为实体类,Emp.xml为映射文件,log4j.properties为mybatis打印日志,mybatis_cfg.xml为mybatis主配置文件,Test.java为测试类,pom.xml为maven引入依赖的文件。


我在项目中使用二级缓存,测试的方法是根据员工的编号查询员工的信息。


1、IEmpDAO.java (提供一个根据员工编号查询员工信息的接口方法)

1. <span style="font-size:18px;">package cn.sz.hcq.dao;  
2.   
3. import cn.sz.hcq.pojo.Emp;  
4.   
5. public interface IEmpDAO {  
6. /**
7.      * 根据员工编号查询员工信息
8.      * 
9.      * @param empno
10.      * @return
11.      */  
12. public Emp findAllByEmpno(Integer empno);  
13.   
14. }  
15. </span>





2、EmpDAOImpl.java 为接口的实现类


1. <span style="font-size:18px;">package cn.sz.hcq.dao.impl;  
2.   
3. import org.apache.ibatis.session.SqlSession;  
4.   
5. import cn.sz.hcq.dao.IEmpDAO;  
6. import cn.sz.hcq.factory.MybatisSqlSessionFactory;  
7. import cn.sz.hcq.pojo.Emp;  
8.   
9. public class EmpDAOImpl implements IEmpDAO {  
10.   
11. public Emp findAllByEmpno(Integer empno) {  
12. null;  
13. try {  
14.             sqlSession = MybatisSqlSessionFactory.getMySqlSession();  
15. return sqlSession.selectOne("cn.sz.hcq.pojo.Emp.findEmpByEmpno",  
16.                     empno);  
17. catch (Exception e) {  
18.             e.printStackTrace();  
19. finally {  
20.             MybatisSqlSessionFactory.closeSqlSession();  
21.         }  
22. return null;  
23.     }  
24. }  
25. </span>





3、MybatisSqlSessionFactory.java 为本人创建的获取sqlSession的工具类



1. <span style="font-size:18px;">package cn.sz.hcq.factory;  
2.   
3. import java.io.IOException;  
4. import java.io.Reader;  
5.   
6. import org.apache.ibatis.io.Resources;  
7. import org.apache.ibatis.session.SqlSession;  
8. import org.apache.ibatis.session.SqlSessionFactory;  
9. import org.apache.ibatis.session.SqlSessionFactoryBuilder;  
10.   
11. public class MybatisSqlSessionFactory {  
12. // 配置文件  
13. private static final String RESOURCE = "mybatis_cfg.xml";  
14. private static Reader reader = null;  
15. private static SqlSessionFactoryBuilder builder = null;  
16. private static SqlSessionFactory factory = null;  
17. // 可以在同一个线程范围内,共享一个对象  
18. private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();  
19.   
20. // 静态代码块(类加载的时候执行一次)  
21. static {  
22. try {  
23.             reader = Resources.getResourceAsReader(RESOURCE);  
24. new SqlSessionFactoryBuilder();  
25.             factory = builder.build(reader);  
26. catch (IOException e) {  
27.             e.printStackTrace();  
28.         }  
29.     }  
30.   
31. public static SqlSession getMySqlSession() {  
32. // 从本地线程中获取session连接  
33.         SqlSession sqlSession = threadLocal.get();  
34. // 连接为空则创建连接,并将该连接添加到本地线程中去  
35. if (sqlSession == null) {  
36. if (factory == null) {  
37.                 rebuildFactory();  
38.             }  
39.             sqlSession = factory.openSession();  
40.         }  
41.         threadLocal.set(sqlSession);  
42. return sqlSession;  
43.     }  
44.   
45. // 创建工厂  
46. public static void rebuildFactory() {  
47. try {  
48.             reader = Resources.getResourceAsReader(RESOURCE);  
49. new SqlSessionFactoryBuilder();  
50.             factory = builder.build(reader);  
51. catch (IOException e) {  
52.             e.printStackTrace();  
53.         }  
54.     }  
55.   
56. // 关闭连接  
57. public static void closeSqlSession() {  
58.         SqlSession sqlSession = threadLocal.get();  
59. if (sqlSession != null) {  
60. // 关闭session  
61.             sqlSession.close();  
62.         }  
63. // 同时将本地线程中置为null(防止用户再次调用时出现空的session)  
64. null);  
65.     }  
66.   
67. }  
68. </span>





4、Emp.java为实体类


1. <span style="font-size:18px;">package cn.sz.hcq.pojo;  
2.   
3. import java.io.Serializable;  
4. import java.util.Date;  
5.   
6. public class Emp implements Serializable {  
7. private Integer empno;  
8. private String ename;  
9. private String job;  
10. private Integer mgr;  
11. private Date hiredate;  
12. private Double sal;  
13. private Double comm;  
14.   
15. public Integer getEmpno() {  
16. return empno;  
17.     }  
18.   
19. public void setEmpno(Integer empno) {  
20. this.empno = empno;  
21.     }  
22.   
23. public String getEname() {  
24. return ename;  
25.     }  
26.   
27. public void setEname(String ename) {  
28. this.ename = ename;  
29.     }  
30.   
31. public String getJob() {  
32. return job;  
33.     }  
34.   
35. public void setJob(String job) {  
36. this.job = job;  
37.     }  
38.   
39. public Integer getMgr() {  
40. return mgr;  
41.     }  
42.   
43. public void setMgr(Integer mgr) {  
44. this.mgr = mgr;  
45.     }  
46.   
47. public Date getHiredate() {  
48. return hiredate;  
49.     }  
50.   
51. public void setHiredate(Date hiredate) {  
52. this.hiredate = hiredate;  
53.     }  
54.   
55. public Double getSal() {  
56. return sal;  
57.     }  
58.   
59. public void setSal(Double sal) {  
60. this.sal = sal;  
61.     }  
62.   
63. public Double getComm() {  
64. return comm;  
65.     }  
66.   
67. public void setComm(Double comm) {  
68. this.comm = comm;  
69.     }  
70.   
71. }  
72. </span>





5、Emp.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="cn.sz.hcq.pojo.Emp">
	<!-- 配置二级缓存 -->
	<cache eviction='FIFO' flushInterval='60000' size='512' readOnly='true' />

	<!-- 根据员工编号查询员工信息 -->
	<select id="findEmpByEmpno" parameterType="java.lang.Integer"
		resultType="cn.sz.hcq.pojo.Emp" useCache="true">
		select
		empno,ename,job,mgr,sal,comm,hiredate from
		emp
		where
		empno=#{empno}
	</select>

</mapper>




6、log4j.properties为mybatis打印日志


1. <span style="font-size:18px;">log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
2. log4j.appender.stdout.Target=System.out  
3. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
4. log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n  
5. log4j.appender.file=org.apache.log4j.FileAppender  
6.   
7. log4j.appender.file.File=d:/log.txt  
8.   
9. log4j.appender.file.layout=org.apache.log4j.PatternLayout  
10.   
11. log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}  %m%n  
12. log4j.rootLogger=debug,file,stdout  
13. #fatal-->error-->warn-->info-->debug  
14.   
15. log4j.logger.com.ibatis=DEBUG  
16. log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG  
17. log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG  
18. log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG  
19. log4j.logger.java.sql.Connection=DEBUG  
20. log4j.logger.java.sql.Statement=DEBUG  
21. log4j.logger.java.sql.PreparedStatement=DEBUG</span>





7、mybatis_cfg.xml为mybatis主配置文件


<?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>
	<!-- 配置的参数 -->
	<settings>
		<!-- 开启二级缓存,默认为true(默认二级缓存是开启的) -->
		<setting name="cacheEnabled" value="true" />
	</settings>
	
	<!--数据源 -->
	<environments default="myconn">
		<environment id="myconn">
			<!--事务管理方式 -->
			<transactionManager type="JDBC"></transactionManager>
			<!--数据库连接参数 -->
			<dataSource type="POOLED">
				<!-- type:数据源连接的方式 ,POOLED:连接池方式, UNPOOLED: 非连接池的方式 ,JNDI:java命名与目录接口方式 -->
				<property name="driver" value="org.gjt.mm.mysql.Driver"></property>
				<property name="url" value="jdbc:mysql://localhost:3306/db"></property>
				<property name="username" value="root"></property>
				<property name="password" value="root"></property>
			</dataSource>
		</environment>
	</environments>

	<!-- 引入实体映射文件 -->
	<mappers>
		<mapper resource="cn/sz/hcq/pojo/Emp.xml" />
	</mappers>
</configuration>




8、pom.xml为maven引入依赖的文件


1. <span style="font-size:18px;"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
3. <modelVersion>4.0.0</modelVersion>  
4. <groupId>cn.sz.hcq.pro</groupId>  
5. <artifactId>Mybatis_ehcache_06</artifactId>  
6. <version>0.0.1-SNAPSHOT</version>  
7. <packaging>war</packaging>  
8. <dependencies>  
9. <!-- mysql数据库驱动 -->  
10. <dependency>  
11. <groupId>mysql</groupId>  
12. <artifactId>mysql-connector-java</artifactId>  
13. <version>5.0.8</version>  
14. </dependency>  
15. <!--mybatis依赖 -->  
16. <dependency>  
17. <groupId>org.mybatis</groupId>  
18. <artifactId>mybatis</artifactId>  
19. <version>3.2.3</version>  
20. </dependency>  
21.   
22. <!-- 日志记录 -->  
23. <dependency>  
24. <groupId>log4j</groupId>  
25. <artifactId>log4j</artifactId>  
26. <version>1.2.17</version>  
27. </dependency>  
28. <dependency>  
29. <groupId>cglib</groupId>  
30. <artifactId>cglib</artifactId>  
31. <version>2.2.2</version>  
32. </dependency>  
33. <dependency>  
34. <groupId>org.slf4j</groupId>  
35. <artifactId>slf4j-log4j12</artifactId>  
36. <version>1.7.5</version>  
37. </dependency>  
38.   
39. </dependencies>  
40. </project></span>





9、Test.java为测试类

1. <span style="font-size:18px;">package cn.sz.hcq.test;  
2.   
3. import cn.sz.hcq.dao.IEmpDAO;  
4. import cn.sz.hcq.dao.impl.EmpDAOImpl;  
5. import cn.sz.hcq.pojo.Emp;  
6.   
7. public class Test {  
8. public static void main(String[] args) {  
9. new EmpDAOImpl();  
10. // 查询编号为7788的员工  
11. // 这里我们连续调用两次方法,发现sql语句就执行一次  
12. 7788);  
13.   
14. 7788);  
15.           
16. "姓名:" + emp.getEname() + ",员工号:" + emp.getEmpno());  
17.     }  
18. }  
19. </span>



在测试中我们两次调用查询方法,在控制台的日志信息中发现我们的sql语句只是执行了一次,因为第二次查询的时候数据从二级缓存中取出(第一次查询时将数据保存到了二级缓存中)。

下面是日志的截图:


redis mybatis二级缓存 实现类 mybatis二级缓存配置_缓存机制_03