|
13 | 13 | import org.hibernate.action.internal.EntityDeleteAction;
|
14 | 14 | import org.hibernate.cache.spi.access.EntityDataAccess;
|
15 | 15 | import org.hibernate.engine.spi.EntityEntry;
|
| 16 | +import org.hibernate.engine.spi.EntityKey; |
16 | 17 | import org.hibernate.engine.spi.PersistenceContext;
|
17 | 18 | import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
18 | 19 | import org.hibernate.event.spi.EventSource;
|
@@ -43,71 +44,119 @@ public ReactiveEntityDeleteAction(
|
43 | 44 | super( id, state, version, instance, persister, isCascadeDeleteEnabled, session );
|
44 | 45 | }
|
45 | 46 |
|
| 47 | + public ReactiveEntityDeleteAction(Object id, EntityPersister persister, EventSource session) { |
| 48 | + super( id, persister, session ); |
| 49 | + } |
| 50 | + |
46 | 51 | @Override
|
47 | 52 | public void execute() throws HibernateException {
|
48 | 53 | throw LOG.nonReactiveMethodCall( "reactiveExecute" );
|
49 | 54 | }
|
50 | 55 |
|
| 56 | + private boolean isInstanceLoaded() { |
| 57 | + // A null instance signals that we're deleting an unloaded proxy. |
| 58 | + return getInstance() != null; |
| 59 | + } |
| 60 | + |
51 | 61 | @Override
|
52 | 62 | public CompletionStage<Void> reactiveExecute() throws HibernateException {
|
53 | 63 | final Object id = getId();
|
| 64 | + final Object version = getCurrentVersion(); |
54 | 65 | final EntityPersister persister = getPersister();
|
55 | 66 | final SharedSessionContractImplementor session = getSession();
|
56 | 67 | final Object instance = getInstance();
|
57 | 68 |
|
58 |
| - final boolean veto = preDelete(); |
| 69 | + final boolean veto = isInstanceLoaded() && preDelete(); |
59 | 70 |
|
60 |
| - Object version = getVersion(); |
61 |
| - if ( persister.isVersionPropertyGenerated() ) { |
62 |
| - // we need to grab the version value from the entity, otherwise |
63 |
| - // we have issues with generated-version entities that may have |
64 |
| - // multiple actions queued during the same flush |
65 |
| - version = persister.getVersion( instance ); |
66 |
| - } |
| 71 | + final Object ck = lockCacheItem(); |
67 | 72 |
|
68 |
| - final Object ck; |
69 |
| - if ( persister.canWriteToCache() ) { |
70 |
| - final EntityDataAccess cache = persister.getCacheAccessStrategy(); |
71 |
| - ck = cache.generateCacheKey( id, persister, session.getFactory(), session.getTenantIdentifier() ); |
72 |
| - setLock( cache.lockItem( session, ck, version ) ); |
73 |
| - } |
74 |
| - else { |
75 |
| - ck = null; |
76 |
| - } |
77 |
| - |
78 |
| - CompletionStage<Void> deleteStep = !isCascadeDeleteEnabled() && !veto |
| 73 | + final CompletionStage<Void> deleteStep = !isCascadeDeleteEnabled() && !veto |
79 | 74 | ? ( (ReactiveEntityPersister) persister ).deleteReactive( id, version, instance, session )
|
80 | 75 | : voidFuture();
|
81 | 76 |
|
82 | 77 | return deleteStep.thenAccept( v -> {
|
83 |
| - //postDelete: |
84 |
| - // After actually deleting a row, record the fact that the instance no longer |
85 |
| - // exists on the database (needed for identity-column key generation), and |
86 |
| - // remove it from the session cache |
87 |
| - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); |
88 |
| - final EntityEntry entry = persistenceContext.removeEntry( instance ); |
89 |
| - if ( entry == null ) { |
90 |
| - throw new AssertionFailure( "possible non-threadsafe access to session" ); |
| 78 | + if ( isInstanceLoaded() ) { |
| 79 | + postDeleteLoaded( id, persister, session, instance, ck ); |
91 | 80 | }
|
92 |
| - entry.postDelete(); |
93 |
| - |
94 |
| - persistenceContext.removeEntity( entry.getEntityKey() ); |
95 |
| - persistenceContext.removeProxy( entry.getEntityKey() ); |
96 |
| - |
97 |
| - if ( persister.canWriteToCache() ) { |
98 |
| - persister.getCacheAccessStrategy().remove( session, ck ); |
| 81 | + else { |
| 82 | + // we're deleting an unloaded proxy |
| 83 | + postDeleteUnloaded( id, persister, session, ck ); |
99 | 84 | }
|
100 | 85 |
|
101 |
| - persistenceContext.getNaturalIdResolutions() |
102 |
| - .removeSharedResolution( id, getNaturalIdValues(), persister ); |
103 |
| - |
104 |
| - postDelete(); |
105 |
| - |
106 | 86 | final StatisticsImplementor statistics = getSession().getFactory().getStatistics();
|
107 | 87 | if ( statistics.isStatisticsEnabled() && !veto ) {
|
108 | 88 | statistics.deleteEntity( getPersister().getEntityName() );
|
109 | 89 | }
|
110 | 90 | } );
|
111 | 91 | }
|
112 | 92 |
|
| 93 | + //TODO: copy/paste from superclass (make it protected!) |
| 94 | + private Object getCurrentVersion() { |
| 95 | + return getPersister().isVersionPropertyGenerated() |
| 96 | + // skip if we're deleting an unloaded proxy, no need for the version |
| 97 | + && isInstanceLoaded() |
| 98 | + // we need to grab the version value from the entity, otherwise |
| 99 | + // we have issues with generated-version entities that may have |
| 100 | + // multiple actions queued during the same flush |
| 101 | + ? getPersister().getVersion( getInstance() ) |
| 102 | + : getVersion(); |
| 103 | + } |
| 104 | + |
| 105 | + //TODO: copy/paste of postDeleteLoaded() from superclass (make it protected!) |
| 106 | + private void postDeleteLoaded( |
| 107 | + Object id, |
| 108 | + EntityPersister persister, |
| 109 | + SharedSessionContractImplementor session, |
| 110 | + Object instance, |
| 111 | + Object ck) { |
| 112 | + // After actually deleting a row, record the fact that the instance no longer |
| 113 | + // exists on the database (needed for identity-column key generation), and |
| 114 | + // remove it from the session cache |
| 115 | + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); |
| 116 | + final EntityEntry entry = persistenceContext.removeEntry(instance); |
| 117 | + if ( entry == null ) { |
| 118 | + throw new AssertionFailure( "possible non-threadsafe access to session" ); |
| 119 | + } |
| 120 | + entry.postDelete(); |
| 121 | + final EntityKey key = entry.getEntityKey(); |
| 122 | + persistenceContext.removeEntity( key ); |
| 123 | + persistenceContext.removeProxy( key ); |
| 124 | + removeCacheItem( ck ); |
| 125 | + persistenceContext.getNaturalIdResolutions().removeSharedResolution( id, getNaturalIdValues(), persister ); |
| 126 | + postDelete(); |
| 127 | + } |
| 128 | + |
| 129 | + //TODO: copy/paste of postDeleteUnloaded() from superclass (make it protected!) |
| 130 | + private void postDeleteUnloaded(Object id, EntityPersister persister, SharedSessionContractImplementor session, Object ck) { |
| 131 | + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); |
| 132 | + final EntityKey key = session.generateEntityKey( id, persister ); |
| 133 | + if ( !persistenceContext.containsDeletedUnloadedEntityKey( key ) ) { |
| 134 | + throw new AssertionFailure( "deleted proxy should be for an unloaded entity: " + key ); |
| 135 | + } |
| 136 | + persistenceContext.removeProxy( key ); |
| 137 | + removeCacheItem( ck ); |
| 138 | + } |
| 139 | + |
| 140 | + //TODO: copy/paste from superclass (make it protected!) |
| 141 | + private Object lockCacheItem() { |
| 142 | + final EntityPersister persister = getPersister(); |
| 143 | + if ( persister.canWriteToCache() ) { |
| 144 | + final EntityDataAccess cache = persister.getCacheAccessStrategy(); |
| 145 | + final SharedSessionContractImplementor session = getSession(); |
| 146 | + final Object ck = cache.generateCacheKey( getId(), persister, session.getFactory(), session.getTenantIdentifier() ); |
| 147 | + setLock( cache.lockItem( session, ck, getCurrentVersion() ) ); |
| 148 | + return ck; |
| 149 | + } |
| 150 | + else { |
| 151 | + return null; |
| 152 | + } |
| 153 | + } |
| 154 | + |
| 155 | + //TODO: copy/paste from superclass (make it protected!) |
| 156 | + private void removeCacheItem(Object ck) { |
| 157 | + final EntityPersister persister = getPersister(); |
| 158 | + if ( persister.canWriteToCache() ) { |
| 159 | + persister.getCacheAccessStrategy().remove( getSession(), ck); |
| 160 | + } |
| 161 | + } |
113 | 162 | }
|
0 commit comments