Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring 为什么我的实体没有从二级缓存中逐出?_Spring_Hibernate_Ehcache_Second Level Cache_Evict - Fatal编程技术网

Spring 为什么我的实体没有从二级缓存中逐出?

Spring 为什么我的实体没有从二级缓存中逐出?,spring,hibernate,ehcache,second-level-cache,evict,Spring,Hibernate,Ehcache,Second Level Cache,Evict,我正在使用Hibernate4.3.11.Final和Spring3.2.11.RELEASE。我不明白为什么我的缓存逐出不起作用。我在我的刀上设置了这个 @Override @Caching(evict = { @CacheEvict("main") }) public Organization save(Organization organization) { return (Organization) super.save(organization); } @Override @

我正在使用Hibernate4.3.11.Final和Spring3.2.11.RELEASE。我不明白为什么我的缓存逐出不起作用。我在我的刀上设置了这个

@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>

但是在下面的测试中,我的实体没有从缓存中被逐出,我知道这是因为带有“hit count#3:”的行打印出“3”,而带有“hit count#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;
    });
private net.sf.ehcache.Cache m_Cache
@自动连线
private net.sf.ehcache.CacheManager ehCacheManager;
@以前
公共作废设置()
{
m_cache=ehCacheManager.getCache(“main”);
m_transactionTemplate=新的transactionTemplate(m_transactionManager);
}//设置
...
@试验
public void testcacheexecution()
{
最终字符串orgId=m_testProps.getProperty(“test.org.id”);
//将实体加载到二级缓存中
m_transactionTemplate.execute((TransactionCallback)transactionStatus->{
m_orgSvc.findById(orgId);
返回null;
});
最终长命中率=m_cache.getStatistics().getCacheHits();
System.out.println(“命中计数#1:+命中计数”);
m_transactionTemplate.execute((TransactionCallback)transactionStatus->{
最终组织org=m_orgSvc.findById(orgId);
System.out.println(“命中计数:+m_cache.getStatistics().getCacheHits());
org.setName(“新名称”);
m_orgSvc.save(组织);
返回null;
});
//重新加载实体。这不应导致缓存命中。
m_transactionTemplate.execute((TransactionCallback)transactionStatus->{
System.out.println(“命中计数2:+m#u cache.getStatistics().getCacheHits());
最终组织newOrg=m_orgSvc.findById(orgId);
System.out.println(“命中计数#3:+m_cache.getStatistics().getCacheHits());
返回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;
    }  
}
公共类CacheKeyGenerator实现KeyGenerator
{
@凌驾
公共对象生成(最终对象目标、最终方法、,
最终对象…参数){
最终列表键=新的ArrayList();
key.add(方法.getDeclaringClass().getName());
key.add(method.getName());
for(最终对象o:params){
键。添加(o);
}
返回键;
}  
}

因此,我不必为我喜欢的每个@Cacheable注释定义一个“键”(代码更少)。但是,我不知道这如何应用于CacheReceive。我认为@CacheReceive注释将使用相同的密钥生成方案。

您缺少
@Cacheable
@cacheReceive
的缓存密钥。因此,这两个操作使用不同的缓存密钥,因此实体不会被逐出。

从JavaDocs for
@Cacheable.key

用于动态计算键的Spring表达式语言(SpEL)表达式。默认值为
“”
,这意味着所有方法参数都被视为键,除非配置了自定义的{@link#keyGenerator}

因此,
@Cacheable(value=“main”)public Organization findById(String id)
意味着返回的对象(类型为
Organization
)将使用键
id
进行缓存

类似地,
@Caching(execute={@cacheexecute(“main”)})公共组织save(Organization Organization)
意味着
组织的字符串表示形式将被视为缓存键


解决方案是进行以下更改:

@Cacheable(value = "main", key ="#id)

@CacheEvict(value = "main", key = "#organization.id")

这将强制两个缓存操作使用相同的键。

我已重新编写了
CodeKeyGenerator
,如下所示。这将根据您发送的参数生成一个键。如果是字符串(如果是id),它将按原样使用它。如果它是
组织
对象,它将从该对象获取id并将其用作密钥。这样,您就不需要在所有位置重写代码。(唯一的更改是您需要将
CacheKeyGenerator
替换为以下代码。)



您试图逐出的不是一个Hibernate的二级缓存,而是一个完全不同的缓存层

按照Hibernate的说法,第二级缓存是一个集群级或JVM级(SessionFactory级)缓存,它以类为单位,以集合为单位

这意味着它完全由Hibernate管理,诸如
@Cacheable
@cacheexecute
之类的注释对它没有影响

在测试中如何获得
m_缓存
实例并不十分清楚,但如果它确实是Hibernate的二级缓存,则不会使用您使用的注释将其逐出

您必须按程序将其逐出,例如:

sessionFactory.evict(Organization.class)
无论如何,只要您在单个JVM中并通过Hibernate访问所有数据,就不必担心缓存逐出,它是由框架本身透明地处理的


有关逐出可能性的更多信息,请参阅Hibernate文档,第20.3章。管理缓存。

因为我在应用程序上下文中包含了这一点(从我的问题中),'您的密钥生成器为这两个方法生成不同的密钥,这会再次导致密钥不匹配,因此不会逐出。打开调试日志以验证这一点。您将看到Spring缓存日志,其中将显示生成密钥时的逻辑错误。我创建了一个示例,以向您演示我的答案是否有效。您可以下载它并以
mvn clean test
的形式运行它,以查看所有测试是否通过。其中有一个测试,用于根据您的调用检查缓存状态。我建议您采集我的样本和
sessionFactory.evict(Organization.class)