//$Id: DefaultReplicateEventListener.java 9673 2006-03-22 14:57:59Z steve.ebersole@jboss.com $
package org.hibernate.event.def;
import org.hibernate.HibernateException;
import org.hibernate.TransientObjectException;
import org.hibernate.ReplicationMode;
import org.hibernate.LockMode;
import org.hibernate.engine.Cascade;
import org.hibernate.engine.CascadingAction;
import org.hibernate.engine.EntityKey;
import org.hibernate.engine.Status;
import org.hibernate.event.EventSource;
import org.hibernate.event.ReplicateEvent;
import org.hibernate.event.ReplicateEventListener;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type;
import java.io.Serializable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Defines the default replicate event listener used by Hibernate to replicate
* entities in response to generated replicate events.
*
* @author Steve Ebersole
*/
public class DefaultReplicateEventListener extends AbstractSaveEventListener implements ReplicateEventListener {
private static final Log log = LogFactory.getLog(DefaultReplicateEventListener.class);
/**
* Handle the given replicate event.
*
* @param event The replicate event to be handled.
* @throws HibernateException
*/
public void onReplicate(ReplicateEvent event) throws HibernateException {
final EventSource source = event.getSession();
if ( source.getPersistenceContext().reassociateIfUninitializedProxy( event.getObject() ) ) {
log.trace("uninitialized proxy passed to replicate()");
return;
}
Object entity = source.getPersistenceContext().unproxyAndReassociate( event.getObject() );
if ( source.getPersistenceContext().isEntryFor(entity) ) {
log.trace("ignoring persistent instance passed to replicate()");
//hum ... should we cascade anyway? throw an exception? fine like it is?
return;
}
EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
// get the id from the object
/*if ( persister.isUnsaved(entity, source) ) {
throw new TransientObjectException("transient instance passed to replicate()");
}*/
Serializable id = persister.getIdentifier( entity, source.getEntityMode() );
if (id==null) {
throw new TransientObjectException("instance with null id passed to replicate()");
}
final ReplicationMode replicationMode = event.getReplicationMode();
final Object oldVersion;
if ( replicationMode == ReplicationMode.EXCEPTION ) {
//always do an INSERT, and let it fail by constraint violation
oldVersion = null;
}
else {
//what is the version on the database?
oldVersion = persister.getCurrentVersion(id, source);
}
if ( oldVersion!=null ) {
//existing row - do an update if appropriate
if ( log.isTraceEnabled() ) {
log.trace( "found existing row for " +
MessageHelper.infoString( persister, id, source.getFactory() ) );
}
boolean canReplicate = replicationMode.shouldOverwriteCurrentVersion(
entity,
oldVersion,
persister.getVersion( entity, source.getEntityMode() ),
persister.getVersionType()
);
if (canReplicate) {
//will result in a SQL UPDATE:
performReplication(entity, id, oldVersion, persister, replicationMode, source);
}
else {
//else do nothing (don't even reassociate object!)
log.trace("no need to replicate");
}
//TODO: would it be better to do a refresh from db?
}
else {
// no existing row - do an insert
if ( log.isTraceEnabled() ) {
log.trace( "no existing row, replicating new instance " +
MessageHelper.infoString( persister, id, source.getFactory() ) );
}
final boolean regenerate = persister.isIdentifierAssignedByInsert(); // prefer re-generation of identity!
final EntityKey key = regenerate ?
null : new EntityKey( id, persister, source.getEntityMode() );
performSaveOrReplicate(
entity,
key,
persister,
regenerate,
replicationMode,
source,
true
);
}
}
protected boolean visitCollectionsBeforeSave(Serializable id, Object[] values, Type[] types, EventSource source) {
//TODO: we use two visitors here, inefficient!
OnReplicateVisitor visitor = new OnReplicateVisitor(source, id, false);
visitor.processEntityPropertyValues(values, types);
return super.visitCollectionsBeforeSave(id, values, types, source);
}
protected boolean substituteValuesIfNecessary(
Object entity,
Serializable id,
Object[] values,
EntityPersister persister,
SessionImplementor source
) {
return false;
}
protected boolean isVersionIncrementDisabled() {
return true;
}
private final void performReplication(
Object entity,
Serializable id,
Object version,
EntityPersister persister,
ReplicationMode replicationMode,
EventSource source)
throws HibernateException {
if ( log.isTraceEnabled() ) {
log.trace(
"replicating changes to " +
MessageHelper.infoString( persister, id, source.getFactory() )
);
}
new OnReplicateVisitor(source, id, true).process( entity, persister );
source.getPersistenceContext().addEntity(
entity,
Status.MANAGED,
null,
new EntityKey( id, persister, source.getEntityMode() ),
version,
LockMode.NONE,
true,
persister,
true,
false
);
cascadeAfterReplicate( entity, persister, replicationMode, source );
}
private void cascadeAfterReplicate(
Object entity,
EntityPersister persister,
ReplicationMode replicationMode,
EventSource source
) {
source.getPersistenceContext().incrementCascadeLevel();
try {
new Cascade(CascadingAction.REPLICATE, Cascade.AFTER_UPDATE, source)
.cascade(persister, entity, replicationMode);
}
finally {
source.getPersistenceContext().decrementCascadeLevel();
}
}
protected CascadingAction getCascadeAction() {
return CascadingAction.REPLICATE;
}
}