2012-03-06 18 views
6

के साथ निर्भर संग्रहों को एक साथ बेदखल करें मुझे अभी एहसास हुआ है कि जब किसी ऑब्जेक्ट को हाइबरनेट कैश, dependant collections, if cached, have to be evicted separately से निकाल दिया जाता है।अभिभावक इकाई

मुझे इस के लिए एक एक बड़ा WTF है:

  • यह एक संग्रह (जैसे जब एक नया एक वस्तु मानचित्रण में जोड़ा जाता है) को बेदखल करने की भूल जाते हैं वास्तव में आसान है;
  • आश्रित संग्रह को बेदखल करने के लिए कोड बदसूरत और भारी है, उदा।

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

यह काफी स्पष्ट है कि अगर माता-पिता वस्तु बदल गया है, वहाँ थोड़ा भावना यह है कि वे के रूप में के संकलनों है रखना है वैसे भी उस माता-पिता से प्राप्त होने की संभावना है।

क्या मुझे यहां कुछ याद आ रही है? क्या इस कोड को मैन्युअल रूप से लिखने के बिना किसी भी वस्तु को अपने सभी बच्चों की इकाइयों के साथ एक साथ फ्लश करने का कोई तरीका नहीं है?

+2

'आश्रित संग्रह' से आपका मतलब है कि वे "ऑल-डिलीट-अनाथ" जैसे कैस्केड के साथ कॉन्फ़िगर किए गए हैं? –

+0

@ नाथन ह्यूजेस - हाँ। मेरी तर्क सूची में जोड़ने के लिए - संग्रहों को बेदखल करते समय, किसी को भी मूल आईडी को पास करना होगा। – mindas

उत्तर

4

यह पुराना issue है। संग्रहित इकाई संग्रह, अद्यतन या निकालने पर संग्रह कैश को बेदखल करने के लिए हाइबरनेट में हुक करने का एक तरीका है। मेरे पास supplied a fix for hibernate है। फिक्स हाइबरनेट 4.3.0.Beta5 के लिए निर्धारित है और संपत्ति से सक्रिय हो जाएगा:

hibernate.cache.auto_evict_collection_cache=true 

जब तक यह सुधार realeased नहीं है तो आप बस अपने SessionFactory साथ CollectionCacheInvalidator पंजीकरण से बेदखली-तर्क सुई वैकल्पिक हल कर सकते हैं और अपने आप से सत्र FactoryServiceRegistry।

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @OneToMany(mappedBy = 'sender', cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true) Set<Gift> sentGifts = [] 

जब बुला उपहार पर हटाकर बाद में माता-पिता सब कुछ बचत सुचारू रूप से काम करता है:

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() 
       ) 
     ); 
    } 
} 
0

यह सिर्फ एक कैश है। एक कैश को डेटाबेस पहुंच को कम करना चाहिए। जब आप किसी ऑब्जेक्ट को बेदखल करते हैं, तो अक्सर आपने बच्चे ऑब्जेक्ट्स के साथ कोई संशोधन नहीं किया है, और अगली बार कैश से उन्हें लोड किया जा सकता है। इसके अलावा अक्सर यह होता है कि बाल वस्तुओं का उपयोग अन्य मूल वस्तुओं द्वारा किया जाता है (इस मामले में नाम 'बच्चा' सही नहीं है, क्योंकि यह एक एन: 1 या एम: एन संबंध है)। बच्चों को बेदखल करना कहीं और अजीब त्रुटियों को उकसा सकता है जहां बच्चे की वस्तुएं अभी भी उपयोग में हैं।

तो अगर बच्चों को बेदखल करना अच्छा है तो बस आपके आवेदन और डेटाबेस डिज़ाइन पर निर्भर करता है। इस प्रकार हाइबरनेट डिफ़ॉल्ट रूप से बाल वस्तुओं को बेदखल नहीं करता है।

यदि आप बच्चे की वस्तुओं को स्वचालित रूप से बेदखल करना चाहते हैं, तो अपनी मैपिंग फ़ाइल में कैस्केड = "बेदखल" का उपयोग करें।

सभी वस्तुओं को बेदखल करने के लिए एक और रबीयत विधि सत्र को बंद करना और एक नया खोलना है। फिर सत्र की सभी वस्तुओं को बेदखल कर दिया जाता है।

+1

मैं आपके अंक से असहमत हूं। कैश से बच्चों को बेदखल करने से कोई असर नहीं पड़ेगा। यदि बाल वस्तुओं का कोई संदर्भ मौजूद नहीं है, तो यह कोई समस्या नहीं है - जब आवश्यक हो तो इसे मांग पर लोड किया जा सकता है। या यदि कोई और वर्ग कैश्ड संग्रह का संदर्भ रखता है, तो फिर यह कोई समस्या नहीं है क्योंकि हार्ड संदर्भ अभी भी मौजूद है। लेकिन कैस्केड = "बेदखल" पर संकेत के लिए धन्यवाद, मैं इसे देख लूंगा। – mindas

0

2 स्तर कैश प्रदाता के रूप में हाइबरनेट 4 और ehcache का उपयोग करते हुए मैं संग्रह बस की घोषणा इकाई को दूर करने में सक्षम था।

+0

सब कुछ काम करता है क्योंकि आपके पास एनोटेशन "fetch = FetchType.EAGER" है –

संबंधित मुद्दे