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来解决问题,后面有机会会再续写上:)

posted @ 2018-12-20 13:08  木易yang~  阅读(273)  评论(0编辑  收藏  举报