[hibernate-commits] Hibernate SVN: r10948 - in trunk/Hibernate3: src/org/hibernate/event/def test/org/hibernate/test/propertyref/basic
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Thu Dec 7 16:53:17 EST 2006
Author: steve.ebersole at jboss.com
Date: 2006-12-07 16:53:10 -0500 (Thu, 07 Dec 2006)
New Revision: 10948
Modified:
trunk/Hibernate3/src/org/hibernate/event/def/AbstractReassociateEventListener.java
trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java
trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java
trunk/Hibernate3/src/org/hibernate/event/def/DefaultReplicateEventListener.java
trunk/Hibernate3/src/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java
trunk/Hibernate3/src/org/hibernate/event/def/OnLockVisitor.java
trunk/Hibernate3/src/org/hibernate/event/def/OnReplicateVisitor.java
trunk/Hibernate3/src/org/hibernate/event/def/OnUpdateVisitor.java
trunk/Hibernate3/src/org/hibernate/event/def/ReattachVisitor.java
trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/Person.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/PropertyRefTest.java
Log:
HHH-2291 - reattachment of collections with a property-ref key;
code and javadoc cleanup
Modified: trunk/Hibernate3/src/org/hibernate/event/def/AbstractReassociateEventListener.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/AbstractReassociateEventListener.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/AbstractReassociateEventListener.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -5,7 +5,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.hibernate.HibernateException;
+
import org.hibernate.LockMode;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.EntityKey;
@@ -25,7 +25,7 @@
*/
public class AbstractReassociateEventListener implements Serializable {
- private static final Log log = LogFactory.getLog(AbstractReassociateEventListener.class);
+ private static final Log log = LogFactory.getLog( AbstractReassociateEventListener.class );
/**
* Associates a given entity (either transient or associated with another session) to
@@ -35,52 +35,53 @@
* @param object The entity to be associated
* @param id The id of the entity.
* @param persister The entity's persister instance.
+ *
* @return An EntityEntry representing the entity within this session.
- * @throws HibernateException
*/
- protected final EntityEntry reassociate(AbstractEvent event, Object object, Serializable id, EntityPersister persister)
- throws HibernateException {
-
- if ( log.isTraceEnabled() ) log.trace(
- "reassociating transient instance: " +
- MessageHelper.infoString( persister, id, event.getSession().getFactory() )
+ protected final EntityEntry reassociate(AbstractEvent event, Object object, Serializable id, EntityPersister persister) {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "reassociating transient instance: " +
+ MessageHelper.infoString( persister, id, event.getSession().getFactory() )
);
-
+ }
+
EventSource source = event.getSession();
EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
-
- source.getPersistenceContext().checkUniqueness(key, object);
-
+
+ source.getPersistenceContext().checkUniqueness( key, object );
+
//get a snapshot
Object[] values = persister.getPropertyValues( object, source.getEntityMode() );
- TypeFactory.deepCopy(
- values,
- persister.getPropertyTypes(),
- persister.getPropertyUpdateability(),
- values,
- source
- );
- Object version = Versioning.getVersion(values, persister);
-
+ TypeFactory.deepCopy(
+ values,
+ persister.getPropertyTypes(),
+ persister.getPropertyUpdateability(),
+ values,
+ source
+ );
+ Object version = Versioning.getVersion( values, persister );
+
EntityEntry newEntry = source.getPersistenceContext().addEntity(
- object,
- Status.MANAGED,
- values,
- key,
- version,
- LockMode.NONE,
- true,
- persister,
- false,
+ object,
+ Status.MANAGED,
+ values,
+ key,
+ version,
+ LockMode.NONE,
+ true,
+ persister,
+ false,
true //will be ignored, using the existing Entry instead
- );
-
- new OnLockVisitor( source, id ).process( object, persister );
-
+ );
+
+ new OnLockVisitor( source, id, object ).process( object, persister );
+
persister.afterReassociate( object, source );
-
+
return newEntry;
-
+
}
}
Modified: trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -6,7 +6,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.hibernate.HibernateException;
+
import org.hibernate.LockMode;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.action.EntityIdentityInsertAction;
@@ -44,60 +44,59 @@
protected static final int DETACHED = 2;
protected static final int DELETED = 3;
- private static final Log log = LogFactory.getLog(AbstractSaveEventListener.class);
+ private static final Log log = LogFactory.getLog( AbstractSaveEventListener.class );
/**
* Prepares the save call using the given requested id.
+ *
* @param entity The entity to be saved.
* @param requestedId The id to which to associate the entity.
+ * @param entityName The name of the entity being saved.
+ * @param anything Generally cascade-specific information.
* @param source The session which is the source of this save event.
+ *
* @return The id used to save the entity.
- * @throws HibernateException
*/
protected Serializable saveWithRequestedId(
Object entity,
Serializable requestedId,
String entityName,
Object anything,
- EventSource source)
- throws HibernateException {
+ EventSource source) {
return performSave(
entity,
requestedId,
- source.getEntityPersister(entityName, entity),
+ source.getEntityPersister( entityName, entity ),
false,
anything,
source,
- true
- );
+ true
+ );
}
/**
* Prepares the save call using a newly generated id.
+ *
* @param entity The entity to be saved
* @param entityName The entity-name for the entity to be saved
+ * @param anything Generally cascade-specific information.
* @param source The session which is the source of this save event.
* @param requiresImmediateIdAccess does the event context require
* access to the identifier immediately after execution of this method (if
* not, post-insert style id generators may be postponed if we are outside
* a transaction).
+ *
* @return The id used to save the entity; may be null depending on the
- * type of id generator used and the requiresImmediateIdAccess value
- * @throws HibernateException
+ * type of id generator used and the requiresImmediateIdAccess value
*/
protected Serializable saveWithGeneratedId(
Object entity,
String entityName,
Object anything,
EventSource source,
- boolean requiresImmediateIdAccess)
- throws HibernateException {
-
- EntityPersister persister = source.getEntityPersister(entityName, entity);
-
- Serializable generatedId = persister.getIdentifierGenerator()
- .generate( source, entity );
-
+ boolean requiresImmediateIdAccess) {
+ EntityPersister persister = source.getEntityPersister( entityName, entity );
+ Serializable generatedId = persister.getIdentifierGenerator().generate( source, entity );
if ( generatedId == null ) {
throw new IdentifierGenerationException( "null id generated for:" + entity.getClass() );
}
@@ -108,16 +107,17 @@
return performSave( entity, null, persister, true, anything, source, requiresImmediateIdAccess );
}
else {
-
+
if ( log.isDebugEnabled() ) {
log.debug(
- "generated identifier: " +
- persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ) +
- ", using strategy: " +
- persister.getIdentifierGenerator().getClass().getName() //TODO: define toString()s for generators
- );
+ "generated identifier: " +
+ persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ) +
+ ", using strategy: " +
+ persister.getIdentifierGenerator().getClass().getName()
+ //TODO: define toString()s for generators
+ );
}
-
+
return performSave( entity, generatedId, persister, false, anything, source, true );
}
}
@@ -125,18 +125,20 @@
/**
* Ppepares the save call by checking the session caches for a pre-existing
* entity and performing any lifecycle callbacks.
+ *
* @param entity The entity to be saved.
* @param id The id by which to save the entity.
* @param persister The entity's persister instance.
- * @param useIdentityColumn Is an identity column in use?
+ * @param useIdentityColumn Is an identity column being used?
+ * @param anything Generally cascade-specific information.
* @param source The session from which the event originated.
* @param requiresImmediateIdAccess does the event context require
* access to the identifier immediately after execution of this method (if
* not, post-insert style id generators may be postponed if we are outside
* a transaction).
+ *
* @return The id used to save the entity; may be null depending on the
- * type of id generator used and the requiresImmediateIdAccess value
- * @throws HibernateException
+ * type of id generator used and the requiresImmediateIdAccess value
*/
protected Serializable performSave(
Object entity,
@@ -145,55 +147,54 @@
boolean useIdentityColumn,
Object anything,
EventSource source,
- boolean requiresImmediateIdAccess)
- throws HibernateException {
+ boolean requiresImmediateIdAccess) {
if ( log.isTraceEnabled() ) {
- log.trace(
- "saving " +
- MessageHelper.infoString( persister, id, source.getFactory() )
- );
+ log.trace(
+ "saving " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
+ );
}
-
+
EntityKey key;
if ( !useIdentityColumn ) {
key = new EntityKey( id, persister, source.getEntityMode() );
- Object old = source.getPersistenceContext().getEntity(key);
- if (old != null) {
- if ( source.getPersistenceContext().getEntry(old).getStatus() == Status.DELETED ) {
- source.forceFlush( source.getPersistenceContext().getEntry(old) );
+ Object old = source.getPersistenceContext().getEntity( key );
+ if ( old != null ) {
+ if ( source.getPersistenceContext().getEntry( old ).getStatus() == Status.DELETED ) {
+ source.forceFlush( source.getPersistenceContext().getEntry( old ) );
}
else {
throw new NonUniqueObjectException( id, persister.getEntityName() );
}
}
- persister.setIdentifier(entity, id, source.getEntityMode());
+ persister.setIdentifier( entity, id, source.getEntityMode() );
}
else {
key = null;
}
-
- if ( invokeSaveLifecycle(entity, persister, source) ) {
+
+ if ( invokeSaveLifecycle( entity, persister, source ) ) {
return id; //EARLY EXIT
}
return performSaveOrReplicate(
- entity,
- key,
- persister,
- useIdentityColumn,
- anything,
+ entity,
+ key,
+ persister,
+ useIdentityColumn,
+ anything,
source,
- requiresImmediateIdAccess
- );
+ requiresImmediateIdAccess
+ );
}
-
+
protected boolean invokeSaveLifecycle(Object entity, EntityPersister persister, EventSource source) {
// Sub-insertions should occur before containing insertion so
// Try to do the callback now
if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
log.debug( "calling onSave()" );
- if ( ( (Lifecycle) entity ).onSave(source) ) {
+ if ( ( ( Lifecycle ) entity ).onSave( source ) ) {
log.debug( "insertion vetoed by onSave()" );
return true;
}
@@ -203,21 +204,25 @@
protected void validate(Object entity, EntityPersister persister, EventSource source) {
if ( persister.implementsValidatable( source.getEntityMode() ) ) {
- ( ( Validatable ) entity ).validate();
- }
+ ( ( Validatable ) entity ).validate();
+ }
}
/**
* Performs all the actual work needed to save an entity (well to get the save moved to
* the execution queue).
+ *
* @param entity The entity to be saved
* @param key The id to be used for saving the entity (or null, in the case of identity columns)
* @param persister The entity's persister instance.
* @param useIdentityColumn Should an identity column be used for id generation?
+ * @param anything Generally cascade-specific information.
* @param source The session which is the source of the current event.
+ * @param requiresImmediateIdAccess Is access to the identifier required immediately
+ * after the completion of the save? persist(), for example, does not require this...
+ *
* @return The id used to save the entity; may be null depending on the
- * type of id generator used and the requiresImmediateIdAccess value
- * @throws HibernateException
+ * type of id generator used and the requiresImmediateIdAccess value
*/
protected Serializable performSaveOrReplicate(
Object entity,
@@ -226,18 +231,17 @@
boolean useIdentityColumn,
Object anything,
EventSource source,
- boolean requiresImmediateIdAccess)
- throws HibernateException {
-
+ boolean requiresImmediateIdAccess) {
+
validate( entity, persister, source );
- Serializable id = key==null ? null : key.getIdentifier();
+ Serializable id = key == null ? null : key.getIdentifier();
boolean inTxn = source.getJDBCContext().isTransactionInProgress();
boolean shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;
if ( useIdentityColumn && !shouldDelayIdentityInserts ) {
- log.trace("executing insertions");
+ log.trace( "executing insertions" );
source.getActionQueue().executeInserts();
}
@@ -253,46 +257,50 @@
null,
LockMode.WRITE,
useIdentityColumn,
- persister,
- false,
+ persister,
+ false,
false
- );
+ );
- cascadeBeforeSave(source, persister, entity, anything);
+ cascadeBeforeSave( source, persister, entity, anything );
- Object[] values = persister.getPropertyValuesToInsert(entity, getMergeMap(anything), source);
+ Object[] values = persister.getPropertyValuesToInsert( entity, getMergeMap( anything ), source );
Type[] types = persister.getPropertyTypes();
- boolean substitute = substituteValuesIfNecessary(entity, id, values, persister, source);
+ boolean substitute = substituteValuesIfNecessary( entity, id, values, persister, source );
if ( persister.hasCollections() ) {
- substitute = substitute || visitCollectionsBeforeSave(id, values, types, source);
+ substitute = substitute || visitCollectionsBeforeSave( entity, id, values, types, source );
}
- if (substitute) persister.setPropertyValues( entity, values, source.getEntityMode() );
+ if ( substitute ) {
+ persister.setPropertyValues( entity, values, source.getEntityMode() );
+ }
- TypeFactory.deepCopy(
- values,
- types,
- persister.getPropertyUpdateability(),
- values,
+ TypeFactory.deepCopy(
+ values,
+ types,
+ persister.getPropertyUpdateability(),
+ values,
source
+ );
+
+ new ForeignKeys.Nullifier( entity, false, useIdentityColumn, source )
+ .nullifyTransientReferences( values, types );
+ new Nullability( source ).checkNullability( values, persister, false );
+
+ if ( useIdentityColumn ) {
+ EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
+ values, entity, persister, source, shouldDelayIdentityInserts
);
-
- new ForeignKeys.Nullifier(entity, false, useIdentityColumn, source)
- .nullifyTransientReferences(values, types);
- new Nullability(source).checkNullability( values, persister, false );
-
- if ( useIdentityColumn ) {
- EntityIdentityInsertAction insert = new EntityIdentityInsertAction( values, entity, persister, source, shouldDelayIdentityInserts );
- if ( ! shouldDelayIdentityInserts ) {
+ if ( !shouldDelayIdentityInserts ) {
log.debug( "executing identity-insert immediately" );
- source.getActionQueue().execute(insert);
+ source.getActionQueue().execute( insert );
id = insert.getGeneratedId();
//now done in EntityIdentityInsertAction
//persister.setIdentifier( entity, id, source.getEntityMode() );
key = new EntityKey( id, persister, source.getEntityMode() );
- source.getPersistenceContext().checkUniqueness(key, entity);
+ source.getPersistenceContext().checkUniqueness( key, entity );
//source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
}
else {
@@ -302,7 +310,7 @@
}
}
- Object version = Versioning.getVersion(values, persister);
+ Object version = Versioning.getVersion( values, persister );
source.getPersistenceContext().addEntity(
entity,
Status.MANAGED,
@@ -312,18 +320,18 @@
LockMode.WRITE,
useIdentityColumn,
persister,
- isVersionIncrementDisabled(),
+ isVersionIncrementDisabled(),
false
- );
+ );
//source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );
if ( !useIdentityColumn ) {
- source.getActionQueue().addAction(
- new EntityInsertAction(id, values, entity, version, persister, source)
- );
+ source.getActionQueue().addAction(
+ new EntityInsertAction( id, values, entity, version, persister, source )
+ );
}
- cascadeAfterSave(source, persister, entity, anything);
+ cascadeAfterSave( source, persister, entity, anything );
markInterceptorDirty( entity, persister, source );
@@ -333,7 +341,7 @@
private void markInterceptorDirty(Object entity, EntityPersister persister, EventSource source) {
if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
FieldInterceptor interceptor = FieldInterceptionHelper.injectFieldInterceptor(
- entity,
+ entity,
persister.getEntityName(),
null,
source
@@ -341,44 +349,55 @@
interceptor.dirty();
}
}
-
+
protected Map getMergeMap(Object anything) {
return null;
}
-
+
/**
* After the save, will te version number be incremented
* if the instance is modified?
+ *
+ * @return True if the version will be incremented on an entity change after save;
+ * false otherwise.
*/
protected boolean isVersionIncrementDisabled() {
return false;
}
-
- protected boolean visitCollectionsBeforeSave(Serializable id, Object[] values, Type[] types, EventSource source) {
- WrapVisitor visitor = new WrapVisitor(source);
+
+ protected boolean visitCollectionsBeforeSave(Object entity, Serializable id, Object[] values, Type[] types, EventSource source) {
+ WrapVisitor visitor = new WrapVisitor( source );
// substitutes into values by side-effect
- visitor.processEntityPropertyValues(values, types);
+ visitor.processEntityPropertyValues( values, types );
return visitor.isSubstitutionRequired();
}
-
+
/**
* Perform any property value substitution that is necessary
* (interceptor callback, version initialization...)
+ *
+ * @param entity The entity
+ * @param id The entity identifier
+ * @param values The snapshot entity state
+ * @param persister The entity persister
+ * @param source The originating session
+ *
+ * @return True if the snapshot state changed such that
+ * reinjection of the values into the entity is required.
*/
protected boolean substituteValuesIfNecessary(
- Object entity,
- Serializable id,
- Object[] values,
+ Object entity,
+ Serializable id,
+ Object[] values,
EntityPersister persister,
- SessionImplementor source
- ) {
+ SessionImplementor source) {
boolean substitute = source.getInterceptor().onSave(
entity,
id,
values,
persister.getPropertyNames(),
persister.getPropertyTypes()
- );
+ );
//keep the existing version number in the case of replicate!
if ( persister.isVersioned() ) {
@@ -386,7 +405,7 @@
values,
persister.getVersionProperty(),
persister.getVersionType(),
- source
+ source
) || substitute;
}
return substitute;
@@ -394,23 +413,23 @@
/**
* Handles the calls needed to perform pre-save cascades for the given entity.
+ *
* @param source The session from whcih the save event originated.
* @param persister The entity's persister instance.
* @param entity The entity to be saved.
- * @throws HibernateException
+ * @param anything Generally cascade-specific data
*/
protected void cascadeBeforeSave(
EventSource source,
EntityPersister persister,
Object entity,
- Object anything)
- throws HibernateException {
+ Object anything) {
// cascade-save to many-to-one BEFORE the parent is saved
source.getPersistenceContext().incrementCascadeLevel();
try {
new Cascade( getCascadeAction(), Cascade.BEFORE_INSERT_AFTER_DELETE, source )
- .cascade(persister, entity, anything);
+ .cascade( persister, entity, anything );
}
finally {
source.getPersistenceContext().decrementCascadeLevel();
@@ -419,96 +438,105 @@
/**
* Handles to calls needed to perform post-save cascades.
+ *
* @param source The session from which the event originated.
* @param persister The entity's persister instance.
* @param entity The entity beng saved.
- * @throws HibernateException
+ * @param anything Generally cascade-specific data
*/
protected void cascadeAfterSave(
EventSource source,
- EntityPersister persister,
- Object entity,
- Object anything)
- throws HibernateException {
+ EntityPersister persister,
+ Object entity,
+ Object anything) {
// cascade-save to collections AFTER the collection owner was saved
source.getPersistenceContext().incrementCascadeLevel();
try {
new Cascade( getCascadeAction(), Cascade.AFTER_INSERT_BEFORE_DELETE, source )
- .cascade(persister, entity, anything);
+ .cascade( persister, entity, anything );
}
finally {
source.getPersistenceContext().decrementCascadeLevel();
}
}
-
+
protected abstract CascadingAction getCascadeAction();
-
+
/**
* Determine whether the entity is persistent, detached, or transient
+ *
+ * @param entity The entity to check
+ * @param entityName The name of the entity
+ * @param entry The entity's entry in the persistence context
+ * @param source The originating session.
+ *
+ * @return The state.
*/
protected int getEntityState(
- Object entity,
- String entityName,
+ Object entity,
+ String entityName,
EntityEntry entry, //pass this as an argument only to avoid double looking
SessionImplementor source) {
- if ( entry != null ) {
- // the object is persistent as it has an entry associated with the
- // session; so check its status
+ if ( entry != null ) { // the object is persistent
+
+ //the entity is associated with the session, so check its status
if ( entry.getStatus() != Status.DELETED ) {
// do nothing for persistent instances
if ( log.isTraceEnabled() ) {
- log.trace(
- "persistent instance of: " +
- getLoggableName(entityName, entity)
- );
+ log.trace(
+ "persistent instance of: " +
+ getLoggableName( entityName, entity )
+ );
}
return PERSISTENT;
}
- else {
+ else {
//ie. e.status==DELETED
if ( log.isTraceEnabled() ) {
- log.trace(
- "deleted instance of: " +
- getLoggableName(entityName, entity)
- );
+ log.trace(
+ "deleted instance of: " +
+ getLoggableName( entityName, entity )
+ );
}
return DELETED;
}
-
+
}
- else {
- // the entity is not associated with the session, so the various
- // unsaved-value algorithms to determine whether it is transient
- // or detached
+ else { // the object is transient or detached
+
+ //the entity is not associated with the session, so
+ //try interceptor and unsaved-value
+
if ( ForeignKeys.isTransient( entityName, entity, getAssumedUnsaved(), source ) ) {
if ( log.isTraceEnabled() ) {
- log.trace(
- "transient instance of: " +
- getLoggableName(entityName, entity)
- );
+ log.trace(
+ "transient instance of: " +
+ getLoggableName( entityName, entity )
+ );
}
return TRANSIENT;
}
else {
if ( log.isTraceEnabled() ) {
- log.trace(
- "detached instance of: " +
- getLoggableName(entityName, entity)
- );
+ log.trace(
+ "detached instance of: " +
+ getLoggableName( entityName, entity )
+ );
}
return DETACHED;
}
}
}
-
+
protected String getLoggableName(String entityName, Object entity) {
- return entityName==null ? entity.getClass().getName() : entityName;
+ return entityName == null ? entity.getClass().getName() : entityName;
}
-
+
protected Boolean getAssumedUnsaved() {
return null;
}
+
}
Modified: trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -6,6 +6,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.hibernate.CacheMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
@@ -38,17 +39,27 @@
*/
public class DefaultDeleteEventListener implements DeleteEventListener {
- private static final Log log = LogFactory.getLog(DefaultDeleteEventListener.class);
+ private static final Log log = LogFactory.getLog( DefaultDeleteEventListener.class );
- /** Handle the given delete event.
- *
- * @param event The delete event to be handled.
- * @throws HibernateException
- */
+ /**
+ * Handle the given delete event.
+ *
+ * @param event The delete event to be handled.
+ *
+ * @throws HibernateException
+ */
public void onDelete(DeleteEvent event) throws HibernateException {
onDelete( event, new IdentitySet() );
}
+ /**
+ * Handle the given delete event. This is the cascaded form.
+ *
+ * @param event The delete event.
+ * @param transientEntities The cache of entities already deleted
+ *
+ * @throws HibernateException
+ */
public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {
final EventSource source = event.getSession();
@@ -79,15 +90,15 @@
if ( id == null ) {
throw new TransientObjectException(
- "the detached instance passed to delete() had a null identifier"
+ "the detached instance passed to delete() had a null identifier"
);
}
EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
- persistenceContext.checkUniqueness(key, entity);
+ persistenceContext.checkUniqueness( key, entity );
- new OnUpdateVisitor( source, id ).process( entity, persister );
+ new OnUpdateVisitor( source, id, entity ).process( entity, persister );
version = persister.getVersion( entity, source.getEntityMode() );
@@ -102,7 +113,7 @@
persister,
false,
false
- );
+ );
}
else {
log.trace( "deleting a persistent instance" );
@@ -123,7 +134,9 @@
);
}*/
- if ( invokeDeleteLifecycle( source, entity, persister ) ) return;
+ if ( invokeDeleteLifecycle( source, entity, persister ) ) {
+ return;
+ }
deleteEntity( source, entity, entityEntry, event.isCascadeDeleteEnabled(), persister, transientEntities );
@@ -132,6 +145,14 @@
}
}
+ /**
+ * Called when we have recognized an attempt to delete a detached entity.
+ * <p/>
+ * This is perfectly valid in Hibernate usage; JPA, however, forbids this.
+ * Thus, this is a hook for HEM to affect this behavior.
+ *
+ * @param event The event.
+ */
protected void performDetachedEntityDeletionCheck(DeleteEvent event) {
// ok in normal Hibernate usage to delete a detached entity; JPA however
// forbids it, thus this is a hook for HEM to affect this behavior
@@ -168,19 +189,31 @@
cascadeAfterDelete( session, persister, entity, transientEntities );
}
+ /**
+ * Perform the entity deletion. Well, as with most operations, does not
+ * really perform it; just schedules an action/execution with the
+ * {@link org.hibernate.engine.ActionQueue} for execution during flush.
+ *
+ * @param session The originating session
+ * @param entity The entity to delete
+ * @param entityEntry The entity's entry in the {@link PersistenceContext}
+ * @param isCascadeDeleteEnabled Is delete cascading enabled?
+ * @param persister The entity persister.
+ * @param transientEntities A cache of already deleted entities.
+ */
protected final void deleteEntity(
- final EventSource session,
- final Object entity,
- final EntityEntry entityEntry,
- final boolean isCascadeDeleteEnabled,
- final EntityPersister persister,
- final Set transientEntities) throws HibernateException {
+ final EventSource session,
+ final Object entity,
+ final EntityEntry entityEntry,
+ final boolean isCascadeDeleteEnabled,
+ final EntityPersister persister,
+ final Set transientEntities) {
if ( log.isTraceEnabled() ) {
log.trace(
"deleting " +
- MessageHelper.infoString( persister, entityEntry.getId(), session.getFactory() )
- );
+ MessageHelper.infoString( persister, entityEntry.getId(), session.getFactory() )
+ );
}
final PersistenceContext persistenceContext = session.getPersistenceContext();
@@ -196,7 +229,7 @@
}
final Object[] deletedState = createDeletedState( persister, currentState, session );
- entityEntry.setDeletedState(deletedState);
+ entityEntry.setDeletedState( deletedState );
session.getInterceptor().onDelete(
entity,
@@ -207,15 +240,15 @@
);
// before any callbacks, etc, so subdeletions see that this deletion happened first
- persistenceContext.setEntryStatus(entityEntry, Status.DELETED);
- EntityKey key = new EntityKey( entityEntry.getId(), persister, session.getEntityMode() );
+ persistenceContext.setEntryStatus( entityEntry, Status.DELETED );
+ EntityKey key = new EntityKey( entityEntry.getId(), persister, session.getEntityMode() );
cascadeBeforeDelete( session, persister, entity, entityEntry, transientEntities );
- new ForeignKeys.Nullifier(entity, true, false, session)
+ new ForeignKeys.Nullifier( entity, true, false, session )
.nullifyTransientReferences( entityEntry.getDeletedState(), propTypes );
- new Nullability(session).checkNullability( entityEntry.getDeletedState(), persister, true );
- persistenceContext.getNullifiableEntityKeys().add(key);
+ new Nullability( session ).checkNullability( entityEntry.getDeletedState(), persister, true );
+ persistenceContext.getNullifiableEntityKeys().add( key );
// Ensures that containing deletions happen before sub-deletions
session.getActionQueue().addAction(
@@ -251,8 +284,8 @@
protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) {
if ( persister.implementsLifecycle( session.getEntityMode() ) ) {
log.debug( "calling onDelete()" );
- if ( ( (Lifecycle) entity ).onDelete(session) ) {
- log.debug("deletion vetoed by onDelete()");
+ if ( ( ( Lifecycle ) entity ).onDelete( session ) ) {
+ log.debug( "deletion vetoed by onDelete()" );
return true;
}
}
@@ -261,13 +294,13 @@
protected void cascadeBeforeDelete(
EventSource session,
- EntityPersister persister,
- Object entity,
- EntityEntry entityEntry,
- Set transientEntities) throws HibernateException {
+ EntityPersister persister,
+ Object entity,
+ EntityEntry entityEntry,
+ Set transientEntities) throws HibernateException {
CacheMode cacheMode = session.getCacheMode();
- session.setCacheMode(CacheMode.GET);
+ session.setCacheMode( CacheMode.GET );
session.getPersistenceContext().incrementCascadeLevel();
try {
// cascade-delete to collections BEFORE the collection owner is deleted
@@ -276,18 +309,18 @@
}
finally {
session.getPersistenceContext().decrementCascadeLevel();
- session.setCacheMode(cacheMode);
+ session.setCacheMode( cacheMode );
}
}
protected void cascadeAfterDelete(
EventSource session,
- EntityPersister persister,
- Object entity,
- Set transientEntities) throws HibernateException {
+ EntityPersister persister,
+ Object entity,
+ Set transientEntities) throws HibernateException {
CacheMode cacheMode = session.getCacheMode();
- session.setCacheMode(CacheMode.GET);
+ session.setCacheMode( CacheMode.GET );
session.getPersistenceContext().incrementCascadeLevel();
try {
// cascade-delete to many-to-one AFTER the parent was deleted
@@ -296,7 +329,7 @@
}
finally {
session.getPersistenceContext().decrementCascadeLevel();
- session.setCacheMode(cacheMode);
+ session.setCacheMode( cacheMode );
}
}
Modified: trunk/Hibernate3/src/org/hibernate/event/def/DefaultReplicateEventListener.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/DefaultReplicateEventListener.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/DefaultReplicateEventListener.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -30,44 +30,43 @@
*/
public class DefaultReplicateEventListener extends AbstractSaveEventListener implements ReplicateEventListener {
- private static final Log log = LogFactory.getLog(DefaultReplicateEventListener.class);
+ private static final Log log = LogFactory.getLog( DefaultReplicateEventListener.class );
- /**
+ /**
* Handle the given replicate event.
*
* @param event The replicate event to be handled.
- * @throws HibernateException
+ *
+ * @throws TransientObjectException An invalid attempt to replicate a transient entity.
*/
- public void onReplicate(ReplicateEvent event) throws HibernateException {
-
+ public void onReplicate(ReplicateEvent event) {
final EventSource source = event.getSession();
-
if ( source.getPersistenceContext().reassociateIfUninitializedProxy( event.getObject() ) ) {
- log.trace("uninitialized proxy passed to replicate()");
+ 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()");
+ 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()");
+ 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
@@ -75,14 +74,16 @@
}
else {
//what is the version on the database?
- oldVersion = persister.getCurrentVersion(id, source);
+ oldVersion = persister.getCurrentVersion( id, source );
}
- if ( oldVersion!=null ) {
+ 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() ) );
+ log.trace(
+ "found existing row for " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
+ );
}
boolean canReplicate = replicationMode.shouldOverwriteCurrentVersion(
@@ -92,26 +93,28 @@
persister.getVersionType()
);
- if (canReplicate) {
+ if ( canReplicate ) {
//will result in a SQL UPDATE:
- performReplication(entity, id, oldVersion, persister, replicationMode, source);
+ performReplication( entity, id, oldVersion, persister, replicationMode, source );
}
else {
//else do nothing (don't even reassociate object!)
- log.trace("no need to replicate");
+ 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() ) );
+ 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 ?
+ final EntityKey key = regenerate ?
null : new EntityKey( id, persister, source.getEntityMode() );
performSaveOrReplicate(
@@ -121,77 +124,74 @@
regenerate,
replicationMode,
source,
- true
+ true
);
}
}
- protected boolean visitCollectionsBeforeSave(Serializable id, Object[] values, Type[] types, EventSource source) {
+ protected boolean visitCollectionsBeforeSave(Object entity, 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);
+ OnReplicateVisitor visitor = new OnReplicateVisitor( source, id, entity, false );
+ visitor.processEntityPropertyValues( values, types );
+ return super.visitCollectionsBeforeSave( entity, id, values, types, source );
}
protected boolean substituteValuesIfNecessary(
- Object entity,
- Serializable id,
- Object[] values,
+ Object entity,
+ Serializable id,
+ Object[] values,
EntityPersister persister,
- SessionImplementor source
- ) {
+ SessionImplementor source) {
return false;
}
-
+
protected boolean isVersionIncrementDisabled() {
return true;
}
-
- private final void performReplication(
+
+ private void performReplication(
Object entity,
Serializable id,
Object version,
EntityPersister persister,
ReplicationMode replicationMode,
- EventSource source)
- throws HibernateException {
+ EventSource source) throws HibernateException {
if ( log.isTraceEnabled() ) {
- log.trace(
- "replicating changes to " +
- MessageHelper.infoString( persister, id, source.getFactory() )
+ log.trace(
+ "replicating changes to " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
);
}
- new OnReplicateVisitor(source, id, true).process( entity, persister );
+ new OnReplicateVisitor( source, id, entity, true ).process( entity, persister );
- source.getPersistenceContext().addEntity(
- entity,
- Status.MANAGED,
- null,
- new EntityKey( id, persister, source.getEntityMode() ),
- version,
- LockMode.NONE,
- true,
- persister,
- true,
+ 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
- ) {
+ Object entity,
+ EntityPersister persister,
+ ReplicationMode replicationMode,
+ EventSource source) {
source.getPersistenceContext().incrementCascadeLevel();
try {
- new Cascade(CascadingAction.REPLICATE, Cascade.AFTER_UPDATE, source)
- .cascade(persister, entity, replicationMode);
+ new Cascade( CascadingAction.REPLICATE, Cascade.AFTER_UPDATE, source )
+ .cascade( persister, entity, replicationMode );
}
finally {
source.getPersistenceContext().decrementCascadeLevel();
Modified: trunk/Hibernate3/src/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -5,6 +5,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
@@ -27,218 +28,240 @@
import org.hibernate.proxy.HibernateProxy;
/**
- * Defines the default update event listener used by hibernate for updating
- * transient entities in response to generated update events.
+ * Defines the default listener used by Hibernate for handling save-update
+ * events.
*
- * @author Steve Ebersole, Gavin King
+ * @author Steve Ebersole
+ * @author Gavin King
*/
public class DefaultSaveOrUpdateEventListener extends AbstractSaveEventListener implements SaveOrUpdateEventListener {
- private static final Log log = LogFactory.getLog(DefaultSaveOrUpdateEventListener.class);
+ private static final Log log = LogFactory.getLog( DefaultSaveOrUpdateEventListener.class );
- /**
+ /**
* Handle the given update event.
*
* @param event The update event to be handled.
- * @throws HibernateException
*/
- public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException {
-
+ public void onSaveOrUpdate(SaveOrUpdateEvent event) {
final SessionImplementor source = event.getSession();
final Object object = event.getObject();
+ final Serializable requestedId = event.getRequestedId();
- final Serializable requestedId = event.getRequestedId();
- if ( requestedId!=null ) {
- //assign the requested id to the proxy, *before*
+ if ( requestedId != null ) {
+ //assign the requested id to the proxy, *before*
//reassociating the proxy
if ( object instanceof HibernateProxy ) {
- ( (HibernateProxy) object ).getHibernateLazyInitializer().setIdentifier(requestedId);
+ ( ( HibernateProxy ) object ).getHibernateLazyInitializer().setIdentifier( requestedId );
}
}
-
- if ( reassociateIfUninitializedProxy(object, source) ) {
- log.trace("reassociated uninitialized proxy");
- // an uninitialized proxy, noop, don't even need to
+
+ if ( reassociateIfUninitializedProxy( object, source ) ) {
+ log.trace( "reassociated uninitialized proxy" );
+ // an uninitialized proxy, noop, don't even need to
// return an id, since it is never a save()
}
else {
//initialize properties of the event:
- final Object entity = source.getPersistenceContext().unproxyAndReassociate(object);
- event.setEntity(entity);
- event.setEntry( source.getPersistenceContext().getEntry(entity) );
+ final Object entity = source.getPersistenceContext().unproxyAndReassociate( object );
+ event.setEntity( entity );
+ event.setEntry( source.getPersistenceContext().getEntry( entity ) );
//return the id in the event object
- event.setResultId( performSaveOrUpdate(event) );
+ event.setResultId( performSaveOrUpdate( event ) );
}
-
+
}
-
+
protected boolean reassociateIfUninitializedProxy(Object object, SessionImplementor source) {
- return source.getPersistenceContext().reassociateIfUninitializedProxy(object);
+ return source.getPersistenceContext().reassociateIfUninitializedProxy( object );
}
-
+
protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
-
- // use various roles to determine if the instance is
- // transient, persistent or detached:
-
- int entityState = getEntityState(
- event.getEntity(),
- event.getEntityName(),
- event.getEntry(),
- event.getSession()
- );
-
- switch (entityState) {
- case DETACHED:
- entityIsDetached(event);
+ int entityState = getEntityState(
+ event.getEntity(),
+ event.getEntityName(),
+ event.getEntry(),
+ event.getSession()
+ );
+
+ switch ( entityState ) {
+ case DETACHED:
+ entityIsDetached( event );
return null;
case PERSISTENT:
- return entityIsPersistent(event);
+ return entityIsPersistent( event );
default: //TRANSIENT or DELETED
- return entityIsTransient(event);
+ return entityIsTransient( event );
}
-
}
-
+
protected Serializable entityIsPersistent(SaveOrUpdateEvent event) throws HibernateException {
-
- log.trace("ignoring persistent instance");
-
+ log.trace( "ignoring persistent instance" );
+
EntityEntry entityEntry = event.getEntry();
- if ( entityEntry==null ) {
- throw new AssertionFailure("entity was transient or detached");
+ if ( entityEntry == null ) {
+ throw new AssertionFailure( "entity was transient or detached" );
}
else {
-
+
if ( entityEntry.getStatus() == Status.DELETED ) {
- throw new AssertionFailure("entity was deleted");
+ throw new AssertionFailure( "entity was deleted" );
}
-
+
final SessionFactoryImplementor factory = event.getSession().getFactory();
Serializable requestedId = event.getRequestedId();
-
+
Serializable savedId;
if ( requestedId == null ) {
savedId = entityEntry.getId();
}
else {
-
+
final boolean isEqual = !entityEntry.getPersister().getIdentifierType()
.isEqual( requestedId, entityEntry.getId(), event.getSession().getEntityMode(), factory );
-
+
if ( isEqual ) {
throw new PersistentObjectException(
"object passed to save() was already persistent: " +
- MessageHelper.infoString( entityEntry.getPersister(), requestedId, factory )
- );
+ MessageHelper.infoString( entityEntry.getPersister(), requestedId, factory )
+ );
}
-
+
savedId = requestedId;
-
+
}
-
+
if ( log.isTraceEnabled() ) {
- log.trace(
- "object already associated with session: " +
- MessageHelper.infoString( entityEntry.getPersister(), savedId, factory )
- );
+ log.trace(
+ "object already associated with session: " +
+ MessageHelper.infoString( entityEntry.getPersister(), savedId, factory )
+ );
}
-
+
return savedId;
-
+
}
}
-
- /**
- * Handle the given save event.
+
+ /**
+ * The given save-update event named a transient entity.
+ * <p/>
+ * Here, we will perform the save processing.
*
* @param event The save event to be handled.
- * @throws HibernateException
+ *
+ * @return The entity's identifier after saving.
*/
- protected Serializable entityIsTransient(SaveOrUpdateEvent event) throws HibernateException {
-
- log.trace("saving transient instance");
+ protected Serializable entityIsTransient(SaveOrUpdateEvent event) {
- final EventSource source = event.getSession();
+ log.trace( "saving transient instance" );
+ final EventSource source = event.getSession();
+
EntityEntry entityEntry = event.getEntry();
if ( entityEntry != null ) {
if ( entityEntry.getStatus() == Status.DELETED ) {
- source.forceFlush(entityEntry);
+ source.forceFlush( entityEntry );
}
else {
- throw new AssertionFailure("entity was persistent");
+ throw new AssertionFailure( "entity was persistent" );
}
}
-
- Serializable id = saveWithGeneratedOrRequestedId(event);
+ Serializable id = saveWithGeneratedOrRequestedId( event );
+
source.getPersistenceContext().reassociateProxy( event.getObject(), id );
-
+
return id;
}
-
+
/**
* Save the transient instance, assigning the right identifier
+ *
+ * @param event The initiating event.
+ *
+ * @return The entity's identifier value after saving.
*/
protected Serializable saveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) {
return saveWithGeneratedId(
- event.getEntity(),
- event.getEntityName(),
- null,
+ event.getEntity(),
+ event.getEntityName(),
+ null,
event.getSession(),
- true
+ true
);
}
-
- /**
- * Handle the given update event.
+
+ /**
+ * The given save-update event named a detached entity.
+ * <p/>
+ * Here, we will perform the update processing.
*
* @param event The update event to be handled.
- * @throws HibernateException
*/
- protected void entityIsDetached(SaveOrUpdateEvent event) throws HibernateException {
-
- log.trace("updating detached instance");
-
-
- if ( event.getSession().getPersistenceContext().isEntryFor( event.getEntity() ) ) {
+ protected void entityIsDetached(SaveOrUpdateEvent event) {
+
+ log.trace( "updating detached instance" );
+
+
+ if ( event.getSession().getPersistenceContext().isEntryFor( event.getEntity() ) ) {
//TODO: assertion only, could be optimized away
- throw new AssertionFailure("entity was persistent");
+ throw new AssertionFailure( "entity was persistent" );
}
-
+
Object entity = event.getEntity();
EntityPersister persister = event.getSession().getEntityPersister( event.getEntityName(), entity );
- event.setRequestedId( getUpdateId( entity, persister, event.getRequestedId(), event.getSession().getEntityMode() ) );
-
- performUpdate(event, entity, persister);
+ event.setRequestedId(
+ getUpdateId(
+ entity, persister, event.getRequestedId(), event.getSession().getEntityMode()
+ )
+ );
+ performUpdate( event, entity, persister );
+
}
-
- protected Serializable getUpdateId(Object entity, EntityPersister persister, Serializable requestedId, EntityMode entityMode)
- throws HibernateException {
+
+ /**
+ * Determine the id to use for updating.
+ *
+ * @param entity The entity.
+ * @param persister The entity persister
+ * @param requestedId The requested identifier
+ * @param entityMode The entity mode.
+ *
+ * @return The id.
+ *
+ * @throws TransientObjectException If the entity is considered transient.
+ */
+ protected Serializable getUpdateId(
+ Object entity,
+ EntityPersister persister,
+ Serializable requestedId,
+ EntityMode entityMode) {
// use the id assigned to the instance
- Serializable id = persister.getIdentifier(entity, entityMode);
- if ( id==null ) {
+ Serializable id = persister.getIdentifier( entity, entityMode );
+ if ( id == null ) {
// assume this is a newly instantiated transient object
// which should be saved rather than updated
throw new TransientObjectException(
"The given object has a null identifier: " +
- persister.getEntityName()
- );
+ persister.getEntityName()
+ );
}
else {
return id;
}
-
+
}
- protected void performUpdate(SaveOrUpdateEvent event, Object entity, EntityPersister persister)
- throws HibernateException {
-
+ protected void performUpdate(
+ SaveOrUpdateEvent event,
+ Object entity,
+ EntityPersister persister) throws HibernateException {
+
if ( !persister.isMutable() ) {
log.trace( "immutable instance passed to doUpdate(), locking" );
reassociate( event, entity, event.getRequestedId(), persister );
@@ -246,27 +269,29 @@
else {
if ( log.isTraceEnabled() ) {
- log.trace(
- "updating " +
- MessageHelper.infoString( persister, event.getRequestedId(), event.getSession().getFactory() )
- );
+ log.trace(
+ "updating " +
+ MessageHelper.infoString(
+ persister, event.getRequestedId(), event.getSession().getFactory()
+ )
+ );
}
-
+
final EventSource source = event.getSession();
-
+
EntityKey key = new EntityKey( event.getRequestedId(), persister, source.getEntityMode() );
-
+
source.getPersistenceContext().checkUniqueness( key, entity );
-
+
if ( invokeUpdateLifecycle( entity, persister, source ) ) {
reassociate( event, event.getObject(), event.getRequestedId(), persister );
return;
}
-
+
// this is a transient object with existing persistent state not loaded by the session
-
- new OnUpdateVisitor( source, event.getRequestedId() ).process( entity, persister );
-
+
+ new OnUpdateVisitor( source, event.getRequestedId(), entity ).process( entity, persister );
+
//TODO: put this stuff back in to read snapshot from
// the second-level cache (needs some extra work)
/*Object[] cachedState = null;
@@ -274,8 +299,8 @@
if ( persister.hasCache() ) {
CacheEntry entry = (CacheEntry) persister.getCache()
.get( event.getRequestedId(), source.getTimestamp() );
- cachedState = entry==null ?
- null :
+ cachedState = entry==null ?
+ null :
entry.getState(); //TODO: half-assemble this stuff
}*/
@@ -288,28 +313,28 @@
LockMode.NONE,
true,
persister,
- false,
+ false,
true //assume true, since we don't really know, and it doesn't matter
+ );
+
+ persister.afterReassociate( entity, source );
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "updating " +
+ MessageHelper.infoString( persister, event.getRequestedId(), source.getFactory() )
);
-
- persister.afterReassociate(entity, source);
-
- if ( log.isTraceEnabled() ) {
- log.trace(
- "updating " +
- MessageHelper.infoString( persister, event.getRequestedId(), source.getFactory() )
- );
}
-
- cascadeOnUpdate(event, persister, entity);
-
+
+ cascadeOnUpdate( event, persister, entity );
+
}
}
protected boolean invokeUpdateLifecycle(Object entity, EntityPersister persister, EventSource source) {
if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
log.debug( "calling onUpdate()" );
- if ( ( ( Lifecycle ) entity ).onUpdate(source) ) {
+ if ( ( ( Lifecycle ) entity ).onUpdate( source ) ) {
log.debug( "update vetoed by onUpdate()" );
return true;
}
@@ -329,8 +354,8 @@
EventSource source = event.getSession();
source.getPersistenceContext().incrementCascadeLevel();
try {
- new Cascade(CascadingAction.SAVE_UPDATE, Cascade.AFTER_UPDATE, source)
- .cascade(persister, entity);
+ new Cascade( CascadingAction.SAVE_UPDATE, Cascade.AFTER_UPDATE, source )
+ .cascade( persister, entity );
}
finally {
source.getPersistenceContext().decrementCascadeLevel();
Modified: trunk/Hibernate3/src/org/hibernate/event/def/OnLockVisitor.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/OnLockVisitor.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/OnLockVisitor.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -14,60 +14,55 @@
* When a transient entity is passed to lock(), we must inspect all its collections and
* 1. associate any uninitialized PersistentCollections with this session
* 2. associate any initialized PersistentCollections with this session, using the
- * existing snapshot
+ * existing snapshot
* 3. throw an exception for each "new" collection
*
* @author Gavin King
*/
public class OnLockVisitor extends ReattachVisitor {
- public OnLockVisitor(EventSource session, Serializable key) {
- super(session, key);
+ public OnLockVisitor(EventSource session, Serializable key, Object owner) {
+ super( session, key, owner );
}
- Object processCollection(Object collection, CollectionType type)
- throws HibernateException {
+ Object processCollection(Object collection, CollectionType type) throws HibernateException {
SessionImplementor session = getSession();
CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() );
- if (collection==null) {
+ if ( collection == null ) {
//do nothing
}
else if ( collection instanceof PersistentCollection ) {
- PersistentCollection persistentCollection = (PersistentCollection) collection;
-
- if ( persistentCollection.setCurrentSession(session) ) {
-
- if ( isOwnerUnchanged( persistentCollection, persister, getKey() ) ) {
+ PersistentCollection persistentCollection = ( PersistentCollection ) collection;
+ if ( persistentCollection.setCurrentSession( session ) ) {
+ if ( isOwnerUnchanged( persistentCollection, persister, extractCollectionKeyFromOwner( persister ) ) ) {
// a "detached" collection that originally belonged to the same entity
if ( persistentCollection.isDirty() ) {
- throw new HibernateException("reassociated object has dirty collection");
+ throw new HibernateException( "reassociated object has dirty collection" );
}
- reattachCollection(persistentCollection, type);
+ reattachCollection( persistentCollection, type );
}
else {
// a "detached" collection that belonged to a different entity
- throw new HibernateException("reassociated object has dirty collection reference");
+ throw new HibernateException( "reassociated object has dirty collection reference" );
}
-
}
else {
// a collection loaded in the current session
// can not possibly be the collection belonging
// to the entity passed to update()
- throw new HibernateException("reassociated object has dirty collection reference");
+ throw new HibernateException( "reassociated object has dirty collection reference" );
}
-
}
else {
// brand new collection
//TODO: or an array!! we can't lock objects with arrays now??
- throw new HibernateException("reassociated object has dirty collection reference (or an array)");
+ throw new HibernateException( "reassociated object has dirty collection reference (or an array)" );
}
return null;
}
-
+
}
Modified: trunk/Hibernate3/src/org/hibernate/event/def/OnReplicateVisitor.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/OnReplicateVisitor.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/OnReplicateVisitor.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -10,40 +10,43 @@
import org.hibernate.type.CollectionType;
/**
- * When an entity is passed to replicate(), and there is an existing row, we must
+ * When an entity is passed to replicate(), and there is an existing row, we must
* inspect all its collections and
* 1. associate any uninitialized PersistentCollections with this session
* 2. associate any initialized PersistentCollections with this session, using the
- * existing snapshot
+ * existing snapshot
* 3. execute a collection removal (SQL DELETE) for each null collection property
- * or "new" collection
+ * or "new" collection
*
* @author Gavin King
*/
public class OnReplicateVisitor extends ReattachVisitor {
-
+
private boolean isUpdate;
-
- OnReplicateVisitor(EventSource session, Serializable key, boolean isUpdate) {
- super(session, key);
+
+ OnReplicateVisitor(EventSource session, Serializable key, Object owner, boolean isUpdate) {
+ super( session, key, owner );
this.isUpdate = isUpdate;
}
Object processCollection(Object collection, CollectionType type)
- throws HibernateException {
-
- if (collection==CollectionType.UNFETCHED_COLLECTION) return null;
+ throws HibernateException {
+ if ( collection == CollectionType.UNFETCHED_COLLECTION ) {
+ return null;
+ }
+
EventSource session = getSession();
- Serializable key = getKey();
CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() );
- if (isUpdate) removeCollection(persister, key, session);
- if ( collection!=null && (collection instanceof PersistentCollection) ) {
- PersistentCollection wrapper = (PersistentCollection) collection;
- wrapper.setCurrentSession(session);
+ if ( isUpdate ) {
+ removeCollection( persister, extractCollectionKeyFromOwner( persister ), session );
+ }
+ if ( collection != null && ( collection instanceof PersistentCollection ) ) {
+ PersistentCollection wrapper = ( PersistentCollection ) collection;
+ wrapper.setCurrentSession( session );
if ( wrapper.wasInitialized() ) {
- session.getPersistenceContext().addNewCollection(persister, wrapper);
+ session.getPersistenceContext().addNewCollection( persister, wrapper );
}
else {
reattachCollection( wrapper, type );
Modified: trunk/Hibernate3/src/org/hibernate/event/def/OnUpdateVisitor.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/OnUpdateVisitor.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/OnUpdateVisitor.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -21,51 +21,49 @@
*/
public class OnUpdateVisitor extends ReattachVisitor {
- OnUpdateVisitor(EventSource session, Serializable key) {
- super(session, key);
+ OnUpdateVisitor(EventSource session, Serializable key, Object owner) {
+ super( session, key, owner );
}
- Object processCollection(Object collection, CollectionType type)
- throws HibernateException {
-
- if (collection==CollectionType.UNFETCHED_COLLECTION) return null;
+ /**
+ * {@inheritDoc}
+ */
+ Object processCollection(Object collection, CollectionType type) throws HibernateException {
+ if ( collection == CollectionType.UNFETCHED_COLLECTION ) {
+ return null;
+ }
+
EventSource session = getSession();
- Serializable key = getKey();
CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() );
+ final Serializable collectionKey = extractCollectionKeyFromOwner( persister );
if ( collection!=null && (collection instanceof PersistentCollection) ) {
PersistentCollection wrapper = (PersistentCollection) collection;
-
if ( wrapper.setCurrentSession(session) ) {
//a "detached" collection!
-
- if ( !isOwnerUnchanged(wrapper, persister, key) ) {
+ if ( !isOwnerUnchanged( wrapper, persister, collectionKey ) ) {
// if the collection belonged to a different entity,
// clean up the existing state of the collection
- removeCollection(persister, key, session);
+ removeCollection( persister, collectionKey, session );
}
-
reattachCollection(wrapper, type);
}
else {
// a collection loaded in the current session
// can not possibly be the collection belonging
// to the entity passed to update()
- removeCollection(persister, key, session);
+ removeCollection(persister, collectionKey, session);
}
-
}
else {
// null or brand new collection
// this will also (inefficiently) handle arrays, which have
// no snapshot, so we can't do any better
- removeCollection(persister, key, session);
- //processArrayOrNewCollection(collection, type);
+ removeCollection(persister, collectionKey, session);
}
return null;
-
}
}
Modified: trunk/Hibernate3/src/org/hibernate/event/def/ReattachVisitor.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/ReattachVisitor.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/src/org/hibernate/event/def/ReattachVisitor.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -14,34 +14,51 @@
import org.hibernate.type.Type;
/**
- * Abstract superclass of visitors that reattach collections
+ * Abstract superclass of visitors that reattach collections.
+ *
* @author Gavin King
*/
public abstract class ReattachVisitor extends ProxyVisitor {
-
- private static final Log log = LogFactory.getLog(ReattachVisitor.class);
- private final Serializable key;
+ private static final Log log = LogFactory.getLog( ReattachVisitor.class );
- final Serializable getKey() {
- return key;
+ private final Serializable ownerIdentifier;
+ private final Object owner;
+
+ public ReattachVisitor(EventSource session, Serializable ownerIdentifier, Object owner) {
+ super( session );
+ this.ownerIdentifier = ownerIdentifier;
+ this.owner = owner;
}
- public ReattachVisitor(EventSource session, Serializable key) {
- super(session);
- this.key=key;
+ /**
+ * Retrieve the identifier of the entity being visited.
+ *
+ * @return The entity's identifier.
+ */
+ final Serializable getOwnerIdentifier() {
+ return ownerIdentifier;
}
- Object processComponent(Object component, AbstractComponentType componentType)
- throws HibernateException {
+ /**
+ * Retrieve the entity being visited.
+ *
+ * @return The entity.
+ */
+ final Object getOwner() {
+ return owner;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ Object processComponent(Object component, AbstractComponentType componentType) throws HibernateException {
Type[] types = componentType.getSubtypes();
- if (component==null) {
+ if ( component == null ) {
processValues( new Object[types.length], types );
}
else {
- super.processComponent(component, componentType);
- //processValues( componentType.getPropertyValues( component, getSession() ), types );
+ super.processComponent( component, componentType );
}
return null;
@@ -51,23 +68,36 @@
* Schedules a collection for deletion.
*
* @param role The persister representing the collection to be removed.
- * @param id The id of the entity containing the collection to be removed.
+ * @param collectionKey The collection key (differs from owner-id in the case of property-refs).
+ * @param source The session from which the request originated.
* @throws HibernateException
*/
- public void removeCollection(CollectionPersister role, Serializable id, EventSource source)
- throws HibernateException {
- if ( log.isTraceEnabled() )
+ void removeCollection(CollectionPersister role, Serializable collectionKey, EventSource source) throws HibernateException {
+ if ( log.isTraceEnabled() ) {
log.trace(
"collection dereferenced while transient " +
- MessageHelper.collectionInfoString( role, id, source.getFactory() )
+ MessageHelper.collectionInfoString( role, ownerIdentifier, source.getFactory() )
);
- /*if ( role.hasOrphanDelete() ) {
- throw new HibernateException(
- "You may not dereference a collection with cascade=\"all-delete-orphan\": " +
- MessageHelper.infoString(role, id)
- );
- }*/
- source.getActionQueue().addAction( new CollectionRemoveAction( null, role, id, false, source ) );
+ }
+ source.getActionQueue().addAction( new CollectionRemoveAction( null, role, collectionKey, false, source ) );
}
+ /**
+ * This version is slightly different for say
+ * {@link org.hibernate.type.CollectionType#getKeyOfOwner} in that here we
+ * need to assume that the owner is not yet associated with the session,
+ * and thus we cannot rely on the owner's EntityEntry snapshot...
+ *
+ * @param role The persister for the collection role being processed.
+ * @return
+ */
+ final Serializable extractCollectionKeyFromOwner(CollectionPersister role) {
+ if ( role.getCollectionType().useLHSPrimaryKey() ) {
+ return ownerIdentifier;
+ }
+ else {
+ return ( Serializable ) role.getOwnerEntityPersister().getPropertyValue( owner, role.getCollectionType().getLHSPropertyName(), getSession().getEntityMode() );
+ }
+
+ }
}
Modified: trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/Person.hbm.xml 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/Person.hbm.xml 2006-12-07 21:53:10 UTC (rev 10948)
@@ -1,17 +1,17 @@
<?xml version="1.0"?>
-<!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Demonstrates the use of property-ref to map legacy data where
foreign keys reference something other than the primary key of
the associated entity. Here we show:
-
- (1) A one-to-one foreign key association (prefer primary key
+
+ (1) A one-to-one foreign key association (prefer primary key
associations)
-
+
(2) A bidirectional one-to-many association on a key that is
not the primary key (prefer associations from foreign keys
to primary keys)
@@ -20,57 +20,47 @@
<hibernate-mapping package="org.hibernate.test.propertyref.basic">
- <class name="Person">
- <id name="id">
- <generator class="hilo"/>
- </id>
-
- <property name="name" length="100"/>
- <property name="userId" column="person_userid" length="8" unique="true"/>
-
- <one-to-one name="address" property-ref="person" cascade="all" fetch="join"/>
-
- <set name="accounts" inverse="true">
- <key column="userId" property-ref="userId"/>
- <one-to-many class="Account"/>
- </set>
-
- <bag name="systems" table="USER_SYSTEM" lazy="false" inverse="false" cascade="all">
+ <class name="Person" table="PROPREF_PERS">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="name" length="100"/>
+ <property name="userId" column="person_userid" length="8" unique="true"/>
+ <one-to-one name="address" property-ref="person" cascade="all" fetch="join"/>
+ <set name="accounts" inverse="true">
+ <key column="userId" property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+ <bag name="systems" table="USER_SYSTEM" lazy="true" inverse="false" cascade="all">
<key column="USER_ID" property-ref="userId" />
<element type="string" column="SYSTEM" />
</bag>
</class>
- <class name="Address">
- <id name="id">
- <generator class="hilo"/>
- </id>
+ <class name="Address" table="PROPREF_ADDR">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+ </class>
- <property name="address" length="300"/>
- <property name="zip" length="5"/>
- <property name="country" length="25"/>
- <many-to-one name="person" unique="true" not-null="true"/>
- </class>
-
- <class name="Account">
- <id name="accountId" length="32">
- <generator class="uuid.hex"/>
- </id>
-
- <many-to-one name="user"
- column="userId"
- property-ref="userId"/>
-
- <property name="type" not-null="true"/>
-
- </class>
-
- <class name="Group" table="`Group`">
- <id name="name"/>
- <set name="users" table="UserGroup" cascade="save-update">
- <key column="groupName"/>
- <many-to-many column="userId" class="Person" property-ref="userId"/>
- </set>
- </class>
+ <class name="Account" table="PROPREF_ACCT">
+ <id name="accountId" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+ <many-to-one name="user" column="userId" property-ref="userId"/>
+ <property name="type" not-null="true"/>
+ </class>
+ <class name="Group" table="PROPREF_GRP">
+ <id name="name"/>
+ <set name="users" table="PROPREF_USERGROUP" cascade="save-update">
+ <key column="groupName"/>
+ <many-to-many column="userId" class="Person" property-ref="userId"/>
+ </set>
+ </class>
+
</hibernate-mapping>
\ No newline at end of file
Modified: trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/PropertyRefTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/PropertyRefTest.java 2006-12-07 21:36:31 UTC (rev 10947)
+++ trunk/Hibernate3/test/org/hibernate/test/propertyref/basic/PropertyRefTest.java 2006-12-07 21:53:10 UTC (rev 10948)
@@ -60,7 +60,7 @@
t = s.beginTransaction();
s.createQuery( "from Person" ).list();
s.clear();
- s.createSQLQuery( "select {p.*} from Person {p}" )
+ s.createSQLQuery( "select {p.*} from PROPREF_PERS {p}" )
.addEntity( "p", Person.class.getName() )
.list();
t.commit();
More information about the hibernate-commits
mailing list