mybatis 一级缓存和二级缓存

2022-10-12 11:59:35

 配置mybatis

maven:

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.0.0</version>
    </dependency>

application.yml:

server:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatisstu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: root
    password: 123456
mybatis: #mybatis配置
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.fen.dou.entity
  config-location: classpath:config/mybatis.xml

 classpath:config/mybatis.xml 其中 <setting name="logImpl" value="STDOUT_LOGGING"/>打印执行sql

<?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>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>

一级缓存

一级缓存作用在同一个sqlSession内的,是默认开启的,添加事务才会生效,一个事务内及一个业务逻辑之内应该不会重复去读取事务(跟可重复读事务相匹配,不会产生脏数据,如果为读未提交和读已提交事务,则会产生脏数据),所以没必要有一级缓存

    @Override
    @Transactional
    public User findUser() {
        userMapper.findUser();
        userMapper.findUser();
        return userMapper.findUser();
    }
import com.fen.dou.Application;
import com.fen.dou.service.IProductService;
import com.fen.dou.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class})
public class TestJunit {

    @Autowired
    private IUserService userService;

    @Test
    public void testSay() throws Exception {
        userService.findUser();
    }
}

 在一个事务内,多次读取数据只会查询一次数据库

二级缓存

二级缓存作用的在同一个mapper作用域下,需手动开启,一个mapper对应的缓存,多个sqlSession可以共享

 手动配置:

 1、配置<setting name="cacheEnabled" value="true" />

<?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>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <setting name="cacheEnabled" value="true" />
</configuration>

2、需要在二级缓存生效的mapper中配置:<cache></cache>

<?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.fen.dou.mapper.ProductMapper">
    <cache></cache>
    <select id="selectCountById" parameterType="int" resultType="int">
        select `count` from product where id=#{productId}
    </select>

    <update id="reduceCount" parameterType="map">
        UPDATE product SET count=count-1 WHERE id=1;
    </update>

</mapper>
<?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.fen.dou.mapper.UserMapper">
    <cache></cache>
    <resultMap id="ResultMap" type="com.fen.dou.entity.User">
        <id column="id" property="id" />
    </resultMap>
    <resultMap id="ResultMap1" type="com.fen.dou.entity.UserVo">

    </resultMap>

    <select id="findUser" resultMap="ResultMap">
        select id,username,password from user where id = 1;
    </select>
    <update id="updateUser" >
       update user set username = 'yangcai' where id = 1
    </update>

    <select id="findUserProduct" resultMap="ResultMap1" useCache="false">
        select product.count,username,password from user left join product on user.id = product.userId where user.id = 1;
    </select>

</mapper>

 3、需要在sql返回结果的实体中添加序列化

@Data
public class UserVo implements Serializable {
    private Integer count;
    private String username;
    private String password;
}
@Data
public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;



    public User(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }
}

 现在来测试二级缓存

    @GetMapping(value = "/test1")
    public User test1(){
        return this.userService.findUser();
    }
    <select id="findUser" resultMap="ResultMap">
        select id,username,password from user where id = 1;
    </select>

请求两次, 两个会话只查询了一次

当执行关联表的sql

    @GetMapping(value = "/test2")
    public UserVo test5(){
        return this.userService.findUserProduct();
    }
   <select id="findUserProduct" resultMap="ResultMap1">
        select product.count,username,password from user left join product on user.id = product.userId where user.id = 1;
    </select>

 这个缓存作用在userMapper作用域下,此时数据库中的count的值确实是5

当我在productMapper作用域下更新count值

    @GetMapping(value = "/update1")
    public void test3() throws Exception {
        this.productService.reduceCount(1,1);
    }
    <update id="reduceCount" parameterType="map">
        UPDATE product SET count=count-1 WHERE id=1;
    </update>

 数据库中的值变成4了,再次请求http://localhost:8080/test2,count的值仍然是5,造成数据不一致了(数据库和缓存的数据不相同)

 所以,这个sql不能生成二级缓存,否则会造成数据不一致,可以对一个mapper下单个查询设置不生成二级缓存,配置 useCache="false",则每次都是从数据库里面去查询数据

    <select id="findUserProduct" resultMap="ResultMap1" useCache="false">
        select product.count,username,password from user left join product on user.id = product.userId where user.id = 1;
    </select>

 二级缓存对单表查询可以设置设置二级缓存,对多表联合查询,需要禁止开启二级缓存,否则会造成数据不一致

  • 作者:为爱停留
  • 原文链接:https://blog.csdn.net/beiduofen2011/article/details/123978786
    更新时间:2022-10-12 11:59:35