• mybatis二级缓存问题


    mybatis二级缓存问题

    1、mybatis缓存中的问题:

    在使用mybatis进行关联查询的时候,如果学生和老师两张表进行关联查询有一下的操作步骤:
    1.1 通过学生进行关联查询老师Mapper文件为:

      <select id="queryStudentAndTeacher" resultMap="BaseResultMap" useCache="true" >
        SELECT
            s.id,
            s.`name`,
            s.gender,
            s.major,
            s.grade,
            s.supervisor_id ,
            t.id as tea_id,
            t.`name` as tea_name,
            t.gender as tea_gender,
            t.research_area  as tea_research_area,
            t.title  as tea_title
        FROM
            student s
        LEFT JOIN teacher t ON s.supervisor_id = t.id
      </select>
    

    1.2 查询完成后我更新了老师

      <update id="updateByExample" parameterType="map" >
        update teacher
        set id = #{record.id,jdbcType=INTEGER},
          name = #{record.name,jdbcType=VARCHAR},
          gender = #{record.gender,jdbcType=VARCHAR},
          research_area = #{record.researchArea,jdbcType=VARCHAR},
          title = #{record.title,jdbcType=VARCHAR}
        <if test="_parameter != null" >
          <include refid="Update_By_Example_Where_Clause" />
        </if>
      </update>
    

    1.3 再次查询:
    我们发现更新了数据,但是查询出的结果却没有变化

        @Test
        public void TestSecondCache() throws IOException{
            org.apache.ibatis.logging.LogFactory.useStdOutLogging();
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                    .build(reader);
            reader.close();
            SqlSession session = null;
            session = sqlSessionFactory.openSession();
            StudentMapper studentMapper = (StudentMapper) session.getMapper(StudentMapper.class);
            Map<String, Object> params = new HashMap<String,Object>();
            /**
             * 模拟下面的情况
             * 1. 执行StudentMapper中的"queryStudentAndTeacher" 操作,此时会将查询到的结果放置到StudentMapper对应的二级缓存StudentCache中;
             * 2. 执行teacherMapper中对teacher的更新操作(update、delete、insert)后,teacher的数据更新;
             * 3. 再执行1完全相同的查询,这时候会直接从StudentMapper二级缓存StudentCache中取值,将StudentMapper中的值直接返回;
             * */
            List<Student> students = studentMapper.queryStudentAndTeacher(params);
            for (Student student : students) {
                System.out.println(student.getId() + student.getTeacher().getName());
            }
            System.out.println(students.size());
            
            //更新老师
            SqlSession session2 = sqlSessionFactory.openSession();
            TeacherMapper teacherMapper = session2.getMapper(TeacherMapper.class);
            Teacher teacher = new Teacher();
            teacher.setName("刘老师");
            teacher.setId(1);
            TeacherExample teacherExample = new TeacherExample();
            teacherExample.createCriteria().andIdEqualTo(1);
            teacherMapper.updateByExample(teacher, teacherExample);
            session2.commit();
            
            //第二次查询,查看是否从缓存中读取
            List<Student> students2 = studentMapper.queryStudentAndTeacher(params);
            for (Student student : students2) {
                System.out.println(student.getId() + student.getTeacher().getName());
            }
            System.out.println(students2.size());
            checkCacheStatus(session);  
        }
    

    enter description here
    enter description here

    2、 如果遇到这种情况,应该如何解决?

    我们可以上网,网上提供的二级缓存插件来解决该问题:

    简介

    MyBatis Enhanced Cache, Control your Caches precisely!

    该插件主要是为了弥补MyBatis二级缓存控制上的不足,提高二级缓存Cache和数据库数据的同步性和一致性,处理各个Cache之间的关联关系。
    该插件可以精确地管理MyBatis的二级缓存,实现对MyBatis二级缓存细粒度的控制。

    当执行过对数据库表的更新操作(update、delete、insert)时,可以指定清除由特定的StatementId表示的查询语句产生的缓存。

    应用场景:

    当前的MyBaits对于缓存的比较粗糙,一般为一个Mapper配置一个Cache缓存,或者多个Mapper共用一个缓存。
    而对缓存的维护都是独立的,缓存之间不会相互影响,指定的Mapper中的语句只会影响到该Mapper对应的Cache缓存。
    有时候希望当执行某些更新操作时,能够刷新或者清空特定的查询语句产生的缓存,以避免数据不一致的情况。
    ###举例


    现有AMapper.xml 中定义了对数据库表ATable的CRUD操作,BMapper定义了对数据库表BTable的CRUD操作;

    假设MyBatis的二级缓存开启,并且AMapper中使用了二级缓存,AMapper对应的二级缓存为ACache

    除此之外,AMapper中还定义了一个跟BTable有关的查询语句,类似如下所述:

    <select id="selectATableWidtJoin" resultMap="BaseResultMap" useCache="true">
          select * from ATable left join BTable on ....
    </select>
    
    1. 执行了selectATableWithJoin操作,该查询的缓存会被放置到对应的二级缓存ACache中,
      当再次执行相同的查询,则直接从二级缓存ACache中;

    2. 如果某个时候,BMapper中执行了对BTable的update操作(update 、delete、insert),BTable 的数据已经更新,

    3. 再次执行selectATableWithJoin操作,该查询的结果直接从二级缓存ACache中取,这时候就造成了**数据不一致**的情况

    针对上述的问题,需要解决

    当BMapper执行对BTable的update操作时,指定刷新 ACache中的 selectATableWithJoin语句产生的缓存

    该插件正是解决上述的这一问题!

    使用方法:

    使用此插件非常简单:

    1. 在mybatisConfig.xml 文件中定义plugin节点如下:

      <plugins>
           <plugin interceptor="org.luanlouis.mybatis.plugin.cache.EnhancedCachingExecutor">
              <property name="dependency" value="dependencys.xml"/>
              <property name="cacheEnabled" value="true"/>
           </plugin>
      </plugins>
    
    1. dependencys.xml配置文件,配置StatemntId依赖关系

    <?xml version="1.0" encoding="UTF-8"?>
    <dependencies>
       <statements>
           <statement id="com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey">
              <observer id="com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments" />
           </statement>
       </statements>
    </dependencies>
    
    • <statement>节点配置更新语句和查询语句的依赖关系,如果id表示的更新语句执行了,会清空由配置的id表示的查询语句生成的缓存。

    • <observer>节点表示当父节点 id表示的更新语句执行后,应该清除此语句所产生的缓存

  • 相关阅读:
    java基础
    C++菜鸟启动之旅--vc6.0使用教程详解
    第8章 Linux磁盘与文件系统管理
    IO(四)----对象的序列化
    IO(三)----序列流
    IO(二)----字符流
    IO(一)----字节流
    File类
    枚举类
    自动装箱和自动拆箱
  • 原文地址:https://www.cnblogs.com/babyhhcsy/p/4512431.html
Copyright © 2020-2023  润新知