mybatis框架学习-缓存

罗里吧嗦

什么是缓存

Mybatis中的缓存
什么是缓存
    存在于内存中的临时数据。
为什么使用缓存
    减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
    适用于缓存:
        经常查询并且不经常改变的。
        数据的正确与否对最终结果影响不大的。
    不适用于缓存:
        经常改变的数据
        数据的正确与否对最终结果影响很大的。
        例如:商品的库存,银行的汇率,股市的牌价。
Mybatis中的一级缓存和二级缓存
    一级缓存:
        它指的是Mybatis中SqlSession对象的缓存。
        当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
        该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
        查询是否有,有的话直接拿出来用。
        当SqlSession对象消失时,mybatis的一级缓存也就消失了。
    
    二级缓存:
        它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
        二级缓存的使用步骤:
            第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
            第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
            第三步:让当前的操作支持二级缓存(在select标签中配置)

一级缓存

一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush close,它就存在。
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除, commit()close()方法时,就会清空一级缓存。

第一次发起查询用户 id 1 的用户信息,先去找缓存中是否有 id 1 的用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果此时 sqlSession 去执行 commit 操作(执行插入、更新、删除),

就会清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户 id 1 的用户信息,先去找缓存中是否有 id 1 的用户信息,缓存中有,直接从缓存中获取用户信息。

证明一级缓存的存在

执行了两次查询同一个id的对象,只执行了一个sql语句,也就是第二次没有执行sql语句,而是直接拿第一次的结果

如果在查询了第一个的时候,关闭sqlsession,然后再次开启,则执行了两次sql语句,也就是没有缓存

执行更新操作,查询同一个id,执行了两次sql语句,也就是清除了一级缓存

 二级缓存

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

多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession

首先开启 mybatis 的二级缓存。

sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交, 将会清空该 mapper 映射下的二级缓存区域的数据。

sqlSession2 去查询与 sqlSession1 相同的用户信息, 首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。 

如何开启二级缓存

     * 步骤:
     *  1.缓存类要实现序列化接口,也就是public class User implements Serializable
     *  2.mybatis配置文件支持二级缓存,也就是<setting name="cacheEnabled" value="true"/>(默认支持)
     *  3.让当前配置文件支持二级缓存,也就是UserMapper.xml中<cache></cache>
     *  4.让当前操作支持二级缓存,也就是在select标签中useCache="true"
     * 另外,二级缓存,存的是数据,而不是对象

经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二
次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。

完整代码

1.创建maven项目导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cong</groupId>
    <artifactId>mybatis_cache</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

</project>

2.User类,以及代理mapper接口

package com.cong.pojo;

import java.io.Serializable;

public class User implements Serializable{
    private int uid;
    private String name;
    private char sex;
    private String addr;
//    @Override
//    public String toString() {
//        return "User{" +
//                "uid=" + uid +
//                ", name='" + name + '\'' +
//                ", sex=" + sex +
//                ", addr='" + addr + '\''+
//                '}';
//    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}



package com.cong.mapper;

import com.cong.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> findAll();//查询所有user,同时获取user含有的account信息
    User findById(int id);
    User findById2(int id);
    void updateUser(User user);
}

3.mybatis配置文件

<?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>
    <settings>
        <!--
        开启二级缓存的支持
         因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。
         为 true 代表开启二级缓存;为false 代表不开启二级缓存。
         -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
        <package name="com.cong.pojo"></package>
    </typeAliases>
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql://localhost:3306/rainbow?characterEncoding=UTF-8"></property>
                <property name="username" value="root"></property>
                <property name="password" value="123456"></property>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper class="com.cong.mapper.UserMapper"></mapper>-->
        <package name="com.cong.mapper"></package>
    </mappers>
</configuration>

4.映射文件

<?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.cong.mapper.UserMapper">
    <!-- 开启二级缓存的支持,测试一级缓存要删掉 -->
    <cache></cache>
    <select id="findAll" resultType="user">
        select * from user;
    </select>
    <select id="findById" resultType="user" parameterType="int">
        select * from user where uid = #{uid};
    </select>
    <!--useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false-->
    <!--注意: 针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。-->
    <select id="findById2" resultType="user" parameterType="int" useCache="true">
        select * from user where uid = #{uid};
    </select>
    <update id="updateUser" parameterType="user">
        update user set name = #{name},addr = #{addr} where uid = #{uid};
    </update>
</mapper>

5.测试一级缓存类

import com.cong.mapper.UserMapper;
import com.cong.pojo.User;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class TestFirstLevelCache {
    private SqlSessionFactory factory;
    private InputStream inputStream;
    private SqlSession sqlSession;
    private UserMapper userMapper;

    /**
     *测试一级缓存是否存在
     * 当把sqlsession关闭再重新打开的时候,就会发现最终得到的结果是false
     */
    @Test
    public void testExistFirstLevelCache0(){
        User user = userMapper.findById(5);
        User user1 = userMapper.findById(5);
        System.out.println(user);
        System.out.println(user1);
        System.out.println(user == user1);
    }
    @Test
    public void testExistFirstLevelCache(){
        User user = userMapper.findById(5);
        //再次开启sqlsession
        sqlSession.close();
        sqlSession = factory.openSession();
        userMapper = sqlSession.getMapper(UserMapper.class);
        //sqlSession.clearCache();//此方法就是清空一级缓存,跟上面代码同样的效果
        User user1 = userMapper.findById(5);
        System.out.println(user);
        System.out.println(user1);
        System.out.println(user == user1);
    }

    /**
     * 测试一级缓存的触发条件
     */
    @Test
    public void testClearCache(){
        User user = userMapper.findById(5);
        user.setName("new rainbow");
        user.setAddr("耀华里5-1");
        //更新操作,会触发清空一级缓存
        userMapper.updateUser(user);
        //再次查询
        User user1 = userMapper.findById(5);
        System.out.println(user);
        System.out.println(user1);
        System.out.println(user == user1);
    }
    @Test
    public void findAllUser(){
        List<User> users = userMapper.findAll();
        for (User user : users) {
            System.out.println(user.toString());
        }
    }
    @Before
    public void init() throws Exception{
        inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        factory = new SqlSessionFactoryBuilder().build(inputStream);
        sqlSession = factory.openSession();
        userMapper = sqlSession.getMapper(UserMapper.class);
    }
    @After
    public  void destroy() throws Exception{
        sqlSession.commit();
        sqlSession.close();
        inputStream.close();
    }
}

7.测试二级缓存类

import com.cong.mapper.UserMapper;
import com.cong.pojo.User;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;

public class TestSecondLevelCache {
    private InputStream in;
    private SqlSessionFactory factory;

    /**
     * 测试二级缓存
     * 步骤:
     *  1.缓存类要实现序列化接口,也就是public class User implements Serializable
     *  2.mybatis配置文件支持二级缓存,也就是<setting name="cacheEnabled" value="true"/>(默认支持)
     *  3.让当前配置文件支持二级缓存,也就是<cache></cache>
     *  4.让当前操作支持耳机缓存,也就是useCache="true"
     * 另外,二级缓存,存的是数据,而不是对象,所以最终结果还会使false
     */
    @Test
    public void test2(){
        SqlSession sqlSession = factory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.findById2(5);
        sqlSession.close();//一级缓存消失
        SqlSession newSqlSession = factory.openSession();
        UserMapper mapper1 = newSqlSession.getMapper(UserMapper.class);
        User user1 = mapper1.findById2(5);
        System.out.println(user);
        System.out.println(user1);
        newSqlSession.close();
        System.out.println(user == user1);//false
    }
    @Before//用于在测试方法执行之前执行
    public void init()throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取 SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
    }
    @After//用于在测试方法执行之后执行
    public void destroy()throws Exception{
        in.close();
    }
}

猜你喜欢

转载自www.cnblogs.com/ccoonngg/p/11361528.html