MyBatis_数据缓存

来源:互联网 发布:2333的意思网络用语 编辑:程序博客网 时间:2024/05/20 23:37

MyBatis的缓存机制

        在实际项目开发中,通常对数据库查询的性能要求很高,MyBatis提供了查询缓存来缓存数据,从而达到提高查询性能的要求。

MyBatis的查询缓存分为一级缓存二级缓存,一级缓存是SqlSession级别的缓存,二级缓存是Mapper级别的缓存,多个SqlSession共享二级缓存。一级与二级缓存的区别如下图:


1.  一级缓存

       MyBatis的一级缓存是SqlSession级别的缓存,在操作数据库时要构造SqlSession对象,一级缓存的作用域就是同一个SqlSession,在对象中提供有一个HashMap来存储缓存数据。不同的SqlSession之间的缓存数据区域之间是互不影响的。

       在同一个SqlSession中执行两次相同的sql语句,第一次执行完毕会将数据库中查询到的数据写到缓存(内存),第二次查询是会直接从缓存中获取数据,而不再查询数据库,从而提高查询效率。当一个SqlSession结束后(被关闭),该SqlSession中的一级缓存也就不存在了。MyBatis默认开启一级缓存,不用再手动设置。

       注意:MyBatis的缓存机制是基于id进行缓存的,MyBatis使用HashMap存储缓存数据是,使用对象的id作为key,而对象作为value保存。

一级缓存的测试:

1) MyBatis配置文件(SqlMapConfig.xml)

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="db.properties" /><!-- 定义别名 --><typeAliases><package name="com.sf.po" /></typeAliases><!-- 和spring整合后 environments配置将废除 --><environments default="development"><environment id="development"><!-- 使用jdbc事务管理 --><transactionManager type="JDBC" /><!-- 数据库连接池 --><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></dataSource></environment></environments><mappers><!-- 自动扫描mapper --><package name="com.sf.mapper" /></mappers></configuration>

2) UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.sf.mapper.UserMapper"><!-- 查询用户 --> <select id="findUserById" parameterType="int" resultType="user"> SELECT * FROM user WHERE id=#{id} </select>  <!-- 更新用户信息 --> <select id="updateUser" parameterType="user"> update user set username=#{username} where id=#{id} </select> </mapper>

3) UserMapper.java

package com.sf.mapper;import com.sf.po.User;public interface UserMapper {/** * 通过id获取用户信息 */public User findUserById(int id) throws Exception;/** * 更新用户信息 */public void updateUser(User user) throws Exception;}

4) 测试TestCache.java

package com.sf.junit.test;import java.io.InputStream;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.BeforeClass;import org.junit.Test;import com.sf.mapper.UserMapper;import com.sf.po.User;/** * 测试一级缓存的使用 * mybatis默认支持一级缓存,不需要在配置文件中去手动配置 * 一级缓存是sqlSession级别的缓存; * 在操作数据库时需要构造sqlSession对象,对象中有一个数据结构(HashMap)用于存储缓存数据 * 不同的sqlSession之间的缓存数据区域(HashMap)之间是互不影响的 * * @author sf * @time 2017-3-22 下午8:04:58 */public class TestCache {private static SqlSessionFactory sqlSessionFactory;@BeforeClasspublic static void setUpBeforeClass() throws Exception {String resource = "SqlMapConfig.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}@Testpublic void testFindUserById() throws Exception {SqlSession sqlSession = sqlSessionFactory.openSession();//创建UserMapper对象,mybatis自动生成mapper代理对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user1 = userMapper.findUserById(10);System.out.println(user1);User user2 = userMapper.findUserById(10);System.out.println(user2);sqlSession.close();}}

5) 执行结果:


       可以看到,在第一次查询id为10的User时执行了select语句,但是第二次执行查询时就没有再执行select语句,即一级缓存中缓存了id为10的User对象,MyBatis直接将该对象从缓存中取出来,并没有再去查询数据库。

       需要注意的是:sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。

      在上面的TestCache.java中加入如下代码:

User user1 = userMapper.findUserById(10);System.out.println(user1);user1.setUsername("王二麻子");/* * 如果sqlSession执行commit操作(插入、修改、删除),清空sqlSession的一级缓存, *  目的是为了让缓存中存储的是最新的数据,避免脏读 */sqlSession.commit();User user2 = userMapper.findUserById(10);System.out.println(user2);


       会看到执行了两次select操作,即sqlSession执行commit操作时,清空了一级缓存。

2.     二级缓存

       二级缓存是Mapper级别的缓存,是根据mapper的namespace划分的。相同namespace的mapper查询数据存放在同一个区域。在使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,其同样是使用HashMap进行数据缓存的。

       相比一级缓存,二级缓存的范围更大,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。不同的SqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则多义词操作会将数据库中查询到的数据写到缓存(内存),第二次查询是会直接从二级缓存中读取,提高查询效率。

二级缓存的测试:

1) 开启二级缓存:

在SqlMapConfig.xml中加入:

<!-- 二级缓存 需要在SqlMapConfig中设置二级缓存的总开关 --><settings><setting name="cacheEnabled" value="true" /></settings>

cacheEnabled的value为true即表示开启二级缓存,默认为false

在UserMapper.xml中加入:

<!-- 二级缓存 二级缓存除了要在SqlMapConfig.xml中配置总开关,还要在具体的Mapper.xml中开启二级缓存 在UserMapper.xml中开启二级缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)  --> <cache/>

表示此mapper开启二级缓存。

cache表示开启当前mapper的namespace下的二级缓存,该元素的属性设置如下:

  •  flushInterval:刷新间隔,可以被设置为任意的正整数(毫秒),默认情况下不设置,仅仅调用语句时刷新缓存数据。
  • size:缓存数目,默认为1024。
  • readOnly:只读。属性可以被设置为true或false。只读的属性会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。可读写的缓存会返回缓存对象的拷贝(通过序列化),这会慢一些,但是安全,故默认是false。

2)实现序列化

       使用二级缓存时,与查询结果映射的Java对象必须实现java.io.Serializable接口的序列化和反序列化操作,如果存在父类,其成员都需要实现序列化接口。实现序列化接口是为了对缓存据进行序列化和反序列化操作,因为额级缓存数据的存储介质多种多样,不一定在内存,可能在硬盘或服务器。

public class User implements Serializable {/* * 要使用二级缓存,还需要让pojo类实现Serializable接口 * 为了将二级缓存数据取出,执行反序列化操作,因为二级缓存的存储介质多种多样,不一定在内存 */private int id;private String username;private Date birthday;private String sex;private String address;

3) 测试:

@Testpublic void testFindUserById() throws Exception {SqlSession sqlSession1 = sqlSessionFactory.openSession();SqlSession sqlSession2 = sqlSessionFactory.openSession();SqlSession sqlSession3 = sqlSessionFactory.openSession();//创建UserMapper对象,mybatis自动生成mapper代理对象UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);User user1 = userMapper1.findUserById(10);System.out.println(user1);//这里执行sqlSession的关闭操作,将sqlSession中的数据写到二级缓存区域sqlSession1.close();UserMapper userMapper2 = sqlSession1.getMapper(UserMapper.class);User user2 = userMapper2.findUserById(10);System.out.println(user2);sqlSession2.close();}


可以看到只执行了一次select操作

4) 禁用缓存

       在UserMapper.xml中的select语句中设置useCache="false"可以禁用当前select语句的二级缓存,该属性值的默认值为true。

<select id="findUserById"parameterType="int" resultType="user" useCache="false">         SELECT* FROM user WHERE id=#{id}</select>

结果:


可以看到执行了两次select操作。

5) 刷新缓存:

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

@Testpublic void testFindUserById() throws Exception {SqlSession sqlSession1 = sqlSessionFactory.openSession();SqlSession sqlSession2 = sqlSessionFactory.openSession();SqlSession sqlSession3 = sqlSessionFactory.openSession();//创建UserMapper对象,mybatis自动生成mapper代理对象UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);User user1 = userMapper1.findUserById(10);System.out.println(user1);//这里执行sqlSession的关闭操作,将sqlSession中的数据写到二级缓存区域sqlSession1.close();/* * 测试执行舒心缓存操作时,清空缓存 * 结果会执行两次数据库查询 */UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);User user3 = userMapper3.findUserById(1);user3.setUsername("点点滴滴");userMapper3.updateUser(user3);sqlSession3.commit();sqlSession3.close();UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);User user2 = userMapper2.findUserById(10);System.out.println(user2);sqlSession2.close();}

<!-- 更新用户信息 --> <select id="updateUser" parameterType="user" flushCache="true"> update user set username=#{username} where id=#{id} </select>
结果:

可以看到执行了两侧select操作,说明执行update操作时刷新了二级缓存中的数据。



0 0
原创粉丝点击