~ubuntu-branches/ubuntu/wily/libhibernate3-java/wily-proposed

« back to all changes in this revision

Viewing changes to src/org/hibernate/event/def/DefaultDeleteEventListener.java

  • Committer: Bazaar Package Importer
  • Author(s): Torsten Werner
  • Date: 2007-10-14 14:43:34 UTC
  • Revision ID: james.westby@ubuntu.com-20071014144334-eamc8i0q10gs1aro
Tags: upstream-3.2.5
ImportĀ upstreamĀ versionĀ 3.2.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//$Id: DefaultDeleteEventListener.java 10949 2006-12-07 21:53:41Z steve.ebersole@jboss.com $
 
2
package org.hibernate.event.def;
 
3
 
 
4
import java.io.Serializable;
 
5
import java.util.Set;
 
6
 
 
7
import org.apache.commons.logging.Log;
 
8
import org.apache.commons.logging.LogFactory;
 
9
 
 
10
import org.hibernate.CacheMode;
 
11
import org.hibernate.HibernateException;
 
12
import org.hibernate.LockMode;
 
13
import org.hibernate.TransientObjectException;
 
14
import org.hibernate.util.IdentitySet;
 
15
import org.hibernate.action.EntityDeleteAction;
 
16
import org.hibernate.classic.Lifecycle;
 
17
import org.hibernate.engine.Cascade;
 
18
import org.hibernate.engine.CascadingAction;
 
19
import org.hibernate.engine.EntityEntry;
 
20
import org.hibernate.engine.EntityKey;
 
21
import org.hibernate.engine.ForeignKeys;
 
22
import org.hibernate.engine.Nullability;
 
23
import org.hibernate.engine.PersistenceContext;
 
24
import org.hibernate.engine.Status;
 
25
import org.hibernate.event.DeleteEvent;
 
26
import org.hibernate.event.DeleteEventListener;
 
27
import org.hibernate.event.EventSource;
 
28
import org.hibernate.persister.entity.EntityPersister;
 
29
import org.hibernate.pretty.MessageHelper;
 
30
import org.hibernate.type.Type;
 
31
import org.hibernate.type.TypeFactory;
 
32
 
 
33
 
 
34
/**
 
35
 * Defines the default delete event listener used by hibernate for deleting entities
 
36
 * from the datastore in response to generated delete events.
 
37
 *
 
38
 * @author Steve Ebersole
 
39
 */
 
40
public class DefaultDeleteEventListener implements DeleteEventListener {
 
41
 
 
42
        private static final Log log = LogFactory.getLog( DefaultDeleteEventListener.class );
 
43
 
 
44
        /**
 
45
         * Handle the given delete event.
 
46
         *
 
47
         * @param event The delete event to be handled.
 
48
         *
 
49
         * @throws HibernateException
 
50
         */
 
51
        public void onDelete(DeleteEvent event) throws HibernateException {
 
52
                onDelete( event, new IdentitySet() );
 
53
        }
 
54
 
 
55
        /**
 
56
         * Handle the given delete event.  This is the cascaded form.
 
57
         *
 
58
         * @param event The delete event.
 
59
         * @param transientEntities The cache of entities already deleted
 
60
         *
 
61
         * @throws HibernateException
 
62
         */
 
63
        public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {
 
64
 
 
65
                final EventSource source = event.getSession();
 
66
 
 
67
                final PersistenceContext persistenceContext = source.getPersistenceContext();
 
68
                Object entity = persistenceContext.unproxyAndReassociate( event.getObject() );
 
69
 
 
70
                EntityEntry entityEntry = persistenceContext.getEntry( entity );
 
71
                final EntityPersister persister;
 
72
                final Serializable id;
 
73
                final Object version;
 
74
 
 
75
                if ( entityEntry == null ) {
 
76
                        log.trace( "entity was not persistent in delete processing" );
 
77
 
 
78
                        persister = source.getEntityPersister( event.getEntityName(), entity );
 
79
 
 
80
                        if ( ForeignKeys.isTransient( persister.getEntityName(), entity, null, source ) ) {
 
81
                                deleteTransientEntity( source, entity, event.isCascadeDeleteEnabled(), persister, transientEntities );
 
82
                                // EARLY EXIT!!!
 
83
                                return;
 
84
                        }
 
85
                        else {
 
86
                                performDetachedEntityDeletionCheck( event );
 
87
                        }
 
88
 
 
89
                        id = persister.getIdentifier( entity, source.getEntityMode() );
 
90
 
 
91
                        if ( id == null ) {
 
92
                                throw new TransientObjectException(
 
93
                                                "the detached instance passed to delete() had a null identifier"
 
94
                                );
 
95
                        }
 
96
 
 
97
                        EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
 
98
 
 
99
                        persistenceContext.checkUniqueness( key, entity );
 
100
 
 
101
                        new OnUpdateVisitor( source, id, entity ).process( entity, persister );
 
102
 
 
103
                        version = persister.getVersion( entity, source.getEntityMode() );
 
104
 
 
105
                        entityEntry = persistenceContext.addEntity(
 
106
                                        entity,
 
107
                                        Status.MANAGED,
 
108
                                        persister.getPropertyValues( entity, source.getEntityMode() ),
 
109
                                        key,
 
110
                                        version,
 
111
                                        LockMode.NONE,
 
112
                                        true,
 
113
                                        persister,
 
114
                                        false,
 
115
                                        false
 
116
                        );
 
117
                }
 
118
                else {
 
119
                        log.trace( "deleting a persistent instance" );
 
120
 
 
121
                        if ( entityEntry.getStatus() == Status.DELETED || entityEntry.getStatus() == Status.GONE ) {
 
122
                                log.trace( "object was already deleted" );
 
123
                                return;
 
124
                        }
 
125
                        persister = entityEntry.getPersister();
 
126
                        id = entityEntry.getId();
 
127
                        version = entityEntry.getVersion();
 
128
                }
 
129
 
 
130
                /*if ( !persister.isMutable() ) {
 
131
                        throw new HibernateException(
 
132
                                        "attempted to delete an object of immutable class: " +
 
133
                                        MessageHelper.infoString(persister)
 
134
                                );
 
135
                }*/
 
136
 
 
137
                if ( invokeDeleteLifecycle( source, entity, persister ) ) {
 
138
                        return;
 
139
                }
 
140
 
 
141
                deleteEntity( source, entity, entityEntry, event.isCascadeDeleteEnabled(), persister, transientEntities );
 
142
 
 
143
                if ( source.getFactory().getSettings().isIdentifierRollbackEnabled() ) {
 
144
                        persister.resetIdentifier( entity, id, version, source.getEntityMode() );
 
145
                }
 
146
        }
 
147
 
 
148
        /**
 
149
         * Called when we have recognized an attempt to delete a detached entity.
 
150
         * <p/>
 
151
         * This is perfectly valid in Hibernate usage; JPA, however, forbids this.
 
152
         * Thus, this is a hook for HEM to affect this behavior.
 
153
         *
 
154
         * @param event The event.
 
155
         */
 
156
        protected void performDetachedEntityDeletionCheck(DeleteEvent event) {
 
157
                // ok in normal Hibernate usage to delete a detached entity; JPA however
 
158
                // forbids it, thus this is a hook for HEM to affect this behavior
 
159
        }
 
160
 
 
161
        /**
 
162
         * We encountered a delete request on a transient instance.
 
163
         * <p/>
 
164
         * This is a deviation from historical Hibernate (pre-3.2) behavior to
 
165
         * align with the JPA spec, which states that transient entities can be
 
166
         * passed to remove operation in which case cascades still need to be
 
167
         * performed.
 
168
         *
 
169
         * @param session The session which is the source of the event
 
170
         * @param entity The entity being delete processed
 
171
         * @param cascadeDeleteEnabled Is cascading of deletes enabled
 
172
         * @param persister The entity persister
 
173
         * @param transientEntities A cache of already visited transient entities
 
174
         * (to avoid infinite recursion).
 
175
         */
 
176
        protected void deleteTransientEntity(
 
177
                        EventSource session,
 
178
                        Object entity,
 
179
                        boolean cascadeDeleteEnabled,
 
180
                        EntityPersister persister,
 
181
                        Set transientEntities) {
 
182
                log.info( "handling transient entity in delete processing" );
 
183
                if ( transientEntities.contains( entity ) ) {
 
184
                        log.trace( "already handled transient entity; skipping" );
 
185
                        return;
 
186
                }
 
187
                transientEntities.add( entity );
 
188
                cascadeBeforeDelete( session, persister, entity, null, transientEntities );
 
189
                cascadeAfterDelete( session, persister, entity, transientEntities );
 
190
        }
 
191
 
 
192
        /**
 
193
         * Perform the entity deletion.  Well, as with most operations, does not
 
194
         * really perform it; just schedules an action/execution with the
 
195
         * {@link org.hibernate.engine.ActionQueue} for execution during flush.
 
196
         *
 
197
         * @param session The originating session
 
198
         * @param entity The entity to delete
 
199
         * @param entityEntry The entity's entry in the {@link PersistenceContext}
 
200
         * @param isCascadeDeleteEnabled Is delete cascading enabled?
 
201
         * @param persister The entity persister.
 
202
         * @param transientEntities A cache of already deleted entities.
 
203
         */
 
204
        protected final void deleteEntity(
 
205
                        final EventSource session,
 
206
                        final Object entity,
 
207
                        final EntityEntry entityEntry,
 
208
                        final boolean isCascadeDeleteEnabled,
 
209
                        final EntityPersister persister,
 
210
                        final Set transientEntities) {
 
211
 
 
212
                if ( log.isTraceEnabled() ) {
 
213
                        log.trace(
 
214
                                        "deleting " +
 
215
                                                        MessageHelper.infoString( persister, entityEntry.getId(), session.getFactory() )
 
216
                        );
 
217
                }
 
218
 
 
219
                final PersistenceContext persistenceContext = session.getPersistenceContext();
 
220
                final Type[] propTypes = persister.getPropertyTypes();
 
221
                final Object version = entityEntry.getVersion();
 
222
 
 
223
                final Object[] currentState;
 
224
                if ( entityEntry.getLoadedState() == null ) { //ie. the entity came in from update()
 
225
                        currentState = persister.getPropertyValues( entity, session.getEntityMode() );
 
226
                }
 
227
                else {
 
228
                        currentState = entityEntry.getLoadedState();
 
229
                }
 
230
 
 
231
                final Object[] deletedState = createDeletedState( persister, currentState, session );
 
232
                entityEntry.setDeletedState( deletedState );
 
233
 
 
234
                session.getInterceptor().onDelete(
 
235
                                entity,
 
236
                                entityEntry.getId(),
 
237
                                deletedState,
 
238
                                persister.getPropertyNames(),
 
239
                                propTypes
 
240
                );
 
241
 
 
242
                // before any callbacks, etc, so subdeletions see that this deletion happened first
 
243
                persistenceContext.setEntryStatus( entityEntry, Status.DELETED );
 
244
                EntityKey key = new EntityKey( entityEntry.getId(), persister, session.getEntityMode() );
 
245
 
 
246
                cascadeBeforeDelete( session, persister, entity, entityEntry, transientEntities );
 
247
 
 
248
                new ForeignKeys.Nullifier( entity, true, false, session )
 
249
                                .nullifyTransientReferences( entityEntry.getDeletedState(), propTypes );
 
250
                new Nullability( session ).checkNullability( entityEntry.getDeletedState(), persister, true );
 
251
                persistenceContext.getNullifiableEntityKeys().add( key );
 
252
 
 
253
                // Ensures that containing deletions happen before sub-deletions
 
254
                session.getActionQueue().addAction(
 
255
                                new EntityDeleteAction(
 
256
                                                entityEntry.getId(),
 
257
                                                deletedState,
 
258
                                                version,
 
259
                                                entity,
 
260
                                                persister,
 
261
                                                isCascadeDeleteEnabled,
 
262
                                                session
 
263
                                )
 
264
                );
 
265
 
 
266
                cascadeAfterDelete( session, persister, entity, transientEntities );
 
267
 
 
268
                // the entry will be removed after the flush, and will no longer
 
269
                // override the stale snapshot
 
270
                // This is now handled by removeEntity() in EntityDeleteAction
 
271
                //persistenceContext.removeDatabaseSnapshot(key);
 
272
        }
 
273
 
 
274
        private Object[] createDeletedState(EntityPersister persister, Object[] currentState, EventSource session) {
 
275
                Type[] propTypes = persister.getPropertyTypes();
 
276
                final Object[] deletedState = new Object[propTypes.length];
 
277
//              TypeFactory.deepCopy( currentState, propTypes, persister.getPropertyUpdateability(), deletedState, session );
 
278
                boolean[] copyability = new boolean[propTypes.length];
 
279
                java.util.Arrays.fill( copyability, true );
 
280
                TypeFactory.deepCopy( currentState, propTypes, copyability, deletedState, session );
 
281
                return deletedState;
 
282
        }
 
283
 
 
284
        protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) {
 
285
                if ( persister.implementsLifecycle( session.getEntityMode() ) ) {
 
286
                        log.debug( "calling onDelete()" );
 
287
                        if ( ( ( Lifecycle ) entity ).onDelete( session ) ) {
 
288
                                log.debug( "deletion vetoed by onDelete()" );
 
289
                                return true;
 
290
                        }
 
291
                }
 
292
                return false;
 
293
        }
 
294
 
 
295
        protected void cascadeBeforeDelete(
 
296
                        EventSource session,
 
297
                        EntityPersister persister,
 
298
                        Object entity,
 
299
                        EntityEntry entityEntry,
 
300
                        Set transientEntities) throws HibernateException {
 
301
 
 
302
                CacheMode cacheMode = session.getCacheMode();
 
303
                session.setCacheMode( CacheMode.GET );
 
304
                session.getPersistenceContext().incrementCascadeLevel();
 
305
                try {
 
306
                        // cascade-delete to collections BEFORE the collection owner is deleted
 
307
                        new Cascade( CascadingAction.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session )
 
308
                                        .cascade( persister, entity, transientEntities );
 
309
                }
 
310
                finally {
 
311
                        session.getPersistenceContext().decrementCascadeLevel();
 
312
                        session.setCacheMode( cacheMode );
 
313
                }
 
314
        }
 
315
 
 
316
        protected void cascadeAfterDelete(
 
317
                        EventSource session,
 
318
                        EntityPersister persister,
 
319
                        Object entity,
 
320
                        Set transientEntities) throws HibernateException {
 
321
 
 
322
                CacheMode cacheMode = session.getCacheMode();
 
323
                session.setCacheMode( CacheMode.GET );
 
324
                session.getPersistenceContext().incrementCascadeLevel();
 
325
                try {
 
326
                        // cascade-delete to many-to-one AFTER the parent was deleted
 
327
                        new Cascade( CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session )
 
328
                                        .cascade( persister, entity, transientEntities );
 
329
                }
 
330
                finally {
 
331
                        session.getPersistenceContext().decrementCascadeLevel();
 
332
                        session.setCacheMode( cacheMode );
 
333
                }
 
334
        }
 
335
 
 
336
}