我这里使用的maven来管理的依赖,pom如下:
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-coreartifactId>
<version>5.0.2.Finalversion>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-ehcacheartifactId>
<version>5.0.2.Finalversion>
dependency>
使用ehcache需要配置文件:ehcache.xml
并修改hibernate的配置
配置文件如下:
<ehcache>
<diskStore path="java.io.tmpdir/cache"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="com.teemo.entity.User"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
<cache name="sample"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/>
ehcache>
cache字段说明:
Hibernate二级缓存逻辑:
会先根据entity的region到ehcache的缓存区域查找缓存,region对应的是ehcache配置中的name字段,如果找不到,会使用entity的全类名来查找缓存,如果还找不到会使用default设置
xml配置如下:
<prop key="hibernate.cache.use_second_level_cache">trueprop>
<prop key="hibernate.cache.use_query_cache">trueprop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactoryprop>
实体类注解:
@Cache(region = "sample", usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
@Cache(region = "all", usage = CacheConcurrencyStrategy.READ_WRITE)
private Set roles = new HashSet();
}
usage提供的事务隔离级别有NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL,但是ehcache不支持TRANSACTIONAL。
二级缓存适用于以下数据:
Hibernate的二级缓存只会根据ID进行缓存,也就是使用get/load方法会将查询对象缓存,如果需要对HQL或SQL进行缓存,则需要在代码中指定setCacheable(true)
。
查询缓存所缓存的key值就是查询所使用的HQL或SQL语句,需要注意的是:查询缓存不仅要求所使用的HQL语句、SQL语句相同,甚至是要求所传入的参数也相同,Hibernate才能从缓存中查去数据。
测试代码:
/**
* service.get方法直接使用Hibernate Session.get(class, id)
*/
@Test
public void testGet() {
DynamicProperty dp1 = service.get(1L);
System.out.println(dp1);
DynamicProperty dp2 = service.get(1L);
System.out.println(dp2);
}
输出结果:
Hibernate: select dynamicpro0_.id as id1_1_0_, dynamicpro0_.author as author2_1_0_(省略)from dynamic_property dynamicpro0_ where dynamicpro0_.id=?
com.teemo.entity.DynamicProperty@5147788d
com.teemo.entity.DynamicProperty@50ab025b
从结果中可以看到,第二次查询命中缓存,没有发出SQL查询。
测试代码:
/**
* service.get方法使用Query query = getSession().createQuery(hql);
* query.list来返回结果,没有指定query的setCacheable
*/
@Test
public void testGetByProperty() {
String key = "System.Version";
DynamicProperty dp1 = service.get("dynamicPropertyKey", key);
System.out.println(dp1);
DynamicProperty dp2 = service.get("dynamicPropertyKey", key);
System.out.println(dp2);
}
输出结果:
Hibernate: select dynamicpro0_.id as id1_1_, dynamicpro0_.author as author2_1_(省略)from dynamic_property dynamicpro0_ where dynamicpro0_.property_key=?
com.teemo.entity.DynamicProperty@5e7e7944
Hibernate: select dynamicpro0_.id as id1_1_, dynamicpro0_.author as author2_1_(省略)from dynamic_property dynamicpro0_ where dynamicpro0_.property_key=?
com.teemo.entity.DynamicProperty@74542394
对于没有指定query.setCacheable(true)的查询,没有使用缓存对象
List/Set中的对象,属于查询缓存
测试代码:
@Test
public void testGetByEmail() {
String email = "[email protected]";
User user1 = userService.get("email", email);
System.out.println(user1);
User user2 = userService.get("email", email);
System.out.println(user2);
}
输出结果:
Hibernate: select user0_.id as id1_6_, user0_.create_time as create_t2_6_(省略)from user user0_ where user0_.email=?
Hibernate: select roles0_.user_id as user_id1_8_0_, roles0_.role_id as role_id2_8_0_(省略)from user_role roles0_ inner join role role1_ on roles0_.role_id=role1_.id where roles0_.user_id=?
Hibernate: select resourcepe0_.role_id as role_id4_5_0_, resourcepe0_.id as id1_5_0_, resourcepe0_.id as id1_5_1_(省略)from role_resource_permission resourcepe0_ where resourcepe0_.role_id=?
com.teemo.entity.User@1e6923ba
Hibernate: select user0_.id as id1_6_, user0_.create_time as create_t2_6_(省略)from user user0_ where user0_.email=?
com.teemo.entity.User@4cd29ffa
从结果中可以看出,在第一次查询User实体时,会出现1+N的情况,先查出User实体,在根据User实体查出关联Role,但是此时会把Role的id放到查询缓存中,对象放到二级缓存中,在第二次查询时,发现查询缓存存在这些Role id,那么就会到二级缓存中查询到响应的对象,所以第二次查询没有发出Role相关的SQL。
测试代码:
@Test
public void testGetByEmail() {
String email = "[email protected]";
User user1 = userService.get("email", email);
System.out.println(user1);
User user2 = userService.get("email", email);
System.out.println(user2);
}
输出结果:
2017-01-10 16:21:19.843 [main] DEBUG org.hibernate.cache.internal.StandardQueryCache - Checking cached query results in region: org.hibernate.cache.internal.StandardQueryCache
2017-01-10 16:21:19.843 [main] DEBUG org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - key: sql: select user0_.id as id1_6_, user0_.create_time as create_t2_6_, user0_.deleted as deleted3_6_, user0_.department_key as departme4_6_, user0_.email as email5_6_, user0_.mobile_phone as mobile_p6_6_, user0_.modify_time as modify_t7_6_, user0_.nickname as nickname8_6_, user0_.password as password9_6_, user0_.salt as salt10_6_, user0_.status as status11_6_, user0_.username as usernam12_6_ from user user0_ where user0_.email=?; parameters: ; named parameters: {[email protected]}; transformer: org.hibernate.transform.CacheableResultTransformer@110f2
2017-01-10 16:21:19.844 [main] DEBUG org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - Element for key sql: select user0_.id as id1_6_, user0_.create_time as create_t2_6_, user0_.deleted as deleted3_6_, user0_.department_key as departme4_6_, user0_.email as email5_6_, user0_.mobile_phone as mobile_p6_6_, user0_.modify_time as modify_t7_6_, user0_.nickname as nickname8_6_, user0_.password as password9_6_, user0_.salt as salt10_6_, user0_.status as status11_6_, user0_.username as usernam12_6_ from user user0_ where user0_.email=?; parameters: ; named parameters: {[email protected]}; transformer: org.hibernate.transform.CacheableResultTransformer@110f2 is null
2017-01-10 16:21:19.845 [main] DEBUG org.hibernate.cache.internal.StandardQueryCache - Query results were not found in cache
Hibernate: select user0_.id as id1_6_, user0_.create_time as create_t2_6_, user0_.deleted as deleted3_6_, user0_.department_key as departme4_6_, user0_.email as email5_6_, user0_.mobile_phone as mobile_p6_6_, user0_.modify_time as modify_t7_6_, user0_.nickname as nickname8_6_, user0_.password as password9_6_, user0_.salt as salt10_6_, user0_.status as status11_6_, user0_.username as usernam12_6_ from user user0_ where user0_.email=?
Hibernate: select roles0_.user_id as user_id1_8_0_, roles0_.role_id as role_id2_8_0_, role1_.id as id1_4_1_, role1_.available as availabl2_4_1_, role1_.description as descript3_4_1_, role1_.role_key as role_key4_4_1_, role1_.role_value as role_val5_4_1_ from user_role roles0_ inner join role role1_ on roles0_.role_id=role1_.id where roles0_.user_id=?
Hibernate: select resourcepe0_.role_id as role_id4_5_0_, resourcepe0_.id as id1_5_0_, resourcepe0_.id as id1_5_1_, resourcepe0_.permission_ids as permissi2_5_1_, resourcepe0_.resource_id as resource3_5_1_, resourcepe0_.role_id as role_id4_5_1_ from role_resource_permission resourcepe0_ where resourcepe0_.role_id=?
2017-01-10 16:21:20.064 [main] DEBUG org.hibernate.cache.internal.StandardQueryCache - Caching query results in region: org.hibernate.cache.internal.StandardQueryCache; timestamp=6078613420920832
2017-01-10 16:21:20.064 [main] DEBUG org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - key: sql: select user0_.id as id1_6_, user0_.create_time as create_t2_6_, user0_.deleted as deleted3_6_, user0_.department_key as departme4_6_, user0_.email as email5_6_, user0_.mobile_phone as mobile_p6_6_, user0_.modify_time as modify_t7_6_, user0_.nickname as nickname8_6_, user0_.password as password9_6_, user0_.salt as salt10_6_, user0_.status as status11_6_, user0_.username as usernam12_6_ from user user0_ where user0_.email=?; parameters: ; named parameters: {[email protected]}; transformer: org.hibernate.transform.CacheableResultTransformer@110f2 value: [6078613420920832, 1]
// 查询出第一个对象,并缓存
com.teemo.entity.User@1a115fb4
2017-01-10 16:21:20.077 [main] DEBUG org.hibernate.cache.internal.StandardQueryCache - Checking cached query results in region: org.hibernate.cache.internal.StandardQueryCache
2017-01-10 16:21:20.077 [main] DEBUG org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - key: sql: select user0_.id as id1_6_, user0_.create_time as create_t2_6_, user0_.deleted as deleted3_6_, user0_.department_key as departme4_6_, user0_.email as email5_6_, user0_.mobile_phone as mobile_p6_6_, user0_.modify_time as modify_t7_6_, user0_.nickname as nickname8_6_, user0_.password as password9_6_, user0_.salt as salt10_6_, user0_.status as status11_6_, user0_.username as usernam12_6_ from user user0_ where user0_.email=?; parameters: ; named parameters: {[email protected]}; transformer: org.hibernate.transform.CacheableResultTransformer@110f2
2017-01-10 16:21:20.078 [main] DEBUG org.hibernate.cache.internal.StandardQueryCache - Checking query spaces are up-to-date: [user]
2017-01-10 16:21:20.079 [main] DEBUG org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - key: user
2017-01-10 16:21:20.079 [main] DEBUG org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion - Element for key user is null
2017-01-10 16:21:20.080 [main] DEBUG org.hibernate.cache.internal.StandardQueryCache - Returning cached query results
// 根据SQL/HQL比对缓存key,命中
com.teemo.entity.User@5b867c31
从结果中看出,在query中设置query.setCacheable(true);
之后,所有被标记为可缓存的对象都会被缓存下来,所以第二次查询User时,直接命中二级缓存,没有发出SQL查询。
我觉得,Hibernate的缓存模式应用场景太单一,对于通用的DAO或者底层查询方法来说,如果设置为query.setCacheable(true)
显然不太灵活,这样会缓存所有的调用该方法查询到的数据,如果不设置的话,又只能缓存get/load查询,结合自己的系统优化缓存模式还是很必要的,例如使用AOP进行Service切面缓存,这样就与DAO的ORM级别缓存分开了,相对更灵活。
上面是我对Hibernate和ehcache的一点了解,如果有描述不当或者错误的地方欢迎指正!