MyBatis的缓存

149 阅读3分钟

缓存

缓存是指存在于内存中的临时数据,使用缓存可以减少和数据库的交互次数,提高执行效率。

什么样的数据适用缓存

经常查询且不经常改变的;数据的正确与否对最终结果影响不大的。

一级缓存

一级缓存是指MyBatis中SqlSession对象的缓存,当SqlSession消失时,一级缓存随之消失。

当我们执行查询后,查询的结果会同时存入到SqlSession中的一块区域。当我们再次查询同样数据时,MyBatis会先从SqlSession查询,若存在结果则使用。

证明一级缓存的存在

详情配置文件参照前面几章节

public class Person {
    private Integer id;
    private Date birthday;
    private String name;
    //省略get /set方法
}

dao层接口

public interface PersonDao {
    Person findById(Integer pid);
}

映射文件PersonDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.on1.dao.PersonDao">
    <select id="findById" resultType="person" parameterType="int">
        select * from person where id = #{pid}
    </select>
    
</mapper>

测试方法:

public class day5 {
    InputStream in;
    SqlSession sqlSession;
    PersonDao personDao;


    @Before
    public void init() throws Exception {
        //        加载配置文件
        in = Resources.getResourceAsStream("mybatisConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();
        //        获取代理对象
        personDao = sqlSession.getMapper(PersonDao.class);
    }

    @After
    public void destroy() throws Exception {
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFirstLevelCache() {
        Person p1 = personDao.findById(41);
        System.out.println(p1);
        Person p2 = personDao.findById(41);
        System.out.println(p2);
        System.out.println(p1 == p2);
    }

}

控制台输出如下,可以发现,两个对象是相同的,且只执行了一句查询语句

证明一级缓存的清空

映射文件PersonDao.xml中添加更新操作:

<update id="updatePerson" parameterType="Person">
    update person set name = #{name} where id = #{id}
</update>

测试方法:

 @Test
    public void testUpdate() {
        Person p1 = personDao.findById(42);
        System.out.println(p1);

        p1.setName("on1");
        p1.setBirthday(new Date());
        personDao.updatePerson(p1);

        Person p2 = personDao.findById(42);
        System.out.println(p2);
        System.out.println(p1 == p2);
    }

控制台输出如下,两个对象并不相同。

当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

二级缓存

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存。

示例

在配置文件中开启二级缓存

<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>

映射文件PersonDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.on1.dao.PersonDao">
    <cache></cache>
    <!--  useCache=”true”代表当前这个 statement 要使用二级缓存;  -->
    <select id="findById" resultType="person" parameterType="int"
                                useCache="false">
        select * from person where id = #{pid}
    </select>
</mapper>

测试:

public class day5 {
    InputStream in;
    SqlSession sqlSession;
    SqlSessionFactory factory;
    PersonDao personDao;

    @Before
    public void init() throws Exception {
        //        加载配置文件
        in = Resources.getResourceAsStream("mybatisConfig.xml");
        factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();
        //        获取代理对象
        personDao = sqlSession.getMapper(PersonDao.class);
    }

    @After
    public void destroy() throws Exception {
        in.close();
    }


    @Test
    public void testTwoLevelCache() {
        Person p1 = personDao.findById(42);
        sqlSession.commit();
        System.out.println(p1);
        sqlSession.close();     //清空一级缓存

        SqlSession ss2 = factory.openSession();
        PersonDao personDao2 = ss2.getMapper(PersonDao.class);
        Person p2 = personDao2.findById(42);
        System.out.println(p2);
        System.out.println(p1 == p2);
    }

}

控制台输出如下,发现它一共执行了两条查询语句

如果我们在映射文件中使用二级缓存:

    <!--  每次查询都需要最新数据,因此要禁用二级缓存  -->
<select id="findById" resultType="person" parameterType="int"
useCache="true">
    select * from person where id = #{pid}
</select>

则控制台输出如下,它只执行一条查询语句

无论是否使用二级缓存,你会发现两个对象p1和p2都是不相同的,这是因为二级缓存存放的内容是数据,类似于:

"id": 42, "name":"on1"

每当有要返回bean类时,它就会新建一个数据是二级缓存的数据的bean类,然后返回该对象,因此p1,p2对象是不同的。

当我们在使用二级缓存时,所缓存的bean类一定要实现 java.io.Serializable 接口,这样就可以使用序列化的方式来保存对象

public class Person implements Serializable {
...
}