1. 原理
二级缓存原理:
Mybatis的二级缓存是指mapper映射文件。二级缓存是多个sqlSession共享的,其作用域是mapper下的同一个namespace。
在不同的sqlSession中,相同的namespace下,相同的查询sql语句并且参数也相同的情况下,会命中二级缓存。如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存。
2. 同一张表的 xml 和 接口 文件copy 到自己的目录下,有自己的增加和修改方法,这样分包很容易 造成脏读,获取到的数据不一样
下面举例说明:
先搭建一个简单的springboot + mybatis 项目,并开启mybatis 的二级缓存
#开启MyBatis的二级缓存
mybatis.configuration.cache-enabled=true
将订单模块和销售变更模块放到 copy相同的 订单ordermapper xml 文件到各自模块:
订单模块:
package com.example.demo.mapper;
import com.example.demo.entity.SOrder;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository("orderSOrderMapper")
public interface OrderSOrderMapper {
int deleteByPrimaryKey(Integer id);
int insert(SOrder record);
int insertSelective(SOrder record);
int updateByExampleSelective(SOrder record);
SOrder selectByPrimaryKey(@Param("id") Integer id);
int updateByPrimaryKey(SOrder record);
}
<?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.example.demo.mapper.OrderSOrderMapper">
<!-- 开启 这个 namespace 的 二级缓存 -->
<cache/>
<resultMap id="BaseResultMap" type="com.example.demo.entity.SOrder" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="description" property="description" jdbcType="VARCHAR" />
<result column="created_GUID" property="createdGuid" jdbcType="VARCHAR" />
<result column="updated_GUID" property="updatedGuid" jdbcType="VARCHAR" />
<result column="created_Time" property="createdTime" jdbcType="TIMESTAMP" />
<result column="updated_Time" property="updatedTime" jdbcType="TIMESTAMP" />
</resultMap>
<sql id="Base_Column_List" >
id, name, description, created_GUID, updated_GUID, created_Time, updated_Time
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
*
from s_order
where id = #{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from s_order
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.example.demo.entity.SOrder" useGeneratedKeys="true" keyProperty="id" >
insert into s_order (name, description, created_GUID,
updated_GUID, created_Time, updated_Time
)
values (#{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{createdGuid,jdbcType=VARCHAR},
#{updatedGuid,jdbcType=VARCHAR}, #{createdTime,jdbcType=TIMESTAMP}, #{updatedTime,jdbcType=TIMESTAMP}
)
</insert>
<insert id="insertSelective" parameterType="com.example.demo.entity.SOrder" useGeneratedKeys="true" keyProperty="id" >
insert into s_order
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="name != null" >
name,
</if>
<if test="description != null" >
description,
</if>
<if test="createdGuid != null" >
created_GUID,
</if>
<if test="updatedGuid != null" >
updated_GUID,
</if>
<if test="createdTime != null" >
created_Time,
</if>
<if test="updatedTime != null" >
updated_Time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="name != null" >
#{name,jdbcType=VARCHAR},
</if>
<if test="description != null" >
#{description,jdbcType=VARCHAR},
</if>
<if test="createdGuid != null" >
#{createdGuid,jdbcType=VARCHAR},
</if>
<if test="updatedGuid != null" >
#{updatedGuid,jdbcType=VARCHAR},
</if>
<if test="createdTime != null" >
#{createdTime,jdbcType=TIMESTAMP},
</if>
<if test="updatedTime != null" >
#{updatedTime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
<update id="updateByExampleSelective" >
update s_order
<set >
<if test="id != null" >
id = #{id,jdbcType=INTEGER},
</if>
<if test="name != null" >
name = #{name,jdbcType=VARCHAR},
</if>
<if test="description != null" >
description = #{description,jdbcType=VARCHAR},
</if>
<if test="createdGuid != null" >
created_GUID = #{createdGuid,jdbcType=VARCHAR},
</if>
<if test="updatedGuid != null" >
updated_GUID = #{updatedGuid,jdbcType=VARCHAR},
</if>
<if test="createdTime != null" >
created_Time = #{createdTime,jdbcType=TIMESTAMP},
</if>
<if test="updatedTime != null" >
updated_Time = #{updatedTime,jdbcType=TIMESTAMP},
</if>
</set>
<if test="_parameter != null" >
</if>
</update>
<update id="updateByPrimaryKey" parameterType="com.example.demo.entity.SOrder" >
update s_order
set name = #{name,jdbcType=VARCHAR},
description = #{description,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
销售变更模块:
package com.example.demo.mapper;
import com.example.demo.entity.SOrder;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository("salechangeSOrderMapper")
public interface SaleChangeSOrderMapper {
int deleteByPrimaryKey(Integer id);
int insert(SOrder record);
int insertSelective(SOrder record);
int updateByExampleSelective(SOrder record);
SOrder selectByPrimaryKey(@Param("id") Integer id);
int updateByPrimaryKey(SOrder record);
}
<?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.example.demo.mapper.SaleChangeSOrderMapper" >
<!-- 开启 这个 namespace 的 二级缓存 -->
<cache/>
<resultMap id="BaseResultMap" type="com.example.demo.entity.SOrder" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="description" property="description" jdbcType="VARCHAR" />
<result column="created_GUID" property="createdGuid" jdbcType="VARCHAR" />
<result column="updated_GUID" property="updatedGuid" jdbcType="VARCHAR" />
<result column="created_Time" property="createdTime" jdbcType="TIMESTAMP" />
<result column="updated_Time" property="updatedTime" jdbcType="TIMESTAMP" />
</resultMap>
<sql id="Base_Column_List" >
id, name, description, created_GUID, updated_GUID, created_Time, updated_Time
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from s_order
where id = #{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from s_order
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.example.demo.entity.SOrder" useGeneratedKeys="true" keyProperty="id" >
insert into s_order (name, description, created_GUID,
updated_GUID, created_Time, updated_Time
)
values (#{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{createdGuid,jdbcType=VARCHAR},
#{updatedGuid,jdbcType=VARCHAR}, #{createdTime,jdbcType=TIMESTAMP}, #{updatedTime,jdbcType=TIMESTAMP}
)
</insert>
<insert id="insertSelective" parameterType="com.example.demo.entity.SOrder" useGeneratedKeys="true" keyProperty="id" >
insert into s_order
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="name != null" >
name,
</if>
<if test="description != null" >
description,
</if>
<if test="createdGuid != null" >
created_GUID,
</if>
<if test="updatedGuid != null" >
updated_GUID,
</if>
<if test="createdTime != null" >
created_Time,
</if>
<if test="updatedTime != null" >
updated_Time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="name != null" >
#{name,jdbcType=VARCHAR},
</if>
<if test="description != null" >
#{description,jdbcType=VARCHAR},
</if>
<if test="createdGuid != null" >
#{createdGuid,jdbcType=VARCHAR},
</if>
<if test="updatedGuid != null" >
#{updatedGuid,jdbcType=VARCHAR},
</if>
<if test="createdTime != null" >
#{createdTime,jdbcType=TIMESTAMP},
</if>
<if test="updatedTime != null" >
#{updatedTime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
<update id="updateByExampleSelective" >
update s_order
<set >
<if test="id != null" >
id = #{id,jdbcType=INTEGER},
</if>
<if test="name != null" >
name = #{name,jdbcType=VARCHAR},
</if>
<if test="description != null" >
description = #{description,jdbcType=VARCHAR},
</if>
<if test="createdGuid != null" >
created_GUID = #{createdGuid,jdbcType=VARCHAR},
</if>
<if test="updatedGuid != null" >
updated_GUID = #{updatedGuid,jdbcType=VARCHAR},
</if>
<if test="createdTime != null" >
created_Time = #{createdTime,jdbcType=TIMESTAMP},
</if>
<if test="updatedTime != null" >
updated_Time = #{updatedTime,jdbcType=TIMESTAMP},
</if>
</set>
<if test="_parameter != null" >
</if>
</update>
<update id="updateByPrimaryKey" parameterType="com.example.demo.entity.SOrder" >
update s_order
set name = #{name,jdbcType=VARCHAR},
description = #{description,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
目录结果如下:
测试controller:
package com.example.demo.controller;
import com.example.demo.entity.Region;
import com.example.demo.entity.SOrder;
import com.example.demo.service.order.OrderSOrderService;
import com.example.demo.service.salechange.OrderSaleChangeService;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.List;
/**
* @program: springboot_01
* @description:
* @author: guoyiguang
* @create: 2021-03-07 13:14
**/
@Controller
@RequestMapping("/order")
public class SOrderController {
@Resource
OrderSOrderService orderSOrderService;
@Resource
OrderSaleChangeService orderSaleChangeService;
@RequestMapping("/getOneFromOrderNameSpace")
@ResponseBody
public SOrder getOneFromOrderNameSpace(Integer id){
return orderSOrderService.selectByPrimaryKey(id);
}
@RequestMapping(value = "/updateOneToOrderNameSpace", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@ResponseBody
public int updateOneToOrderNameSpace(@RequestBody SOrder sOrder){
return orderSOrderService.updateByPrimaryKey(sOrder);
}
@RequestMapping("/getOneFromSaleChangeNameSpace")
@ResponseBody
public SOrder getOneFromSaleChangeNameSpace(Integer id){
return orderSaleChangeService.selectByPrimaryKey(id);
}
@RequestMapping(value = "/updateOneToSaleChangeNameSpace", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@ResponseBody
public int updateOneToSaleChangeNameSpace(@RequestBody SOrder sOrder){
return orderSaleChangeService.updateByPrimaryKey(sOrder);
}
}
订单模块开始修改数据:
订单模块查看数据:
但是这个时候销售模块拿id 为16的数据:
说明销售模块没有感知到 订单模块数据修改,因为二级缓存是以 namespace 为粒度的(也是个map),这个namespace 涉及到的增加,删除修改,都会触发清掉本namespace先关的缓存,订单模块的namespace 清空掉自己缓存可以拿到最新数据,但是这个update的操作,销售模块无法感知到,所以他还是从自己的缓存里拿的数据,所以分包操作同一张表可能会产生脏读问题