104、【JavaEE】【Mybatis】缓存

2021-10-20  本文已影响0人  yscyber

1、概述

2、一级缓存

<select ····· flushCache="true">
    ······
</select>

2、二级缓存

MyBatis-15 MyBatis-16

1、相关的 POJO 需要实现序列化接口,即实现java.io.Serializable接口。

2、在核心配置文件中配置<setting name="cacheEnabled" value="true"/>(默认值为true,所以不显式配置也行)。cacheEnabled的含义是“全局性地开启或关闭所有映射器配置文件中已配置的任何缓存”,意思是cacheEnabledfalse,映射配置文件中的<cache/>配置也将无效。

3、在映射配置文件中配置<cache/>

4、在<select>标签中,使用useCache=true,表示当前查询将会使用二级缓存;否则该查询禁用二级缓存。

3、一级缓存与二级缓存的关系

4、案例

4.1、二级缓存造成“脏读”

-- 用户表
CREATE TABLE lg_user (
user_id INT,
user_nickname VARCHAR(20) NOT NULL,
user_phone_number VARCHAR(20) NOT NULL,
CONSTRAINT PRIMARY KEY pk_lg_user(user_id)
);

-- 订单表
CREATE TABLE lg_order (
order_id INT,
order_account DECIMAL(8,2) NOT NULL,
order_user_id INT NOT NULL,
CONSTRAINT PRIMARY KEY pk_lg_order(order_id)
);
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

import java.math.BigDecimal;

@NoArgsConstructor
@AllArgsConstructor
@Data
@ToString
public class Order implements Serializable {

    private static final long serialVersionUID = 7010281991983231524L;

    private Integer id;

    private BigDecimal account;

}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;
import java.util.List;

@NoArgsConstructor
@AllArgsConstructor
@Data
@ToString()
public class User implements Serializable {

    private static final long serialVersionUID = -6402364968509046679L;

    private Integer id;

    private String nickname;

    private String phoneNumber;

    private List<Order> orderList;

}
import com.yscyber.mybatis.five.pojo.Order;

import java.util.List;

public interface OrderRepo {

    List<Order> listOrdersByUserId(Integer userId);

    int insertOrder(Order order);

}
import com.yscyber.mybatis.five.pojo.User;

public interface UserRepo {

    User getUserById(Integer id);

}
<?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.yscyber.mybatis.five.repo.OrderRepo">

    <resultMap id="orderResultMap" type="Order">
        <id property="id" column="order_id"/>
        <result property="account" column="order_account"/>
    </resultMap>

    <select id="listOrdersByUserId" parameterType="int" resultMap="orderResultMap">
        SELECT order_id, order_account
        FROM lg_order
        WHERE order_user_id=#{userId}
    </select>

    <insert id="insertOrder" parameterType="Order">
        INSERT INTO lg_order(order_id, order_account, order_user_id)
        VALUES (#{id}, #{account}, 1)
    </insert>

</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.yscyber.mybatis.five.repo.UserRepo">

    <cache/>

    <resultMap id="userResultMap" type="User">
        <id property="id" column="user_id"/>
        <result property="nickname" column="user_nickname"/>
        <result property="phoneNumber" column="user_phone_number"/>

        <!--
            将查询出的 column="user_id" 作为 listOrdersByUserId 参数
         -->
        <collection property="orderList" ofType="Order" select="com.yscyber.mybatis.five.repo.OrderRepo.listOrdersByUserId" column="user_id">
            <id property="id" column="order_id"/>
            <result property="account" column="order_account"/>
        </collection>
    </resultMap>
    
    <select id="getUserById" parameterType="int" resultMap="userResultMap" useCache="true">
        SELECT user_id, user_nickname, user_phone_number
        FROM lg_user
        WHERE user_id=#{id}
    </select>

</mapper>
<?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>
    
    <properties resource="jdbc.properties"/>
    
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        
        <setting name="cacheEnabled" value="true"/>

        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="lazyLoadTriggerMethods" value=""/>
    </settings>

    <typeAliases>
        <package name="com.yscyber.mybatis.five.pojo"/>
    </typeAliases>
    
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
        </plugin>
    </plugins>

    <environments default="dev">
        <!-- 配置数据源 -->
        <environment id="dev">
            <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>
        <package name="com.yscyber.mybatis.five.repo"/>
    </mappers>
    
</configuration>
    @Test
    public void test1() throws IOException {
        InputStream ris = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ris);

        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserRepo userRepo = sqlSession.getMapper(UserRepo.class);
        OrderRepo orderRepo = sqlSession.getMapper(OrderRepo.class);

        User user1 = userRepo.getUserById(1); // UserRepo 查询
        System.out.println(user1);

        Order order = new Order();
        order.setId(4);
        order.setAccount(new BigDecimal(23.00));
        orderRepo.insertOrder(order); // OrderRepo 更新
        sqlSession.commit(); // 这里的 commit 方法无法清空 UserRepo 的二级缓存,只能清空该 SqlSession 对象的一级缓存以及 OrderRepo 的二级缓存(此处 OrderRepo 也没有开启二级缓存)

        User user2 = userRepo.getUserById(1);
        System.out.println(user2);

        sqlSession.close();
    }

最后,因为 UserRepo 的二级缓存,读取出的“订单”不是更新后的“订单”,还是第一次查询出的“订单”,造成“脏读”。

上一篇 下一篇

猜你喜欢

热点阅读