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