-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Changing Collections Cascade behavior at runtime
PostPosted: Fri Sep 12, 2008 10:59 pm 
Beginner
Beginner

Joined: Sun Oct 14, 2007 7:29 pm
Posts: 23
Hello,

I am using Hibernate 3.2 and I was hoping to be able to change the cascading behavior of a one to many relationship at runtime rather than having to lock-in the behavior at build time. Depending on what is happening in my application, I would like to programatically select Cascade persist, merge, and save-update at certain times but I don't want this to be bhe default behavior all the time (hence, setting the cascade property in the mapping file to persist, merge, save-update will not work.

In the Hibernate Reference manual, section 10.11, last paragraph of this section, it states that "Finally, note that cascading operations can be applied to an object graph at call time or at flush time.", but I am not able to find any documentation that explains how to accomplish this.

Do any of you have an idea?

Thanks,

Matt


Top
 Profile  
 
 Post subject: Re: Changing Collections Cascade behavior at runtime
PostPosted: Wed Sep 22, 2010 11:17 am 
Newbie

Joined: Fri Jan 27, 2006 1:48 am
Posts: 9
This looks a lot like the following post from 2005:

viewtopic.php?f=1&t=946165&p=2436095&hilit=cascade+runtime#p2436095

I am surprised there is not anyone with a resolution/example! - Any ideas out there?


Top
 Profile  
 
 Post subject: Re: Changing Collections Cascade behavior at runtime
PostPosted: Tue Sep 28, 2010 10:50 am 
Newbie

Joined: Fri Jan 27, 2006 1:48 am
Posts: 9
Here is some background & my initial investigation on the topic...

I[We] am[are] implementing a services layer. It is not to be solely consumed by a UI layer.
There is in fact business processes that will access it [services layer].
As are result, we are using detached objects & the merge method to "re-attach".
Our design has a number of variations on the graphs associated with our domain entities.
In other words, we have variations on what we want to eagerly load in a "get" service
& subsequently what we want to cascade to in a "save" service. Hence, the need for
"dynamic"/runtime cascades.

Looking at the reference documentation, in order to "dynamically" cascade,
There are three potential options: the Lifecycle interface, interceptor & EventListener.

The Lifecycle is deprecated & doesn't handle merge, anyway.
Interceptors, likewise do not handle merges[looking at the EmptyInterceptor interface].
It does have a preFlush method, however I have decided that I will attempt to go with an EventListener approach
and subclass DefaultMergeEventListener....

You could say that I am taking the "call time" approach referred to in the reference documentation:

"...Finally, note that cascading operations can be applied to an object graph at call time or at flush time..."


Top
 Profile  
 
 Post subject: Re: Changing Collections Cascade behavior at runtime
PostPosted: Thu Sep 30, 2010 3:01 pm 
Newbie

Joined: Fri Jan 27, 2006 1:48 am
Posts: 9
I came up with an implementation.
Here is what I did:

I sub-classed DefaultMergeEventListener. I needed to override the cascadeOnMerge method to affect the change.
As it turns out, since l did not have access to the cascade info, I had to create a class that implements the EntityPersister interface. I use this class [MergeEntityPersister] to provide the changes to the cascade attribute to the Cascade object.

Here is the relevant code:

MergeEventListener :
Code:
public class MergeEventListener extends DefaultMergeEventListener  {

   protected void cascadeOnMerge(
         final EventSource source,
         final EntityPersister persister,
         final Object entity,
         final Map copyCache
      ) {
         MergeEntityPersister mergeEventPersister = new MergeEntityPersister(persister);
         
         CascadeStyle[] cascadeStyle = persister.getPropertyCascadeStyles();
         boolean hasCascades = persister.hasCascades();

         // If an entity implements DynamicCascade interface AND has cascades - then get go get 'em!
         if (entity instanceof com.ops.commons.datatransfer.DynamicCascade
               &&  ((com.ops.commons.datatransfer.DynamicCascade)entity).getCascadeMap() != null) {         
            Map cascadeMap = ((com.ops.commons.datatransfer.DynamicCascade)entity).getCascadeMap();
            
            String[] propertyNames = persister.getPropertyNames();
            for (int i = 0; i < propertyNames.length; i++) {
               CascadeStyle newStyle = (CascadeStyle)cascadeMap.get(propertyNames[i]);
               if (newStyle != null) {
                  cascadeStyle[i] = newStyle;
                  hasCascades = true;
               }
            }
         }
         mergeEventPersister.setCascadeStyle(cascadeStyle);
         mergeEventPersister.setHasCascades(hasCascades);

         source.getPersistenceContext().incrementCascadeLevel();
         try {
            new Cascade( getCascadeAction(), Cascade.BEFORE_MERGE, source )
                  .cascade(mergeEventPersister, entity, copyCache);
         }
         finally {
            source.getPersistenceContext().decrementCascadeLevel();
         }
      }
   
   
}


Relevant part of MergeEntityPersister:
Code:
public class MergeEntityPersister implements EntityPersister {

   
   EntityPersister persister;
   CascadeStyle[] cascadeStyle;
   boolean hasCascades;
   
   public MergeEntityPersister(EntityPersister persister) {this.persister = persister;}

   private SessionFactoryImplementor factory;

   // These are additional methods that are necessary to modify the Cascade info
   public void setCascadeStyle(CascadeStyle[] cascadeStyle) { this.cascadeStyle = cascadeStyle;}
   public void setHasCascades(boolean hasCascades) {this.hasCascades = hasCascades;}
   
   // These are methods that override the current persister methods
   public CascadeStyle[] getPropertyCascadeStyles() {
            return this.cascadeStyle;
   }

   public boolean hasCascades() {
      return this.hasCascades;
   }

   // These methods merely wrap the current persister
   public Type[] getPropertyTypes() {
      return persister.getPropertyTypes();
   }

   public boolean hasUninitializedLazyProperties(Object object, EntityMode entityMode) {
      
      return persister.hasUninitializedLazyProperties(object, entityMode);
   }

   public Object getPropertyValue(Object object, int i, EntityMode entityMode) throws HibernateException {
      return persister.getPropertyValue(object, i, entityMode);
   }

   public Object getPropertyValue(Object object, String propertyName, EntityMode entityMode) throws HibernateException {
      return persister.getPropertyValue(object, propertyName, entityMode);
   }

   // The rest of these methods are dummies that we need to fulfill the EntityPersister interface
   // we only care about what is necessary to modify the cascade function
   // .. and NONE of these play a part


I setup the "new" cascade attribute[s] in a Map - in my Entity Object...This way I have control over exactly what collections are cascaded [.vs. modifying cascade attributes based on Class].
Code:
            // Set up dynamic cascade values for processing in the EventListener
            Map cascadeMap = new java.util.HashMap();
            cascadeMap.put("companyXs", CascadeStyle.ALL);       
            contactDTO.setCascadeMap(cascadeMap);
            ...
            (new ContactDAO()).merge(contactDTO);



I initially set up the Listener in the configuration file, programmatically [as described in the reference documentation]:
Code:
// here we add the listener
             org.hibernate.event.MergeEventListener[] mergeList = { new org.component.services.MergeEventListener() };
             config.getEventListeners().setMergeEventListeners(mergeList);


It seems a somewhat obscure way to implement it [ RE: in particular overriding the persister]...
I haven't thoroughly exercised it but so far so good...


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
cron
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.