Author: ste...@jb...
Date: 2006-05-24 17:14:56 -0400 (Wed, 24 May 2006)
New Revision: 9944
Added:
trunk/Hibernate3/test/org/hibernate/test/deletetransient/
trunk/Hibernate3/test/org/hibernate/test/deletetransient/Address.java
trunk/Hibernate3/test/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java
trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.java
Modified:
trunk/Hibernate3/src/org/hibernate/engine/BatchFetchQueue.java
trunk/Hibernate3/src/org/hibernate/engine/Cascade.java
trunk/Hibernate3/src/org/hibernate/engine/CascadingAction.java
trunk/Hibernate3/src/org/hibernate/event/DeleteEventListener.java
trunk/Hibernate3/src/org/hibernate/event/EventSource.java
trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java
trunk/Hibernate3/src/org/hibernate/impl/IteratorImpl.java
trunk/Hibernate3/src/org/hibernate/impl/SessionImpl.java
Log:
HHH-1779 : delete() on transient entity per jpa spec;
HHH-1617 : BatchFetchQueue L2 cache peeking
Modified: trunk/Hibernate3/src/org/hibernate/engine/BatchFetchQueue.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/engine/BatchFetchQueue.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/engine/BatchFetchQueue.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -8,6 +8,7 @@
import org.apache.commons.collections.SequencedHashMap;
import org.hibernate.EntityMode;
+import org.hibernate.cache.CacheKey;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
@@ -17,27 +18,27 @@
* Tracks entity and collection keys that are available for batch
* fetching, and the queries which were used to load entities, which
* can be re-used as a subquery for loading owned collections.
- *
+ *
* @author Gavin King
*/
public class BatchFetchQueue {
public static final Object MARKER = new MarkerObject("MARKER");
-
+
// A set of entity keys that we predict we might need to load soon
// TODO: this would be better as a SequencedReferenceSet, but no such beast exists!
private final Map batchLoadableEntityKeys = new SequencedHashMap(8); //actually, a Set
-
+
// The subqueries that were used to load the entity with the given key
private final Map subselectsByEntityKey = new HashMap(8); //new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);
-
+
// The owning persistence context
private final PersistenceContext context;
-
+
public BatchFetchQueue(PersistenceContext context) {
this.context = context;
}
-
+
public void clear() {
batchLoadableEntityKeys.clear();
subselectsByEntityKey.clear();
@@ -54,26 +55,26 @@
public void clearSubselects() {
subselectsByEntityKey.clear();
}
-
+
/**
- * After evicting or deleting or loading an entity, we don't
+ * After evicting or deleting or loading an entity, we don't
* need to batch fetch it anymore, remove it from the queue
* if necessary
*/
public void removeBatchLoadableEntityKey(EntityKey key) {
if ( key.isBatchLoadable() ) batchLoadableEntityKeys.remove(key);
}
-
+
/**
- * After evicting or deleting an entity, we don't need to
- * know the query that was used to load it anymore (don't
+ * After evicting or deleting an entity, we don't need to
+ * know the query that was used to load it anymore (don't
* call this after loading the entity, since we might still
* need to load its collections)
*/
public void removeSubselect(EntityKey key) {
subselectsByEntityKey.remove(key);
}
-
+
/**
* If an EntityKey represents a batch loadable entity, add
* it to the queue.
@@ -83,18 +84,18 @@
}
/**
- * Get a batch of uninitialized collection keys for this role
- * @param collectionPersister the collection role
- * @param id a key that must be included
+ * Get a batch of uninitialized collection keys for a given role
+ *
+ * @param collectionPersister The persister for the collection role.
+ * @param id A key that must be included in the batch fetch
* @param batchSize the maximum number of keys to return
* @return an array of collection keys, of length batchSize (padded with nulls)
*/
public Serializable[] getCollectionBatch(
- final CollectionPersister collectionPersister,
- final Serializable id,
+ final CollectionPersister collectionPersister,
+ final Serializable id,
final int batchSize,
- final EntityMode entityMode
- ) {
+ final EntityMode entityMode) {
Serializable[] keys = new Serializable[batchSize];
keys[0] = id;
int i = 1;
@@ -107,37 +108,41 @@
Iterator iter = context.getCollectionEntries().entrySet().iterator(); //TODO: calling entrySet on an IdentityMap is SLOW!!
while ( iter.hasNext() ) {
Map.Entry me = (Map.Entry) iter.next();
-
+
CollectionEntry ce = (CollectionEntry) me.getValue();
PersistentCollection collection = (PersistentCollection) me.getKey();
if ( !collection.wasInitialized() && ce.getLoadedPersister() == collectionPersister ) {
-
- if ( checkForEnd && i == end ) return keys; //the first key found after the given key
-
+
+ if ( checkForEnd && i == end ) {
+ return keys; //the first key found after the given key
+ }
+
//if ( end == -1 && count > batchSize*10 ) return keys; //try out ten batches, max
-
- final boolean isEqual = collectionPersister.getKeyType().isEqual(
- id,
- ce.getLoadedKey(),
- entityMode,
- collectionPersister.getFactory()
- );
-
+
+ final boolean isEqual = collectionPersister.getKeyType().isEqual(
+ id,
+ ce.getLoadedKey(),
+ entityMode,
+ collectionPersister.getFactory()
+ );
+
if ( isEqual ) {
end = i;
//checkForEnd = false;
}
- else {
+ else if ( !isCached( ce.getLoadedKey(), collectionPersister, entityMode ) ) {
keys[i++] = ce.getLoadedKey();
//count++;
}
-
+
if ( i == batchSize ) {
i = 1; //end of array, start filling again from start
- if (end!=-1) checkForEnd = true;
+ if ( end != -1 ) {
+ checkForEnd = true;
+ }
}
}
-
+
}
return keys; //we ran out of keys to try
}
@@ -146,52 +151,79 @@
* Get a batch of unloaded identifiers for this class, using a slightly
* complex algorithm that tries to grab keys registered immediately after
* the given key.
- *
- * @param entityName The name of the persistent class
- * @param id an identifier that must be included
- * @param batchSize the maximum number of keys to return
- * @return an array of identifiers, of length batchSize (padded with nulls)
+ *
+ * @param persister The persister for the entities being loaded.
+ * @param id The identifier of the entity currently demanding load.
+ * @param batchSize The maximum number of keys to return
+ * @return an array of identifiers, of length batchSize (possibly padded with nulls)
*/
public Serializable[] getEntityBatch(
- final EntityPersister persister,
- final Serializable id,
- final int batchSize,
- final EntityMode entityMode
- ) {
+ final EntityPersister persister,
+ final Serializable id,
+ final int batchSize,
+ final EntityMode entityMode) {
Serializable[] ids = new Serializable[batchSize];
ids[0] = id; //first element of array is reserved for the actual instance we are loading!
int i = 1;
int end = -1;
boolean checkForEnd = false;
- //int count = 0;
+
Iterator iter = batchLoadableEntityKeys.keySet().iterator();
while ( iter.hasNext() ) {
-
EntityKey key = (EntityKey) iter.next();
if ( key.getEntityName().equals( persister.getEntityName() ) ) { //TODO: this needn't exclude subclasses...
-
- if ( checkForEnd && i == end ) return ids; //the first id found after the given id
-
- //if ( end == -1 && count > batchSize*10 ) return ids; //try out ten batches, max
-
+ if ( checkForEnd && i == end ) {
+ //the first id found after the given id
+ return ids;
+ }
if ( persister.getIdentifierType().isEqual( id, key.getIdentifier(), entityMode ) ) {
end = i;
- //checkForEnd = false;
}
else {
- ids[i++] = key.getIdentifier();
- //count++;
+ if ( !isCached( key, persister, entityMode ) ) {
+ ids[i++] = key.getIdentifier();
+ }
}
-
if ( i == batchSize ) {
i = 1; //end of array, start filling again from start
if (end!=-1) checkForEnd = true;
}
-
}
-
}
return ids; //we ran out of ids to try
}
+ private boolean isCached(
+ EntityKey entityKey,
+ EntityPersister persister,
+ EntityMode entityMode) {
+ if ( persister.hasCache() ) {
+ CacheKey key = new CacheKey(
+ entityKey.getIdentifier(),
+ persister.getIdentifierType(),
+ entityKey.getEntityName(),
+ entityMode,
+ context.getSession().getFactory()
+ );
+ return persister.getCache().getCache().get( key ) != null;
+ }
+ return false;
+ }
+
+ private boolean isCached(
+ Serializable collectionKey,
+ CollectionPersister persister,
+ EntityMode entityMode) {
+ if ( persister.hasCache() ) {
+ CacheKey cacheKey = new CacheKey(
+ collectionKey,
+ persister.getKeyType(),
+ persister.getRole(),
+ entityMode,
+ context.getSession().getFactory()
+ );
+ return persister.getCache().getCache().get( cacheKey ) != null;
+ }
+ return false;
+ }
}
Modified: trunk/Hibernate3/src/org/hibernate/engine/Cascade.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/engine/Cascade.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/engine/Cascade.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -348,7 +348,7 @@
if ( log.isTraceEnabled() ) {
log.trace("deleting orphaned entity instance: " + entityName);
}
- eventSource.delete(entityName, orphan, false);
+ eventSource.delete( entityName, orphan, false, null );
}
}
Modified: trunk/Hibernate3/src/org/hibernate/engine/CascadingAction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/engine/CascadingAction.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/engine/CascadingAction.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -3,6 +3,7 @@
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -42,10 +43,12 @@
public static final CascadingAction DELETE = new CascadingAction() {
public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
throws HibernateException {
- if ( log.isTraceEnabled() ) log.trace("cascading to delete: " + entityName);
- if ( ForeignKeys.isNotTransient(entityName, child, null, session) ) {
- session.delete(entityName, child, isCascadeDeleteEnabled);
+ if ( log.isTraceEnabled() ) {
+ log.trace("cascading to delete: " + entityName);
}
+// if ( ForeignKeys.isNotTransient(entityName, child, null, session) ) {
+ session.delete( entityName, child, isCascadeDeleteEnabled, ( Set ) anything );
+// }
}
public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
// delete does cascade to uninitialized collections
Modified: trunk/Hibernate3/src/org/hibernate/event/DeleteEventListener.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/DeleteEventListener.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/event/DeleteEventListener.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -4,6 +4,7 @@
import org.hibernate.HibernateException;
import java.io.Serializable;
+import java.util.Set;
/**
* Defines the contract for handling of deletion events generated from a session.
@@ -18,4 +19,6 @@
* @throws HibernateException
*/
public void onDelete(DeleteEvent event) throws HibernateException;
+
+ public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException;
}
Modified: trunk/Hibernate3/src/org/hibernate/event/EventSource.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/EventSource.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/event/EventSource.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -3,6 +3,7 @@
import java.io.Serializable;
import java.util.Map;
+import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.Session;
@@ -57,6 +58,6 @@
/**
* Cascade delete an entity instance
*/
- public void delete(String entityName, Object child, boolean isCascadeDeleteEnabled);
+ public void delete(String entityName, Object child, boolean isCascadeDeleteEnabled, Set transientEntities);
}
Modified: trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/event/def/DefaultDeleteEventListener.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -2,6 +2,7 @@
package org.hibernate.event.def;
import java.io.Serializable;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -9,6 +10,7 @@
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.TransientObjectException;
+import org.hibernate.util.IdentitySet;
import org.hibernate.action.EntityDeleteAction;
import org.hibernate.classic.Lifecycle;
import org.hibernate.engine.Cascade;
@@ -44,10 +46,16 @@
* @throws HibernateException
*/
public void onDelete(DeleteEvent event) throws HibernateException {
+ onDelete( event, new IdentitySet() );
+ }
+
+ public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {
+
final EventSource source = event.getSession();
final PersistenceContext persistenceContext = source.getPersistenceContext();
Object entity = persistenceContext.unproxyAndReassociate( event.getObject() );
+
EntityEntry entityEntry = persistenceContext.getEntry(entity);
final EntityPersister persister;
@@ -60,9 +68,17 @@
id = persister.getIdentifier( entity, source.getEntityMode() );
if ( id == null ) {
- throw new TransientObjectException(
- "the detached instance passed to delete() had a null identifier"
- );
+// throw new TransientObjectException(
+// "the detached instance passed to delete() had a null identifier"
+// );
+ if ( ForeignKeys.isNotTransient( persister.getEntityName(), entity, null, source ) ) {
+ throw new TransientObjectException( "the detached instance passed to delete() had a null identifier" );
+ }
+ else {
+ deleteTransientEntity( source, entity, event.isCascadeDeleteEnabled(), persister, transientEntities );
+ // EARLY EXIT!!!
+ return;
+ }
}
EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
@@ -107,25 +123,53 @@
if ( invokeDeleteLifecycle( source, entity, persister ) ) return;
- deleteEntity( source, entity, entityEntry, event.isCascadeDeleteEnabled(), persister );
+ deleteEntity( source, entity, entityEntry, event.isCascadeDeleteEnabled(), persister, transientEntities );
if ( source.getFactory().getSettings().isIdentifierRollbackEnabled() ) {
persister.resetIdentifier( entity, id, version, source.getEntityMode() );
}
-
}
+ /**
+ * We encountered a delete request on a transient instance.
+ * <p/>
+ * This is a deviation from historical Hibernate (pre-3.2) behavior to
+ * align with the JPA spec, which states that transient entities can be
+ * passed to remove operation in which case cascades still need to be
+ * performed.
+ *
+ * @param session The session which is the source of the event
+ * @param entity The entity being delete processed
+ * @param cascadeDeleteEnabled
+ * @param persister The entity persister
+ */
+ protected void deleteTransientEntity(
+ EventSource session,
+ Object entity,
+ boolean cascadeDeleteEnabled,
+ EntityPersister persister,
+ Set transientEntities) {
+ log.info( "handling transient entity in delete processing" );
+ if ( transientEntities.contains( entity ) ) {
+ log.trace( "already handled transient entity; skipping" );
+ return;
+ }
+ transientEntities.add( entity );
+ cascadeBeforeDelete( session, persister, entity, null, transientEntities );
+ cascadeAfterDelete( session, persister, entity, transientEntities );
+ }
+
protected final void deleteEntity(
final EventSource session,
final Object entity,
final EntityEntry entityEntry,
final boolean isCascadeDeleteEnabled,
- final EntityPersister persister)
- throws HibernateException {
+ final EntityPersister persister,
+ final Set transientEntities) throws HibernateException {
if ( log.isTraceEnabled() ) {
log.trace(
- "deleting " +
+ "deleting " +
MessageHelper.infoString( persister, entityEntry.getId(), session.getFactory() )
);
}
@@ -143,13 +187,13 @@
else {
currentState = entityEntry.getLoadedState();
}
-
+
final Object[] deletedState = new Object[propTypes.length];
- TypeFactory.deepCopy(
- currentState,
- propTypes,
- persister.getPropertyUpdateability(),
- deletedState,
+ TypeFactory.deepCopy(
+ currentState,
+ propTypes,
+ persister.getPropertyUpdateability(),
+ deletedState,
session
);
entityEntry.setDeletedState(deletedState);
@@ -166,7 +210,7 @@
persistenceContext.setEntryStatus(entityEntry, Status.DELETED);
EntityKey key = new EntityKey( entityEntry.getId(), persister, session.getEntityMode() );
- cascadeBeforeDelete(session, persister, entity, entityEntry);
+ cascadeBeforeDelete( session, persister, entity, entityEntry, transientEntities );
new ForeignKeys.Nullifier(entity, true, false, session)
.nullifyTransientReferences( entityEntry.getDeletedState(), propTypes );
@@ -175,19 +219,19 @@
// Ensures that containing deletions happen before sub-deletions
session.getActionQueue().addAction(
- new EntityDeleteAction(
- entityEntry.getId(),
- deletedState,
- version,
- entity,
- persister,
- isCascadeDeleteEnabled,
- session
+ new EntityDeleteAction(
+ entityEntry.getId(),
+ deletedState,
+ version,
+ entity,
+ persister,
+ isCascadeDeleteEnabled,
+ session
)
);
-
- cascadeAfterDelete(session, persister, entity);
-
+
+ cascadeAfterDelete( session, persister, entity, transientEntities );
+
// the entry will be removed after the flush, and will no longer
// override the stale snapshot
// This is now handled by removeEntity() in EntityDeleteAction
@@ -210,15 +254,16 @@
EventSource session,
EntityPersister persister,
Object entity,
- EntityEntry entityEntry) throws HibernateException {
+ EntityEntry entityEntry,
+ Set transientEntities) throws HibernateException {
CacheMode cacheMode = session.getCacheMode();
session.setCacheMode(CacheMode.GET);
session.getPersistenceContext().incrementCascadeLevel();
try {
// cascade-delete to collections BEFORE the collection owner is deleted
- new Cascade(CascadingAction.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session)
- .cascade(persister, entity);
+ new Cascade( CascadingAction.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session )
+ .cascade( persister, entity, transientEntities );
}
finally {
session.getPersistenceContext().decrementCascadeLevel();
@@ -229,15 +274,16 @@
protected void cascadeAfterDelete(
EventSource session,
EntityPersister persister,
- Object entity) throws HibernateException {
+ Object entity,
+ Set transientEntities) throws HibernateException {
CacheMode cacheMode = session.getCacheMode();
session.setCacheMode(CacheMode.GET);
session.getPersistenceContext().incrementCascadeLevel();
try {
// cascade-delete to many-to-one AFTER the parent was deleted
- new Cascade(CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session)
- .cascade(persister, entity);
+ new Cascade( CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session )
+ .cascade( persister, entity, transientEntities );
}
finally {
session.getPersistenceContext().decrementCascadeLevel();
Modified: trunk/Hibernate3/src/org/hibernate/impl/IteratorImpl.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/impl/IteratorImpl.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/impl/IteratorImpl.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -144,7 +144,8 @@
session.delete(
( (EntityType) types[0] ).getAssociatedEntityName(),
currentResult,
- false
+ false,
+ null
);
}
Modified: trunk/Hibernate3/src/org/hibernate/impl/SessionImpl.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/impl/SessionImpl.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/src/org/hibernate/impl/SessionImpl.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -756,8 +756,8 @@
/**
* Delete a persistent object
*/
- public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled) throws HibernateException {
- fireDelete( new DeleteEvent(entityName, object, isCascadeDeleteEnabled, this) );
+ public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled, Set transientEntities) throws HibernateException {
+ fireDelete( new DeleteEvent( entityName, object, isCascadeDeleteEnabled, this ), transientEntities );
}
private void fireDelete(DeleteEvent event) {
@@ -769,7 +769,16 @@
}
}
+ private void fireDelete(DeleteEvent event, Set transientEntities) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ DeleteEventListener[] deleteEventListener = listeners.getDeleteEventListeners();
+ for ( int i = 0; i < deleteEventListener.length; i++ ) {
+ deleteEventListener[i].onDelete( event, transientEntities );
+ }
+ }
+
// load()/get() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void load(Object object, Serializable id) throws HibernateException {
Added: trunk/Hibernate3/test/org/hibernate/test/deletetransient/Address.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/deletetransient/Address.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/test/org/hibernate/test/deletetransient/Address.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -0,0 +1,34 @@
+package org.hibernate.test.deletetransient;
+
+/**
+ * todo: describe Address
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private Long id;
+ private String info;
+
+ public Address() {
+ }
+
+ public Address(String info) {
+ this.info = info;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/test/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -0,0 +1,101 @@
+package org.hibernate.test.deletetransient;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+
+/**
+ * todo: describe DeleteTransientEntityTest
+ *
+ * @author Steve Ebersole
+ */
+public class DeleteTransientEntityTest extends TestCase {
+ public DeleteTransientEntityTest(String name) {
+ super( name );
+ }
+
+ protected String[] getMappings() {
+ return new String[] { "deletetransient/Person.hbm.xml" };
+ }
+
+ public void testTransientEntityDeletionNoCascades() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.delete( new Address() );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeletionCascadingToTransientAssociation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.getAddresses().add( new Address() );
+ s.delete( p );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeleteCascadingToCircularity() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p1 = new Person();
+ Person p2 = new Person();
+ p1.getFriends().add( p2 );
+ p2.getFriends().add( p1 );
+ s.delete( p1 );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeletionCascadingToDetachedAssociation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Address address = new Address();
+ address.setInfo( "123 Main St." );
+ s.save( address );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Person p = new Person();
+ p.getAddresses().add( address );
+ s.delete( p );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Long count = ( Long ) s.createQuery( "select count(*) from Address" ).list().get( 0 );
+ assertEquals( "delete not cascaded properly across transient entity", 0, count.longValue() );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeletionCascadingToPersistentAssociation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Address address = new Address();
+ address.setInfo( "123 Main St." );
+ s.save( address );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ address = ( Address ) s.get( Address.class, address.getId() );
+ Person p = new Person();
+ p.getAddresses().add( address );
+ s.delete( p );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Long count = ( Long ) s.createQuery( "select count(*) from Address" ).list().get( 0 );
+ assertEquals( "delete not cascaded properly across transient entity", 0, count.longValue() );
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.hbm.xml 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.hbm.xml 2006-05-24 21:14:56 UTC (rev 9944)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.deletetransient">
+
+ <class name="Person" table="T_PERSON">
+ <id name="id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string"/>
+ <set name="addresses" lazy="true" inverse="false" cascade="all">
+ <key column="PERSON_ID"/>
+ <one-to-many class="Address"/>
+ </set>
+ <bag name="friends" lazy="true" inverse="false" cascade="all" table="T_FRIENDS">
+ <key column="FRIEND_ID_1"/>
+ <many-to-many class="Person" column="FRIEND_ID_2"/>
+ </bag>
+ </class>
+
+ <class name="Address" table="T_ADDRESS">
+ <id name="id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="info" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.java 2006-05-24 13:34:55 UTC (rev 9943)
+++ trunk/Hibernate3/test/org/hibernate/test/deletetransient/Person.java 2006-05-24 21:14:56 UTC (rev 9944)
@@ -0,0 +1,57 @@
+package org.hibernate.test.deletetransient;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.ArrayList;
+
+/**
+ * todo: describe Person
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private Set addresses = new HashSet();
+ private Collection friends = new ArrayList();
+
+ public Person() {
+ }
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set getAddresses() {
+ return addresses;
+ }
+
+ public void setAddresses(Set addresses) {
+ this.addresses = addresses;
+ }
+
+ public Collection getFriends() {
+ return friends;
+ }
+
+ public void setFriends(Collection friends) {
+ this.friends = friends;
+ }
+}
|