天天看點

MyBatis緩存不一緻問題測試

資料準備

首先我們建立兩張表:

CREATE TABLE `person` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',
  `name` varchar(45) NOT NULL DEFAULT '' COMMENT '姓名',
  `gender` tinyint(1) NOT NULL DEFAULT '0' COMMENT '性别(1:男,2:女)',
  `company_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '公司id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '人員資訊表';
           
CREATE TABLE `company` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `name` varchar(45) NOT NULL DEFAULT '' COMMENT '公司名稱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='公司資訊表';
           

接下來我們往兩張表中插入一些資料:

INSERT INTO person (age, name, gender, company_id) VALUES (1, 'tia', 1, 1);
INSERT INTO company (name) VALUES ('A');
           

實體和DAO準備

環境的搭建這裡不過多贅述了,相信大家都能很輕松的搞定。

實體
/**
 * 實體基類
 */
@Data
public class BaseModel implements Serializable {

    public static final long serialVersionUID = 1L;

    private Date modified;

    private Date created;

    private Long id;

}
           
@Data
@ToString(callSuper = true)
public class Person extends BaseModel {

    /**
     * 姓名
     */
    private String name;

    /**
     * 年齡
     */
    private Integer age;

    /**
     * 性别
     */
    private Byte gender;

    /**
     * 公司id
     */
    private Long companyId;

    /**
     * 公司資訊
     */
    private Company company;

}
           
@Data
@ToString(callSuper = true)
public class Company extends BaseModel {

    /**
     * 姓名
     */
    private String name;

}
           
DAO
/**
 * @author 0xZzzz
 */
@CacheNamespace
public interface PersonDAO {

    /**
     * 主鍵查詢
     *
     * @param id pk
     * @return person
     */
    @Select("select * from person where id = #{id}")
    Person getById(Long id);

    /**
     * 關聯company
     *
     * @param id pk
     * @return person join company
     */
    @Select("select t1.*, t2.id as company_id, t2.id as \"company.id\", t2.name as \"company.name\" "
        + "from person t1 left join company t2 on t1.company_id = t2.id "
        + "where t1.id = #{id}")
    Person joinCompanyById(Long id);

    /**
     * 更新
     *
     * @param person row
     * @return effect rows
     */
    @Update("update person set name = #{name} where id = #{id}")
    int update(Person person);

}
           
@CacheNamespace
public interface CompanyDAO {

    /**
     * 主鍵查詢
     *
     * @param id pk
     * @return row
     */
    @Select("select * from company where id = #{id}")
    Company getById(Long id);

    /**
     * 更新
     *
     * @param company row
     * @return effect rows
     */
    @Update("update company set name = #{name} where id = #{id}")
    int update(Company company);

}
           

一級緩存測試

測試思路

因為一級緩存是SqlSession次元的緩存,是以我們建立兩個SqlSession對象,都讀取資料庫的同一條記錄,然後用其中一個SqlSession對象改變這條記錄的值,然後用另一個SqlSession對象再次讀取這條記錄,看是否會讀取到過期的值。

代碼實作
public String level1() {
    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
    PersonDAO personDAO1 = sqlSession1.getMapper(PersonDAO.class);
    Person person1 = personDAO1.getById(1L);
    System.err.println("sqlSession1 query:" + person1);

    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
    PersonDAO personDAO2 = sqlSession2.getMapper(PersonDAO.class);
    System.err.println("sqlSession2 query:" + personDAO2.getById(1L));

    person1.setName("lia");
    personDAO1.update(person1);
    System.err.println("sqlSession1 update:" + personDAO1.getById(1L));

    System.err.println("sqlSession2 query: " + personDAO2.getById(1L));

    return "OK";
}
           

解釋一下方法的流程:

  1. 首先我們建立SqlSession對象sqlSession1,并用其讀取person表中id為1的記錄并列印
  2. 然後我們建立另一個SqlSession對象sqlSession2,同樣用其讀取person表中id為1的記錄并列印
  3. 接下來我們用sqlSession1将這條記錄的name字段更新為lia,再次查詢并列印
  4. 最後我們用sqlSession2再次查詢這條記錄并檢視結果
結果輸出
sqlSession1 query:Person(super=BaseModel(modified=null, created=null, id=1), name=tia, age=22, gender=1, companyId=1, company=null)
sqlSession2 query:Person(super=BaseModel(modified=null, created=null, id=1), name=tia, age=22, gender=1, companyId=1, company=null)
sqlSession1 update:Person(super=BaseModel(modified=null, created=null, id=1), name=lia, age=22, gender=1, companyId=1, company=null)
sqlSession2 query: Person(super=BaseModel(modified=null, created=null, id=1), name=tia, age=22, gender=1, companyId=1, company=null)
           

我們看到,在sqlSession1将name字段修改lia之後,sqlSession2再次讀取這條記錄讀到的還是tia,是以在使用Mybatis一級緩存的時候确實會存在緩存不一緻的問題,使用時我們需要注意這個點。

二級緩存測試

測試思路

因為MyBatis二級緩存是namespace次元的緩存,是以我們把針對同一張表的CRUD操作定義在兩個不同的namespace也就是DAO中,用其中的一個DAO更新掉這張表一條記錄,然後用另一個DAO再次查詢這條記錄,看是否會讀取到過期的值。

代碼實作
public String level2() {
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    CompanyDAO companyDAO = sqlSession.getMapper(CompanyDAO.class);
    Company company = companyDAO.getById(1L);
    sqlSession.commit();
    System.err.println("CompanyDAO query company:" + company);

    PersonDAO personDAO = sqlSession.getMapper(PersonDAO.class);
    Person person = personDAO.joinCompanyById(1L);
    sqlSession.commit();
    System.err.println("PersonDAO query person join company:" + person);

    company.setName("B");
    companyDAO.update(company);
    sqlSession.commit();
    System.err.println("CompanyDAO update company:" + companyDAO.getById(1L));

    System.err.println("PersonDAO query person join company:" + personDAO.joinCompanyById(1L));
    sqlSession.commit();

    return "OK";
}
           

解釋一下方法的流程:

  1. 首先我們用CompanyDAO讀取company表中id為1的記錄并列印
  2. 然後我們用PersonDAO讀取person表中id為1的記錄,并關聯company表中id為1的記錄,讀取後列印
  3. 接下來我們用CompanyDAO将company表中id為1的記錄的name字段更新為B,再次查詢并列印
  4. 最後我們用PersonDAO再次查詢關聯後的記錄并檢視結果
結果輸出
CompanyDAO query company:Company(super=BaseModel(modified=null, created=null, id=1), name=A)
PersonDAO query person join company:Person(super=BaseModel(modified=null, created=null, id=1), name=lia, age=22, gender=1, companyId=1, company=Company(super=BaseModel(modified=null, created=null, id=1), name=A))
CompanyDAO update company:Company(super=BaseModel(modified=null, created=null, id=1), name=B)
PersonDAO query person join company:Person(super=BaseModel(modified=null, created=null, id=1), name=lia, age=22, gender=1, companyId=1, company=Company(super=BaseModel(modified=null, created=null, id=1), name=A))
           

我們看到在CompanyDAO将company表中id為1的記錄的name字段修改為B後,PersonDAO再次查詢關聯的記錄後,Company的name還是A,是以在使用Mybatis二級緩存的時候也确實會存在緩存不一緻的問題,使用時我們需要注意這個點。

繼續閱讀