AAA spring 集成mybatis使用二级缓存 (下篇)

来源:互联网 发布:java测试 博客园 编辑:程序博客网 时间:2024/05/23 01:31

先看《spring 集成mybatis》和《Mybatis一级缓存》,在此基础上进行修改。

集成了spring之后,似乎没有办法直接使用一级缓存了,但是二级缓存还是可以的。


当你第一次执行的时候Cache Hit Ratio 0

第二次执行的时候就 Cache Hit Ratio  0.5

使用二级缓存只需要更改;两处地方

1 avaBean序列化,

2 映射xml文件里面添加<cache/>

一、实体类

public class User implements Serializable {      /**      *       */      private static final long serialVersionUID = -8081225022334946487L;      private int id;      private String name;      private Date birthday;      private double salary;      public int getId() {          return id;      }      public void setId(int id) {          this.id = id;      }      public String getName() {          return name;      }      public void setName(String name) {          this.name = name;      }      public Date getBirthday() {          return birthday;      }      public void setBirthday(Date birthday) {          this.birthday = birthday;      }      public double getSalary() {          return salary;      }      public void setSalary(double salary) {          this.salary = salary;      }      @Override      public String toString() {          return "User [id=" + id + ", name=" + name + ", birthday=" + birthday                  + ", salary=" + salary + "]";      }              }  


同样要实现Serializable接口。


二、userMapper.xml


<?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.mapper.UserMapper">  
    <cache/>  
      
    <resultMap type="User" id="userResult">  
        <result column="user_id" property="id"/>  
        <result column="user_name" property="name"/>  
        <result column="user_birthday" property="birthday"/>  
        <result column="user_salary" property="salary"/>  
    </resultMap>  
      
    <!-- 取得插入数据后的 id -->  
    <insert id="save" keyColumn="user_id" keyProperty="id" useGeneratedKeys="true">  
        insert into s_user(user_name,user_birthday,user_salary)  
        values(#{name},#{birthday},#{salary})  
    </insert>  
      
    <update id="update">  
        update s_user  
        set user_name = #{name},  
        user_birthday = #{birthday},  
        user_salary = #{salary}  
        where user_id = #{id}  
    </update>  
      
    <delete id="delete">  
        delete from s_user  
        where user_id = #{id}  
    </delete>  
      
    <select id="findById" resultMap="userResult">  
        select *  
        from s_user  
        where user_id = #{id}  
    </select>  
      
    <select id="findAll" resultMap="userResult">  
        select *   
        from s_user  
    </select>  
</mapper>  


同样加上<cache/>就可以了。


三、测试方法

@Test      public void findAllWithCache1() {          User user = userMapper.findById(1);          System.out.println(user);                    user = userMapper.findById(1);          System.out.println(user);      }  


执行日志:

2015-05-09 18:07:11,062 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Creating a new SqlSession
2015-05-09 18:07:11,078 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1425f38] was not registered for synchronization because synchronization is not active
2015-05-09 18:07:11,156 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Fetching JDBC Connection from DataSource
2015-05-09 18:07:11,156 [main] DEBUG [org.springframework.jdbc.datasource.DriverManagerDataSource] - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/mybatis]
2015-05-09 18:07:11,171 [main] DEBUG [org.mybatis.spring.transaction.SpringManagedTransaction] - JDBC Connection [com.mysql.jdbc.JDBC4Connection@a0afab] will not be managed by Spring
2015-05-09 18:07:11,171 [main] DEBUG [com.mapper.UserMapper.findById] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@a0afab]
2015-05-09 18:07:11,187 [main] DEBUG [com.mapper.UserMapper.findById] - ==>  Preparing: select * from s_user where user_id = ? 
2015-05-09 18:07:11,234 [main] DEBUG [com.mapper.UserMapper.findById] - ==> Parameters: 1(Integer)
2015-05-09 18:07:11,296 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1425f38]
2015-05-09 18:07:11,296 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Returning JDBC Connection to DataSource
User [id=1, name=marry, birthday=Sat Apr 18 00:00:00 CST 2015, salary=2000.0]
2015-05-09 18:07:11,296 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Creating a new SqlSession
2015-05-09 18:07:11,296 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1905742] was not registered for synchronization because synchronization is not active
2015-05-09 18:07:11,312 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1905742]
User [id=1, name=marry, birthday=Sat Apr 18 00:00:00 CST 2015, salary=2000.0]


可以看到,spring默认在查询完就关闭了SqlSession,所以想要用一级缓存就很难了。

然后也看到第二次查询并没有去获取JDBC连接。


三、Cache使用时的注意事项

1. 只能在【只有单表操作】的表上使用缓存

不只是要保证这个表在整个系统中只有单表操作,而且和该表有关的全部操作必须全部在一个namespace下。

2. 在可以保证查询远远大于insert,update,delete操作的情况下使用缓存

这一点不需要多说,所有人都应该清楚。记住,这一点需要保证在1的前提下才可以! 


四、避免使用二级缓存

可能会有很多人不理解这里,二级缓存带来的好处远远比不上他所隐藏的危害。

  1. 缓存是以namespace为单位的,不同namespace下的操作互不影响。

  2. insert,update,delete操作会清空所在namespace下的全部缓存。

  3. 通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的namespace

为什么避免使用二级缓存

在符合【Cache使用时的注意事项】的要求时,并没有什么危害。

其他情况就会有很多危害了。

针对一个表的某些操作不在他独立的namespace下进行。

例如在UserMapper.xml中有大多数针对user表的操作。但是在一个XXXMapper.xml中,还有针对user单表的操作。

这会导致user在两个命名空间下的数据不一致。如果在UserMapper.xml中做了刷新缓存的操作,在XXXMapper.xml中缓存仍然有效,如果有针对user的单表查询,使用缓存的结果可能会不正确。

更危险的情况是在XXXMapper.xml做了insert,update,delete操作时,会导致UserMapper.xml中的各种操作充满未知和风险。

有关这样单表的操作可能不常见。但是你也许想到了一种常见的情况。

多表操作一定不能使用缓存

为什么不能?

首先不管多表操作写到那个namespace下,都会存在某个表不在这个namespace下的情况。

例如两个表:roleuser_role,如果我想查询出某个用户的全部角色role,就一定会涉及到多表的操作。

<code class="language-xml hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">select</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"selectUserRoles"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">resultType</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"UserRoleVO"</span>></span>    select * from user_role a,role b where a.roleid = b.roleid and a.userid = #{userid}<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">select</span>></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

像上面这个查询,你会写到那个xml中呢??

不管是写到RoleMapper.xml还是UserRoleMapper.xml,或者是一个独立的XxxMapper.xml中。如果使用了二级缓存,都会导致上面这个查询结果可能不正确。

如果你正好修改了这个用户的角色,上面这个查询使用缓存的时候结果就是错的。

这点应该很容易理解。

在我看来,就以MyBatis目前的缓存方式来看是无解的。多表操作根本不能缓存。

如果你让他们都使用同一个namespace(通过<cache-ref>)来避免脏数据,那就失去了缓存的意义。

看到这里,实际上就是说,二级缓存不能用。整篇文章介绍这么多也没什么用了。



五、挽救二级缓存?

想更高效率的使用二级缓存是解决不了了。

但是解决多表操作避免脏数据还是有法解决的。解决思路就是通过拦截器判断执行的sql涉及到那些表(可以用jsqlparser解析),然后把相关表的缓存自动清空。但是这种方式对缓存的使用效率是很低的。

设计这样一个插件是相当复杂的,既然我没想着去实现,就不废话了。

最后还是建议,放弃二级缓存,在业务层使用可控制的缓存代替更好。


0 0
原创粉丝点击