|
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