前言
对于现在的java开发人员来说,MyBatis无疑是一种优秀的持久化框架,它可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录,是用来操作数据库的一把利器,本节想跟大家分享学习一下MyBatis的缓存机制,提供了一级、二级缓存来缓存数据,以提高查询的性能。
一、一级缓存
MyBatis默认开启一级缓存,一级缓存是SqlSession级别的缓存,意思就是,同一个SqlSession多次调用同一个Mapper中的同一个方法(即执行相同的SQL语句)只会进行一次数据库查询,第一次执行完后会将数据库中查询到的数据写到缓存,第二次直接从缓存中取数据并不会进行第二次数据库查询。当SqlSession执行其他的操作时,会清空缓存。
二、二级缓存
MyBatis默认没有开启二级缓存,开启时需要在MaBatis配置文件中写入如下代码:
<settings> <setting name="cacheEnabled" value="true"/> </settings>
接着还要在Mapper.xml映射文件中开启当前的二级缓存:
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"></cache>
二级缓存是mapper级别的缓存,多个SqlSession可以共用二级缓存,意思就是,不同的SqlSession两次执行相同的namespace下的相同的Sql语句,第二次查询只会查询第一次查询并缓存的数据,也不会去数据库中查询。
三、一级缓存测试
1、运行环境
JDK1.8
MyBatis
Mysql5.7
Maven
编译器: IntelliJ IDEA
2、项目结构
3、步骤
(1)、Maven库
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.9</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.1.1</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
(2)mysql数据表
mysql> desc student; +-------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | sex | varchar(255) | YES | | NULL | | +-------+--------------+------+-----+---------+----------------+ 4 rows in set
(3)实体类
public class Student { private Integer id; private String name; private Integer age; private String sex; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + '}'; } }
(4)创建dao层接口
public interface StudentMapper { public Student selectStuById(Integer id)throws Exception; }
(5)创建mapper映射文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.stone.dao.StudentMapper"> <select id="selectStuById" parameterType="int" resultType="com.stone.model.Student"> select * from student where id=#{id} </select> </mapper>
(6)创建mybatis-config.xml配置文件
<?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> <typeAliases> <package name="com.stone.model" /> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/bjsxt" /> <property name="username" value="root" /> <property name="password" value="xiaokai960201" /> </dataSource> </environment> </environments> <mappers> <mapper resource="Mapper/StudentMapper.xml" /> </mappers> </configuration>
(7)log4j日志配置
#设置日志的级别,定义日志信息的输出目的 log4j.rootLogger=DEBUG, A1 ,R #定义A1的输出目的地为控制台 log4j.appender.A1=org.apache.log4j.ConsoleAppender #布局为 PatternLayout 可以灵活地指定布局模式。 log4j.appender.A1.layout=org.apache.log4j.PatternLayout #设置输出格式 log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%c]-[%p] %m%n #定义R的输出目的地为文件,并且文件大小到达指定尺寸的时候产生一个新的文件 log4j.appender.R=org.apache.log4j.RollingFileAppender #设置输出的文件地址 log4j.appender.R.File=D:\\Test_Log4j.log #设置文件大小伟100 kb 文件到达100时,产生一个新文件, #MaxBackupIndex 最大记录的文件数为1 查过一个文件删除文件较早的。 log4j.appender.R.MaxFileSize=100KB log4j.appender.R.MaxBackupIndex=1 #以下和上面一样 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
(8)OneCacheTest测试类(相同sqlsession)
public class OneCacheTest { public static void main(String[] args) throws Exception { OneCacheTest oneCacheTest=new OneCacheTest(); oneCacheTest.test1(); } public void test1() throws Exception{ SqlSession session = SqlSessionFac.getSqlSession(); StudentMapper studentMapper=session.getMapper(StudentMapper.class); Student student1=studentMapper.selectStuById(1); System.out.println(student1.toString()); Student student2=studentMapper.selectStuById(1); System.out.println(student2.toString()); Student student5=studentMapper.selectStuById(1); System.out.println(student5.toString()); } }
结果:
2018-04-28 21:11:49 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 21:11:51 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1268650975.
2018-04-28 21:11:51 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@4b9e13df]
2018-04-28 21:11:51 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Preparing: select * from student where id=?
2018-04-28 21:11:51 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name='asd', age=11, sex='男'}
Student{id=1, name='asd', age=11, sex='男'} Student{id=1, name='asd', age=11, sex='男'}
(9)OneCacheTest测试类(不同sqlsession)
public class OneCacheTest {
public static void main(String[] args) throws Exception {
OneCacheTest oneCacheTest=new OneCacheTest();
oneCacheTest.test1();
}
public void test1() throws Exception{
SqlSession session1 = SqlSessionFac.getSqlSession();
StudentMapper studentMapper1=session1.getMapper(StudentMapper.class);
Student student1=studentMapper1.selectStuById(1);
System.out.println(student1.toString());
session1.close();
SqlSession session2 = SqlSessionFac.getSqlSession();
session2 = SqlSessionFac.getSqlSession();
StudentMapper studentMapper2=session2.getMapper(StudentMapper.class);
Student student2=studentMapper2.selectStuById(1);
System.out.println(student2.toString());
session2.close();
}
}
结果:
2018-04-28 21:20:13 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 21:20:13 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1268650975.
2018-04-28 21:20:13 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@4b9e13df]
2018-04-28 21:20:13 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Preparing: select * from student where id=?
2018-04-28 21:20:13 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name='asd', age=11, sex='男'}
2018-04-28 21:20:14 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 21:20:14 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1620303253.
2018-04-28 21:20:14 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@6093dd95]
2018-04-28 21:20:14 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Preparing: select * from student where id=?
2018-04-28 21:20:14 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name='asd', age=11, sex='男'}
(10)总结
由上述结果可看出,同一个SqlSession多次调用同一个Mapper中的同一个方法(即执行相同的SQL语句)只会进行一次数据库查询,第一次执行完后会将数据库中查询到的数据写到缓存,第二次直接从缓存中取数据并不会进行第二次数据库查询。而不同的sqlsession会进行数据库查询,所以一级缓存是SqlSession级别的。
四、二级缓存测试
(1)在Mybatis-config中的configuration标签中开启二级缓存
<settings> <!--开启二级缓存--> <setting name="cacheEnabled" value="true"/> </settings>
(2)在Mapper映射文件中 开启当前mapper 的 namespace 下的二级缓存
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"></cache>
(3)创建TwoCacheTest测试类
public class OneCacheTest { public static void main(String[] args) throws Exception { OneCacheTest oneCacheTest=new OneCacheTest(); oneCacheTest.test1(); } public void test1() throws Exception{ // 获取 SqlSession 对象 SqlSession session1 = SqlSessionFac.getSqlSession(); StudentMapper studentMapper1 = session1.getMapper(StudentMapper.class); Student student = studentMapper1.selectStuById(1); System.out.println(student.toString()); // 关闭 session1.close(); // 再次获取 SqlSession 对象 SqlSession session2 = SqlSessionFac.getSqlSession(); StudentMapper studentMapper2 = session2.getMapper(StudentMapper.class); Student student2 = studentMapper2.selectStuById(1); System.out.println(student2.toString()); session2.close(); } }
结果:
2018-04-28 22:15:38 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Openning JDBC Connection
2018-04-28 22:15:38 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 94345706.
2018-04-28 22:15:38 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ooo Using Connection [com.mysql.jdbc.JDBC4Connection@59f99ea]
2018-04-28 22:15:38 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Preparing: select * from student where id=?
2018-04-28 22:15:38 [com.stone.dao.StudentMapper.selectStuById]-[DEBUG] ==> Parameters: 1(Integer)
Student{id=1, name='asd', age=11, sex='男'}
2018-04-28 22:15:38 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@59f99ea]
2018-04-28 22:15:38 [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@59f99ea]
2018-04-28 22:15:38 [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 94345706 to pool.
2018-04-28 22:15:38 [org.apache.ibatis.cache.decorators.LoggingCache]-[DEBUG] Cache Hit Ratio [com.stone.dao.StudentMapper]: 0.5
Student{id=1, name='asd', age=11, sex='男'}
(4)由结果可以看出
开启二级缓存后,不同sqlsession访问同一个sql的同一个参数,第一次从数据库中查询后,第二次查询就直接从缓存中取数据了,不用经过数据库。