与父实体一起驱逐相关集合 [英] Evict dependant collections together with parent entity

查看:245
本文介绍了与父实体一起驱逐相关集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚意识到,当一个对象从Hibernate缓存中被驱逐时,依赖集合,如果缓存,必须分别 逐出。



我这是一个大的跆拳道:


  • 真的很容易忘记驱逐一个集合(例如,当一个新集合被添加到对象映射);
  • 驱逐依赖集合的代码非常笨重,比如

    MyClass myObject = ...;

    getHibernateTemplate()。evict(myObject);

    Cache cache = getHibernateTemplate()。getSessionFactory()。getCache();

    cache.evictCollection(my .package.MyClass.myCollection1,id);

    ...

    cache.evictCollection(my.package.MyClass.myCollectionN,id);




它是q很明显,如果父对象发生了变化,那么保留它的集合是没有意义的,因为它们很可能是从父母派生出来的。



我是否错过了某些东西这里?如果没有手动编写所有这些代码,是否真的没有办法将对象与其所有子实体一起刷新?

旧的问题。在插入,更新或删除集合引用实体时,有一种方法可以挂钩到hibernate中以驱逐收集缓存。我有提供了一个修复hibernate 。该修补程序计划用于Hibernate 4.3.0.Beta5,并且将由属性激活:

  hibernate.cache.auto_evict_collection_cache = true 

只要注册了CollectionCacheInvalidator,就可以解决注入驱逐逻辑问题你自己的SessionFactory和SessionFactoryServiceRegistry。

  import javax.persistence.OneToMany; 
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import my.own.library.BeanInformationFromClass;
导入my.own.library.PropertyInformationFromClass;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostInsertEventListener;
import org.hibernate.event.spi.PreDeleteEvent;
import org.hibernate.event.spi.PreDeleteEventListener;
import org.hibernate.event.spi.PreUpdateEvent;
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;

/ **
* @作者Andreas Berger(由$作者$最新修改)
* @version $ Id $
* @created 27.08.13 - 17 :49
* /
public class CollectionCacheInvalidator
实现PostInsertEventListener,PreDeleteEventListener,PreUpdateEventListener {

private static final Logger LOGGER = Logger.getLogger(CollectionCacheInvalidator.class);

私人地图< String,String> mappedByFieldMapping;

public void integrate(SessionFactoryImplementor sf,SessionFactoryServiceRegistry registry){
EventListenerRegistry eventListenerRegistry = registry.getService(EventListenerRegistry.class);
eventListenerRegistry.appendListeners(EventType.POST_INSERT,this);
eventListenerRegistry.appendListeners(EventType.PRE_DELETE,this);
eventListenerRegistry.appendListeners(EventType.PRE_UPDATE,this);

mappedByFieldMapping = new HashMap< String,String>();

地图< String,CollectionPersister> persiters = sf.getCollectionPersisters(); $(b)if(persiters!= null){
for(CollectionPersister collectionPersister:persiters.values()){
if(!collectionPersister.hasCache()){
continue;
}
if(!(collectionPersister instanceof Joinable)){
continue;
}
字符串oneToManyFieldName = collectionPersister.getNodeName();
EntityPersister ownerEntityPersister = collectionPersister.getOwnerEntityPersister();
类ownerClass = ownerEntityPersister.getMappedClass();

//逻辑获取OneToMany注解的mappedBy属性。
BeanInformationFromClass bi = new BeanInformationFromClass(ownerClass);
PropertyInformationFromClass prop = bi.getProperty(oneToManyFieldName);
OneToMany oneToMany = prop.getAnnotation(OneToMany.class);
String mappedBy = null;
if(oneToMany!= null&& StringUtils.isNotBlank(oneToMany.mappedBy())){
mappedBy = oneToMany.mappedBy(); $(b)b
mappedByFieldMapping.put(((可连接)collectionPersister).getName(),mappedBy);
}
}
}

@Override
public void onPostInsert(PostInsertEvent event){
evictCache(event.getEntity(),event .getPersister(),event.getSession(),null);
}

@Override
public boolean onPreDelete(PreDeleteEvent event){
evictCache(event.getEntity(),event.getPersister(),event.getSession() , 空值 );
返回false;
}

@Override
public boolean onPreUpdate(PreUpdateEvent event){
evictCache(event.getEntity(),event.getPersister(),event.getSession() ,event.getOldState());
返回false;


private void evictCache(Object entity,EntityPersister persister,EventSource session,Object [] oldState){
try {
SessionFactoryImplementor factory = persister.getFactory() ;

Set< String> collectionRoles = factory.getCollectionRolesByEntityParticipant(persister.getEntityName());
if(collectionRoles == null || collectionRoles.isEmpty()){
return;
}
for(String role:collectionRoles){
CollectionPersister collectionPersister = factory.getCollectionPersister(role);
if(!collectionPersister.hasCache()){
continue;
}
if(!(collectionPersister instanceof Joinable)){
continue;
}
String mappedBy = mappedByFieldMapping.get(((Joinable)collectionPersister).getName());
if(mappedBy!= null){
int i = persister.getEntityMetamodel()。getPropertyIndex(mappedBy);
Serializable oldId = null;
if(oldState!= null){
oldId = session.getIdentifier(oldState [i]);
}
Object ref = persister.getPropertyValue(entity,i);
Serializable id = null;
if(ref!= null){
id = session.getIdentifier(ref);

if(id!= null&&!id.equals(oldId)){
evict(id,collectionPersister,session);
if(oldId!= null){
evict(id,collectionPersister,session);
}
}
}
else {
LOGGER.debug(Evict CollectionRegion+ role);
collectionPersister.getCacheAccessStrategy()。evictAll();


$ b $ catch(Exception e){
LOGGER.error(,e);



private void evict(Serializable id,CollectionPersister collectionPersister,EventSource session){
LOGGER.debug(Evict CollectionRegion+ collectionPersister.getRole() +for id+ id);
collectionPersister.getCacheAccessStrategy()。evict(
session.generateCacheKey(
id,
collectionPersister.getKeyType(),
collectionPersister.getRole()

);
}
}


I have just realized that when an object is evicted from Hibernate cache, dependant collections, if cached, have to be evicted separately.

For me this is a one big WTF:

  • it is really easy to forget to evict a collection (e.g. when a new one is added to the object mapping);
  • code for evicting dependant collections is ugly and bulky, e.g.

    MyClass myObject = ...;
    getHibernateTemplate().evict(myObject);
    Cache cache = getHibernateTemplate().getSessionFactory().getCache();
    cache.evictCollection("my.package.MyClass.myCollection1, id);
    ...
    cache.evictCollection("my.package.MyClass.myCollectionN, id);

It is quite obvious that if parent object has changed, there is little sense to keep it's collections around as they are most likely to be derived from that parent anyway.

Am I missing something here? Is there really no way to flush an object together with all its child entities without writing all this code manually?

解决方案

This is an old issue. There is a way to hook into hibernate to evict collection caches on inserting, updating or removing the collections referred entity. I have supplied a fix for hibernate. The Fix is scheduled for Hibernate 4.3.0.Beta5 and will be activated by the property:

hibernate.cache.auto_evict_collection_cache=true

As long this fix is not realeased you can workaround to inject eviction-logic by just registering the CollectionCacheInvalidator with your SessionFactory and SessionFactoryServiceRegistry by your own.

import javax.persistence.OneToMany;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import my.own.library.BeanInformationFromClass;
import my.own.library.PropertyInformationFromClass;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostInsertEventListener;
import org.hibernate.event.spi.PreDeleteEvent;
import org.hibernate.event.spi.PreDeleteEventListener;
import org.hibernate.event.spi.PreUpdateEvent;
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;

/**
 * @author Andreas Berger (latest modification by $Author$)
 * @version $Id$
 * @created 27.08.13 - 17:49
 */
public class CollectionCacheInvalidator
        implements PostInsertEventListener, PreDeleteEventListener, PreUpdateEventListener {

    private static final Logger LOGGER = Logger.getLogger( CollectionCacheInvalidator.class );

    private Map<String, String> mappedByFieldMapping;

    public void integrate(SessionFactoryImplementor sf, SessionFactoryServiceRegistry registry) {
        EventListenerRegistry eventListenerRegistry = registry.getService( EventListenerRegistry.class );
        eventListenerRegistry.appendListeners( EventType.POST_INSERT, this );
        eventListenerRegistry.appendListeners( EventType.PRE_DELETE, this );
        eventListenerRegistry.appendListeners( EventType.PRE_UPDATE, this );

        mappedByFieldMapping = new HashMap<String, String>();

        Map<String, CollectionPersister> persiters = sf.getCollectionPersisters();
        if ( persiters != null ) {
            for ( CollectionPersister collectionPersister : persiters.values() ) {
                if ( !collectionPersister.hasCache() ) {
                    continue;
                }
                if ( !(collectionPersister instanceof Joinable) ) {
                    continue;
                }
                String oneToManyFieldName = collectionPersister.getNodeName();
                EntityPersister ownerEntityPersister = collectionPersister.getOwnerEntityPersister();
                Class ownerClass = ownerEntityPersister.getMappedClass();

                // Logic to get the mappedBy attribute of the OneToMany annotation.
                BeanInformationFromClass bi = new BeanInformationFromClass( ownerClass );
                PropertyInformationFromClass prop = bi.getProperty( oneToManyFieldName );
                OneToMany oneToMany = prop.getAnnotation( OneToMany.class );
                String mappedBy = null;
                if ( oneToMany != null && StringUtils.isNotBlank( oneToMany.mappedBy() ) ) {
                    mappedBy = oneToMany.mappedBy();
                }
                mappedByFieldMapping.put( ((Joinable) collectionPersister).getName(), mappedBy );
            }
        }
    }

    @Override
    public void onPostInsert(PostInsertEvent event) {
        evictCache( event.getEntity(), event.getPersister(), event.getSession(), null );
    }

    @Override
    public boolean onPreDelete(PreDeleteEvent event) {
        evictCache( event.getEntity(), event.getPersister(), event.getSession(), null );
        return false;
    }

    @Override
    public boolean onPreUpdate(PreUpdateEvent event) {
        evictCache( event.getEntity(), event.getPersister(), event.getSession(), event.getOldState() );
        return false;
    }

    private void evictCache(Object entity, EntityPersister persister, EventSource session, Object[] oldState) {
        try {
            SessionFactoryImplementor factory = persister.getFactory();

            Set<String> collectionRoles = factory.getCollectionRolesByEntityParticipant( persister.getEntityName() );
            if ( collectionRoles == null || collectionRoles.isEmpty() ) {
                return;
            }
            for ( String role : collectionRoles ) {
                CollectionPersister collectionPersister = factory.getCollectionPersister( role );
                if ( !collectionPersister.hasCache() ) {
                    continue;
                }
                if ( !(collectionPersister instanceof Joinable) ) {
                    continue;
                }
                String mappedBy = mappedByFieldMapping.get( ((Joinable) collectionPersister).getName() );
                if ( mappedBy != null ) {
                    int i = persister.getEntityMetamodel().getPropertyIndex( mappedBy );
                    Serializable oldId = null;
                    if ( oldState != null ) {
                        oldId = session.getIdentifier( oldState[i] );
                    }
                    Object ref = persister.getPropertyValue( entity, i );
                    Serializable id = null;
                    if ( ref != null ) {
                        id = session.getIdentifier( ref );
                    }
                    if ( id != null && !id.equals( oldId ) ) {
                        evict( id, collectionPersister, session );
                        if ( oldId != null ) {
                            evict( id, collectionPersister, session );
                        }
                    }
                }
                else {
                    LOGGER.debug( "Evict CollectionRegion " + role );
                    collectionPersister.getCacheAccessStrategy().evictAll();
                }
            }
        }
        catch (Exception e) {
            LOGGER.error( "", e );
        }
    }

    private void evict(Serializable id, CollectionPersister collectionPersister, EventSource session) {
        LOGGER.debug( "Evict CollectionRegion " + collectionPersister.getRole() + " for id " + id );
        collectionPersister.getCacheAccessStrategy().evict(
                session.generateCacheKey(
                        id,
                        collectionPersister.getKeyType(),
                        collectionPersister.getRole()
                )
        );
    }
}

这篇关于与父实体一起驱逐相关集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆