mybatis缓存机制

mybatis正如大多数持久层框架一样,缓存提供一级缓存和二级缓存。如hibernate

先说说mybatis一级缓存

用过hibernate的人应该了解,hibernate一级缓存的作用域是session。而session的生命周期很短,所以hibernate的一级缓存生命期也很短。

mybatis的一级缓存与hibernate一样,基于PerpetualCache 的 HashMap本地缓存,作用域也是session,一旦session close或则flush后,其中的缓存数据也将清空。

具体举例如下:

AboutDto.Java

[java]  view plain  copy
  1. public class AboutDto{  
  2.     private String areacode;  
  3.   
  4.     private String areaname;  
  5.   
  6.     private String parentcode;  
  7.   
  8.     private String memo;  
  9.   
  10.       
  11.     public AboutDto()  
  12.     {}  
  13.       
  14.     public AboutDto(String areacode, String areaname, String parentcode) {  
  15.         this.areacode = areacode;  
  16.         this.areaname = areaname;  
  17.         this.parentcode = parentcode;  
  18.     }  
  19.   
  20.     public String getAreacode() {  
  21.         return areacode;  
  22.     }  
  23.   
  24.     public void setAreacode(String areacode) {  
  25.         this.areacode = areacode == null ? null : areacode.trim();  
  26.     }  
  27.   
  28.     public String getAreaname() {  
  29.         return areaname;  
  30.     }  
  31.   
  32.     public void setAreaname(String areaname) {  
  33.         this.areaname = areaname == null ? null : areaname.trim();  
  34.     }  
  35.   
  36.     public String getParentcode() {  
  37.         return parentcode;  
  38.     }  
  39.   
  40.     public void setParentcode(String parentcode) {  
  41.         this.parentcode = parentcode == null ? null : parentcode.trim();  
  42.     }  
  43.   
  44.     public String getMemo() {  
  45.         return memo;  
  46.     }  
  47.   
  48.     public void setMemo(String memo) {  
  49.         this.memo = memo == null ? null : memo.trim();  
  50.     }  

AboutDtoMapper.java

[java]  view plain  copy
  1. public interface AboutDtoMapper {  
  2.   
  3.     AboutDto selectByPrimaryKey(@Param("areacode")String areacode);  
  4. }  
在这里,我只定义了一个根据主键查询的接口方法

AboutDtoMapper.xml

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  3. <mapper namespace="test.dao.AboutDtoMapper">  
  4.   <resultMap id="BaseResultMap" type="test.model.AboutDto">  
  5.     <id column="AREACODE" jdbcType="VARCHAR" property="areacode" />  
  6.     <result column="AREANAME" jdbcType="VARCHAR" property="areaname" />  
  7.     <result column="PARENTCODE" jdbcType="VARCHAR" property="parentcode" />  
  8.     <result column="MEMO" jdbcType="VARCHAR" property="memo" />  
  9.   </resultMap>  
  10.   <sql id="Base_Column_List">  
  11.     AREACODE, AREANAME, PARENTCODE, MEMO  
  12.   </sql>  
  13.   <select id="selectByPrimaryKey" parameterType="map" resultMap="BaseResultMap" >  
  14.     select * from SYS_AREAINFO  
  15.     where AREACODE = #{areacode}  
  16.   </select>  
  17. </mapper>  
测试

[java]  view plain  copy
  1. public static void main(String[] args)   
  2.     {  
  3.         AboutDtoMapper mapper = session.getMapper(AboutDtoMapper.class);  
  4.         AboutDto dto = mapper.selectByPrimaryKey("pqgl01");  
  5.         System.out.println(dto.getAreaname());  
  6.         <span style="color:#FF0000;">session.commit();//1</span>  
  7.         AboutDto dto2 = mapper.selectByPrimaryKey("pqgl01");  
  8.         System.out.println(dto2.getAreaname());  
  9.     }  

测试结果

[html]  view plain  copy
  1. DEBUG - Created connection 7791465.  
  2. DEBUG - ooo Connection Opened  
  3. DEBUG - ==>  Executing: select * from SYS_AREAINFO where AREACODE = ?   
  4. DEBUG - ==> Parameters: pqgl01(String)  
  5. DEBUG - <==    Columns: AREACODE, AREANAME, PARENTCODE, MEMO  
  6. DEBUG - <==        Row: pqgl01, 龙泉驿, pqgl20, null  
  7. 龙泉驿  
  8. DEBUG - ==>  Executing: select * from SYS_AREAINFO where AREACODE = ?   
  9. DEBUG - ==> Parameters: pqgl01(String)  
  10. DEBUG - <==    Columns: AREACODE, AREANAME, PARENTCODE, MEMO  
  11. DEBUG - <==        Row: pqgl01, 龙泉驿, pqgl20, null  
  12. 龙泉驿  
mybatis默认一级缓存是开启的,所以这里就不贴mybatis-config.xml中代码了。

上面测试中,当session提交后,控制台打印显示,程序执行了两次查询数据库的操作。当我们屏蔽掉1处commit代码,再看控制台结果

[html]  view plain  copy
  1. DEBUG - PooledDataSource forcefully closed/removed all connections.  
  2. DEBUG - PooledDataSource forcefully closed/removed all connections.  
  3. DEBUG - PooledDataSource forcefully closed/removed all connections.  
  4. DEBUG - PooledDataSource forcefully closed/removed all connections.  
  5. DEBUG - Created connection 7791465.  
  6. DEBUG - ooo Connection Opened  
  7. DEBUG - ==>  Executing: select * from SYS_AREAINFO where AREACODE = ?   
  8. DEBUG - ==> Parameters: pqgl01(String)  
  9. DEBUG - <==    Columns: AREACODE, AREANAME, PARENTCODE, MEMO  
  10. DEBUG - <==        Row: pqgl01, 龙泉驿, pqgl20, null  
  11. 龙泉驿  
  12. 龙泉驿  
这里只执行了一次查询数据库操作。
由此可见,mybatis的一级缓存作用域只存在于session。

下面说说mybatis的二级缓存。

mybatis的二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。

我们先测试不开启mybatis的二级缓存。

测试代码

[java]  view plain  copy
  1. public static void main(String args[])  
  2.     {  
  3.         session1 = sqlSessionFactory.openSession();  
  4.                 session2 = sqlSessionFactory.openSession();  
  5.         AboutDtoMapper mapper1 = session1.getMapper(AboutDtoMapper.class);  
  6.         AboutDtoMapper mapper2 = session2.getMapper(AboutDtoMapper.class);  
  7.       
  8.         AboutDto dto = mapper1.selectByPrimaryKey("pqgl01");  
  9.         System.out.println(dto.getAreaname());  
  10.         session1.commit();  
  11.         AboutDto dto2 = mapper2.selectByPrimaryKey("pqgl01");  
  12.         System.out.println(dto2.getAreaname());  
  13.         session2.commit();  
  14.     }  
这里我们定义了两个session,分别查询主键为pggl01对象。控制台输出结果

[html]  view plain  copy
  1. DEBUG - Created connection 21021313.  
  2. DEBUG - ooo Connection Opened  
  3. DEBUG - ==>  Executing: select * from SYS_AREAINFO where AREACODE = ?   
  4. DEBUG - ==> Parameters: pqgl01(String)  
  5. DEBUG - <==    Columns: AREACODE, AREANAME, PARENTCODE, MEMO  
  6. DEBUG - <==        Row: pqgl01, 龙泉驿, pqgl20, null  
  7. 龙泉驿  
  8. DEBUG - ==>  Executing: select * from SYS_AREAINFO where AREACODE = ?   
  9. DEBUG - ==> Parameters: pqgl01(String)  
  10. DEBUG - <==    Columns: AREACODE, AREANAME, PARENTCODE, MEMO  
  11. DEBUG - <==        Row: pqgl01, 龙泉驿, pqgl20, null  
  12. 龙泉驿  
这里mybatis与数据库交互了两次,说明mybatis目前尚未开启二级缓存。需要我们手动开启。

在这里我用的Ehcache。需要注意的是,这里需要加入几个jar包

slf4j-api-1.6.1.jar

mybatis-ehcache-1.0.3.jar

ehcache-core-2.6.9.jar

配置AboutDtoMapper.xml

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  3. <mapper namespace="test.dao.AboutDtoMapper">  
  4.     <span style="color:#FF0000;"><cache type="org.mybatis.caches.ehcache.EhcacheCache"/></span> <!--这里指明缓存类型-->  
  5.   <resultMap id="BaseResultMap" type="test.model.AboutDto">  
  6.     <id column="AREACODE" jdbcType="VARCHAR" property="areacode" />  
  7.     <result column="AREANAME" jdbcType="VARCHAR" property="areaname" />  
  8.     <result column="PARENTCODE" jdbcType="VARCHAR" property="parentcode" />  
  9.     <result column="MEMO" jdbcType="VARCHAR" property="memo" />  
  10.   </resultMap>  
  11.   <sql id="Base_Column_List">  
  12.     AREACODE, AREANAME, PARENTCODE, MEMO  
  13.   </sql>  
  14.   <select id="selectByPrimaryKey" parameterType="map" resultMap="BaseResultMap" >  
  15.     select * from SYS_AREAINFO  
  16.     where AREACODE = #{areacode}  
  17.   </select>  
  18. </mapper>  
ehcache.xml

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true">  
  3.     <diskStore path="d:\test" />  
  4.     <defaultCache   
  5.             maxElementsInMemory="10000"   
  6.             maxElementsOnDisk="100000000"   
  7.             eternal="false"   
  8.             overflowToDisk="true"   
  9.             timeToIdleSeconds="300"   
  10.             timeToLiveSeconds="600"   
  11.             diskPersistent="false"   
  12.             diskExpiryThreadIntervalSeconds="120"   
  13.             memoryStoreEvictionPolicy="LRU"   
  14.             />   
  15.               
  16.     <!--  
  17.     name:Cache的唯一标识  
  18.     maxElementsInMemory:内存中最大缓存对象数  
  19.     maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大  
  20.     eternal:Element是否永久有效,一但设置了,timeout将不起作用  
  21.     overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中  
  22.     timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大  
  23.     timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大   
  24.     diskPersistent:是否缓存虚拟机重启期数据  
  25.     diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒  
  26.     diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区  
  27.     memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)   
  28.     -->  
  29.   
  30. </ehcache>  

特别需要说明的是,当开启mybatis的二级缓存时,Mapper的实体对象必须实现可序列化接口,否则会异常

[html]  view plain  copy
  1. Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:   
  2. ### Error committing transaction.  Cause: org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException: test.model.AboutDto  
  3. ### Cause: org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException: test.model.AboutDto  
  4.     at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:8)  
  5.     at org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:127)  
  6.     at org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:119)  
  7.     at Test.main(Test.java:60)  
  8. Caused by: org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException: test.model.AboutDto  
  9.     at org.apache.ibatis.cache.decorators.SerializedCache.serialize(SerializedCache.java:67)  
  10.     at org.apache.ibatis.cache.decorators.SerializedCache.putObject(SerializedCache.java:27)  
  11.     at org.apache.ibatis.cache.decorators.LoggingCache.putObject(LoggingCache.java:30)  
  12.     at org.apache.ibatis.cache.decorators.SynchronizedCache.putObject(SynchronizedCache.java:31)  
  13.     at org.apache.ibatis.cache.decorators.TransactionalCache$AddEntry.commit(TransactionalCache.java:96)  
  14.     at org.apache.ibatis.cache.decorators.TransactionalCache.commit(TransactionalCache.java:66)  
  15.     at org.apache.ibatis.cache.TransactionalCacheManager.commit(TransactionalCacheManager.java:22)  
  16.     at org.apache.ibatis.executor.CachingExecutor.commit(CachingExecutor.java:81)  
  17.     at org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:124)  
  18.     ... 2 more  
  19. Caused by: java.io.NotSerializableException: test.model.AboutDto  
  20.     at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)  
  21.     at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)  
  22.     at java.util.ArrayList.writeObject(ArrayList.java:570)  
  23.     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
  24.     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)  
  25.     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)  
  26.     at java.lang.reflect.Method.invoke(Method.java:597)  
  27.     at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)  
  28.     at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)  
  29.     at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)  
  30.     at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)  
  31.     at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)  
  32.     at org.apache.ibatis.cache.decorators.SerializedCache.serialize(SerializedCache.java:62)  
  33.     ... 10 more  

测试结果

[html]  view plain  copy
  1. DEBUG - ooo Connection Opened  
  2. DEBUG - Cache Hit Ratio [test.dao.AboutDtoMapper]: 0.0  
  3. DEBUG - ==>  Executing: select * from SYS_AREAINFO where AREACODE = ?   
  4. DEBUG - ==> Parameters: pqgl01(String)  
  5. DEBUG - <==    Columns: AREACODE, AREANAME, PARENTCODE, MEMO  
  6. DEBUG - <==        Row: pqgl01, 龙泉驿, pqgl20, null  
  7. 龙泉驿  
  8. DEBUG - put added 0 on heap  
  9. 龙泉驿  
  10. DEBUG - Cache Hit Ratio [test.dao.AboutDtoMapper]: 0.5  
  11. DEBUG - fault removed 0 from heap  
  12. DEBUG - fault added 0 on disk  
测试结果表明,只操作一次数据库


最后,补充一点,手动控制mybatis的一级缓存是否开启方式,在mybatis-config.xml文件中加

[html]  view plain  copy
  1. <settings>  
  2.       <setting name="cacheEnabled" value="true"/>  
  3. </settings>  

如有错误之处,请及时说明。


1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值