我正在使用Hibernate 4.3.11.Final和Spring 3.2.11.RELEASE。我很困惑为什么我的缓存驱逐不起作用。我在DAO中设置了这个......
@Override
@Caching(evict = { @CacheEvict("main") })
public Organization save(Organization organization)
{
return (Organization) super.save(organization);
}
@Override
@Cacheable(value = "main")
public Organization findById(String id)
{
return super.find(id);
}
这是我的Spring配置......
<cache:annotation-driven key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="org.mainco.subco.myproject.util.CacheKeyGenerator" />
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="classpath:ehcache.xml"
p:shared="true" />
<util:map id="jpaPropertyMap">
<entry key="hibernate.show_sql" value="true" />
<entry key="hibernate.dialect" value="org.mainco.subco.myproject.jpa.subcoMysql5Dialect" />
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />
<entry key="hibernate.cache.use_second_level_cache" value="true" />
<entry key="hibernate.cache.use_query_cache" value="false" />
<entry key="hibernate.generate_statistics" value="true" />
<entry key="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE" />
</util:map>
<bean id="sharedEntityManager"
class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
然而在下面的测试中,我的实体并没有从缓存中被逐出,我知道这是因为带有“命中数#3:”的行打印出“3”,而带有&#34;命中数的行#2 :“打印出”2“。
private net.sf.ehcache.Cache m_cache
@Autowired
private net.sf.ehcache.CacheManager ehCacheManager;
@Before
public void setup()
{
m_cache = ehCacheManager.getCache("main");
m_transactionTemplate = new TransactionTemplate(m_transactionManager);
} // setup
...
@Test
public void testCacheEviction()
{
final String orgId = m_testProps.getProperty("test.org.id");
// Load the entity into the second-level cache
m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
m_orgSvc.findById(orgId);
return null;
});
final long hitCount = m_cache.getStatistics().getCacheHits();
System.out.println("hit count #1:" + hitCount);
m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
final Organization org = m_orgSvc.findById(orgId);
System.out.println("hit count:" + m_cache.getStatistics().getCacheHits());
org.setName("newName");
m_orgSvc.save(org);
return null;
});
// Reload the entity. This should not incur a hit on the cache.
m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
System.out.println("hit count #2:" + m_cache.getStatistics().getCacheHits());
final Organization newOrg = m_orgSvc.findById(orgId);
System.out.println("hit count #3:" + m_cache.getStatistics().getCacheHits());
return null;
});
允许我从二级缓存中逐出实体的正确配置是什么?
编辑:我在应用程序上下文中引用的CacheKeyGenerator类定义如下
public class CacheKeyGenerator implements KeyGenerator
{
@Override
public Object generate(final Object target, final Method method,
final Object... params) {
final List<Object> key = new ArrayList<Object>();
key.add(method.getDeclaringClass().getName());
key.add(method.getName());
for (final Object o : params) {
key.add(o);
}
return key;
}
}
因此我不必为每个@Cacheable注释定义一个“键”,我更喜欢(更少的代码)。但是,我不知道这是如何适用于CacheEviction的。我认为@CacheEvict注释将使用相同的密钥生成方案。
答案 0 :(得分:4)
我重新编写了CodeKeyGenerator
,如下所示。这将根据您发送的参数创建一个键。如果它是一个字符串(如果是id),它将按原样使用它。如果它是Organization
对象,它从该对象获取id并将其用于密钥。这样您就不需要在所有地方重写代码。 (只需更改即可使用以下代码替换CacheKeyGenerator
。)
public class CacheKeyGenerator implements KeyGenerator
{
@Override
public Object generate(final Object target, final Method method,
final Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
if (params[0].getClass().getName() == "Organization" ) {
sb.append(((Organization) params[0]).id);
}
else if (params[0].getClass().getName() == "java.lang.String" ) {
sb.append(params[0].toString());
}
return sb.toString();
}
}
答案 1 :(得分:3)
您缺少@Cacheable
和@CacheEvict
的缓存密钥。因此,这两个操作使用不同的缓存键,因此实体不会被驱逐。
来自@Cacheable.key
的JavaDocs:
用于动态计算密钥的Spring表达式语言(SpEL)表达式。默认值为
""
,表示所有方法参数都被视为键,除非已配置自定义{@link #keyGenerator}。
因此,@Cacheable(value = "main") public Organization findById(String id)
表示返回的对象(类型为Organization
)将使用密钥id
缓存。
同样,@Caching(evict = { @CacheEvict("main") }) public Organization save(Organization organization)
表示organization
的字符串表示将被视为缓存键。
解决方案是进行以下更改:
@Cacheable(value = "main", key ="#id)
@CacheEvict(value = "main", key = "#organization.id")
这将强制两个缓存操作使用相同的密钥。
答案 2 :(得分:1)
你想要驱逐的不是一个Hibernate的二级缓存,而是一个Spring Cache,这是一个完全不同的缓存层。
根据Hibernate的docs,二级缓存是逐个类和逐个集合的集群或JVM级(SessionFactory级)缓存
这意味着它仅由Hibernate管理,@Cacheable
或@CacheEvict
等注释对其没有影响。
如何在测试中获得m_cache
实例并不是特别清楚,但如果它实际上是Hibernate的二级缓存,则不会使用您使用的注释来驱逐它。
你必须以编程方式逐出它,例如:
sessionFactory.evict(Organization.class)
无论如何,只要你在单个JVM和Hibernate中进行所有数据访问,你就不必担心缓存被驱逐,它是由框架本身透明地处理的。
有关驱逐可能性的更多信息,请参阅Hibernate文档, 20.3一章。管理缓存。