MyBatis 緩存機制
前言
對於現在的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的同一個參數,第一次從數據庫中查詢後,第二次查詢就直接從緩存中取數據了,不用經過數據庫。
MyBatis 緩存機制