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表,表的字段如下:
本人演示的项目是Maven项目:
项目结构如下:
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语句只是执行了一次,因为第二次查询的时候数据从二级缓存中取出(第一次查询时将数据保存到了二级缓存中)。
下面是日志的截图: