mybatis的缓存机制
简介:
Mybatis是我们常用到的数据层访问框架,在通常的开发过程中,我们一般是使用它的默认的缓存配置。这里的话我们简单的分析一下Mybatis的缓存机制。
Mybatis的一级缓存:
在程序的运行过程中,我们有可能在一次的数据库会话中,执行多次查询条件完全相同的查询语句,这时候Mybatis中的一级缓存就排上了用场,如果是执行相同的SQL语句,会避免直接对数据库来进行操作,而是从一级缓存中来查询数据,这样可以在一定程度上提高查询的性能。
一级缓存的应用范围是在SqlSession中,当用户发起松球的时候,Mybatis会根据当前执行的语句生成映射声明(MappedStatement),然后在Local Cache中进行查询,如果有缓存中有数据的数就直接返回查询的结果给用户。如果缓存中没有的话,查询数据库,并且将结果写入到Local Cache中,最后将结果返回个用户。下面做一个基本的案例展示。
User类的代码如下:
1 package com.buwei.entity; 2 3 /** 4 * @author buwei 5 * @date 2018/12/20 12:33 6 */ 7 public class User { 8 private int id; 9 private String name; 10 private String password; 11 // 省略getter和setter方法 12 }
接口UserMapper和对应的映射文件UserMapper.xml的代码如下:
1 package com.buwei.mapper; 2 3 import com.buwei.entity.User; 4 import java.util.List; 5 /** 6 * @author buwei 7 * @date 2018/12/20 12:34 8 */ 9 public interface UserMapper { 10 /** 11 * 查询所有 12 * @return 返回所有的查询数据 13 */ 14 List<User> findAll(); 15 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.buwei.mapper.UserMapper"> 6 <!--配置查询所有--> 7 <select id="findAll" resultType="com.buwei.entity.User"> 8 select * from user 9 </select> 10 </mapper>
主要的配置就是Mybatis的配置文件了,需要配置开启一级缓存的配置语句,在文件中有相应的注释,具体配置在第十行:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 7 <settings> 8 <!--控制台打印日志--> 9 <setting name="logImpl" value="STDOUT_LOGGING"/> 10 <!--开启一级缓存--> 11 <setting name="localCacheScope" value="SESSION"/> 12 </settings> 13 <!--配置mybatis的环境--> 14 <environments default="mysql"> 15 <!--配置mysql的环境--> 16 <environment id="mysql"> 17 <!--配置事务的类型--> 18 <transactionManager type="JDBC"></transactionManager> 19 <!--配置连接数据库的信息,用的是数据源(连接池)--> 20 <dataSource type="POOLED"> 21 <property name="driver" value="com.mysql.jdbc.Driver"/> 22 <property name="url" value="jdbc:mysql://localhost:3306/mybatiscache"/> 23 <property name="username" value="root"/> 24 <property name="password" value="root"/> 25 </dataSource> 26 </environment> 27 </environments> 28 29 30 <!--告知mybatis映射配置的位置--> 31 <mappers> 32 <package name="com.buwei.mapper"></package> 33 </mappers> 34 </configuration>
测试类:MybatisCacheTest,测试创建一个SQLsession的情况下,连续执行五次查询,控制台打印实际的查询:
1 package com.buwei.test; 2 3 import com.buwei.entity.User; 4 import com.buwei.mapper.UserMapper; 5 import org.apache.ibatis.io.Resources; 6 import org.apache.ibatis.session.SqlSession; 7 import org.apache.ibatis.session.SqlSessionFactory; 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 9 10 import java.io.IOException; 11 import java.io.InputStream; 12 import java.util.List; 13 14 /** 15 * @author buwei 16 * @date 2018/12/20 12:40 17 */ 18 19 public class MybatisCacheTest { 20 21 public static void main(String[] args) throws IOException { 22 // 1.读取配置文件 23 InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); 24 // 2.创建sqlSessionFactory的构建者对象 25 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 26 // 3.使用构建者创建工厂对象SQLSessionFactory 27 SqlSessionFactory factory = builder.build(in); 28 // 4.使用SQLSessionFactory生产SQLSession 29 SqlSession session = factory.openSession(); 30 // 5.使用session创建mapper的代理对象 31 UserMapper userMapper = session.getMapper(UserMapper.class); 32 // 6使用代理对象执行查询所有方法 33 for (int i = 0; i < 5; i++) { 34 List<User> list = userMapper.findAll(); 35 System.out.println(list); 36 } 37 // 7.释放资源 38 session.close(); 39 in.close(); 40 } 41 }
执行测试方法,我们会发现在实际的查询过程中,只向数据库执行了一次查询,控制台打印如下:
Mybatis的二级缓存:
我们前面讲到的一级缓存,它的共享范围就是在SqlSession中,如果多个SqlSession之间需要共享缓存,这时候就需要使用到二级缓存了。二级缓存的范围是在namespace中的。开启了二级缓存之后,执行查询一级缓存之前,会先查询二级缓存。
开启二级缓存,我们需要在Mybatis的配置文件中配置:
1 <setting name="cacheEnabled" value="true"/>
需要在Mybatis的接口映射文件中配置cache挥着cache-ref,如下:
1 <cache/>
创建测试类MybatisCacheTest02:
1 package com.buwei.test; 2 3 import com.buwei.entity.User; 4 import com.buwei.mapper.UserMapper; 5 import org.apache.ibatis.io.Resources; 6 import org.apache.ibatis.session.SqlSession; 7 import org.apache.ibatis.session.SqlSessionFactory; 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 9 10 import java.io.IOException; 11 import java.io.InputStream; 12 import java.util.List; 13 14 /** 15 * @author buwei 16 * @date 2018/12/20 12:40 17 */ 18 19 public class MybatisCacheTest02 { 20 21 public static void main(String[] args) throws IOException { 22 // 1.读取配置文件 23 InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml"); 24 // 2.创建sqlSessionFactory的构建者对象 25 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 26 // 3.使用构建者创建工厂对象SQLSessionFactory 27 SqlSessionFactory factory = builder.build(in); 28 // 4.使用SQLSessionFactory生产SQLSession 29 SqlSession session01 = factory.openSession(); 30 SqlSession session02 = factory.openSession(); 31 // 5.使用session创建mapper的代理对象 32 UserMapper userMapper01 = session01.getMapper(UserMapper.class); 33 UserMapper userMapper02 = session02.getMapper(UserMapper.class); 34 // 6使用代理对象执行查询所有方法 35 for (int i = 0; i < 2; i++) { 36 List<User> list01 = userMapper01.findAll(); 37 System.out.println( "userMapper01读取数据"+ list01); 38 } 39 for (int i = 0; i < 2; i++) { 40 List<User> list02 = userMapper02.findAll(); 41 System.out.println("userMapper02读取数据"+list02); 42 } 43 // 7.释放资源 44 session02.close(); 45 session01.close(); 46 in.close(); 47 } 48 }
执行方法之后我们发现在session没有执行commit()方法时,二级缓存并没有起到作用
我们在userMapper01执行了查询之后执行:
1 session01.commit();
然后再次执行测试方法:
从控制台打印的数据可以知道,session02的查询有使用到缓存。
还有就是当我们执行了更新数据之后,会清空缓存,这里就没有做展示了,有兴趣的可以自己来测试一下。
二级缓存的范围是namespace的,然后我们通常就是一张表的查询会对应一个映射文件,就是在我们执行多表查询的时候有时候不同的namespace之间数据更新是感应不到的,这时候就会读取到脏数据,这时候可以使用cache-ref来解决问题,后面有机会会再续写上:)