Skip to content

Commit 51eeab7

Browse files
committed
HHH-19248 - Fix for issue
Signed-off-by: Jan Schatteman <[email protected]>
1 parent cb9ad69 commit 51eeab7

File tree

9 files changed

+174
-107
lines changed

9 files changed

+174
-107
lines changed

hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,6 @@ public EntityDeleteAction(
6060
this.version = version;
6161
this.isCascadeDeleteEnabled = isCascadeDeleteEnabled;
6262
this.state = state;
63-
64-
final NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping();
65-
if ( naturalIdMapping != null ) {
66-
naturalIdValues = session.getPersistenceContextInternal().getNaturalIdResolutions()
67-
.removeLocalResolution(
68-
getId(),
69-
naturalIdMapping.extractNaturalIdFromEntityState( state ),
70-
getPersister()
71-
);
72-
}
7363
}
7464

7565
/**
@@ -125,6 +115,16 @@ public void execute() throws HibernateException {
125115

126116
final boolean veto = isInstanceLoaded() && preDelete();
127117

118+
final NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping();
119+
if ( naturalIdMapping != null ) {
120+
naturalIdValues = session.getPersistenceContextInternal().getNaturalIdResolutions()
121+
.removeLocalResolution(
122+
getId(),
123+
naturalIdMapping.extractNaturalIdFromEntityState( state ),
124+
getPersister()
125+
);
126+
}
127+
128128
final Object ck = lockCacheItem();
129129

130130
if ( !isCascadeDeleteEnabled && !veto ) {

hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,19 @@
44
*/
55
package org.hibernate.internal;
66

7+
import org.hibernate.engine.spi.EntityEntry;
8+
import org.hibernate.engine.spi.EntityKey;
9+
import org.hibernate.engine.spi.PersistenceContext;
10+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
11+
import org.hibernate.engine.spi.Status;
712
import org.hibernate.id.IdentifierGenerationException;
13+
import org.hibernate.loader.LoaderLogging;
14+
import org.hibernate.metamodel.mapping.EntityMappingType;
15+
import org.hibernate.metamodel.mapping.NaturalIdMapping;
816
import org.hibernate.persister.entity.EntityPersister;
917

18+
import java.util.Collection;
19+
1020
/**
1121
* @author Gavin King
1222
*/
@@ -28,4 +38,63 @@ public static String[] getNaturalIdPropertyNames(EntityPersister persister) {
2838
}
2939
return propertyNames;
3040
}
41+
42+
public static void performAnyNeededCrossReferenceSynchronizations(
43+
boolean synchronizationEnabled,
44+
EntityMappingType entityMappingType,
45+
SharedSessionContractImplementor session) {
46+
47+
if ( !synchronizationEnabled ) {
48+
// synchronization (this process) was disabled
49+
return;
50+
}
51+
52+
final NaturalIdMapping naturalIdMapping = entityMappingType.getNaturalIdMapping();
53+
54+
if ( !naturalIdMapping.isMutable() ) {
55+
// only mutable natural-ids need this processing
56+
return;
57+
}
58+
59+
if ( ! session.isTransactionInProgress() ) {
60+
// not in a transaction so skip synchronization
61+
return;
62+
}
63+
64+
final EntityPersister entityPersister = entityMappingType.getEntityPersister();
65+
66+
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
67+
final Collection<?> cachedPkResolutions =
68+
persistenceContext.getNaturalIdResolutions()
69+
.getCachedPkResolutions( entityPersister );
70+
final boolean loggerDebugEnabled = LoaderLogging.LOADER_LOGGER.isDebugEnabled();
71+
for ( Object pk : cachedPkResolutions ) {
72+
final EntityKey entityKey = session.generateEntityKey( pk, entityPersister );
73+
final Object entity = persistenceContext.getEntity( entityKey );
74+
final EntityEntry entry = persistenceContext.getEntry( entity );
75+
76+
if ( entry == null ) {
77+
if ( loggerDebugEnabled ) {
78+
LoaderLogging.LOADER_LOGGER.debugf(
79+
"Cached natural-id/pk resolution linked to null EntityEntry in persistence context : %s#%s",
80+
entityMappingType.getEntityName(),
81+
pk
82+
);
83+
}
84+
continue;
85+
}
86+
87+
if ( !entry.requiresDirtyCheck( entity ) ) {
88+
continue;
89+
}
90+
91+
// MANAGED is the only status we care about here...
92+
if ( entry.getStatus() != Status.MANAGED ) {
93+
continue;
94+
}
95+
96+
persistenceContext.getNaturalIdResolutions().handleSynchronization( pk, entity, entityPersister );
97+
}
98+
}
99+
31100
}

hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate.internal;
66

77
import java.util.List;
8-
import java.util.Set;
98

109
import org.hibernate.CacheMode;
1110
import org.hibernate.LockOptions;
@@ -16,10 +15,11 @@
1615
import org.hibernate.graph.GraphSemantic;
1716
import org.hibernate.graph.RootGraph;
1817
import org.hibernate.graph.spi.RootGraphImplementor;
19-
import org.hibernate.internal.util.collections.CollectionHelper;
2018
import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions;
2119
import org.hibernate.persister.entity.EntityPersister;
2220

21+
import static org.hibernate.internal.NaturalIdHelper.performAnyNeededCrossReferenceSynchronizations;
22+
2323
/**
2424
* @author Steve Ebersole
2525
*/
@@ -82,6 +82,8 @@ public NaturalIdMultiLoadAccess<T> enableOrderedReturn(boolean enabled) {
8282
@Override
8383
@SuppressWarnings( "unchecked" )
8484
public List<T> multiLoad(Object... ids) {
85+
performAnyNeededCrossReferenceSynchronizations( true, entityDescriptor, session );
86+
8587
final CacheMode sessionCacheMode = session.getCacheMode();
8688
boolean cacheModeChanged = false;
8789

@@ -110,7 +112,6 @@ public List<T> multiLoad(Object... ids) {
110112
}
111113

112114
try {
113-
session.autoFlushIfRequired( (Set) CollectionHelper.setOf( entityDescriptor.getQuerySpaces() ) );
114115
return (List<T>) entityDescriptor.getMultiNaturalIdLoader().multiLoad( ids, this, session );
115116
}
116117
finally {

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiNaturalIdLoader.java

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
1515
import org.hibernate.metamodel.mapping.EntityMappingType;
1616

17-
import java.util.ArrayList;
1817
import java.util.Collections;
1918
import java.util.List;
2019

20+
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
21+
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
22+
import static org.hibernate.loader.ast.internal.LoaderHelper.upgradeLock;
23+
2124
/**
2225
* @author Jan Schatteman
2326
*/
@@ -57,7 +60,18 @@ private <K> List<E> performUnorderedMultiLoad(K[] naturalIds, MultiNaturalIdLoad
5760
);
5861
}
5962

60-
protected abstract <K> List<E> unorderedMultiLoad(K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions);
63+
protected <K> List<E> unorderedMultiLoad(K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions) {
64+
final List<E> results = arrayList( naturalIds.length );
65+
final Object[] unresolvedIds =
66+
checkPersistenceContextForCachedResults( naturalIds, session, lockOptions, results );
67+
if ( !isEmpty( unresolvedIds ) ) {
68+
results.addAll( loadEntitiesWithUnresolvedIds(unresolvedIds, session, lockOptions) );
69+
}
70+
71+
return results;
72+
}
73+
74+
protected abstract List<E> loadEntitiesWithUnresolvedIds(Object[] unresolvedIds, SharedSessionContractImplementor session, LockOptions lockOptions);
6175

6276
private <K> List<E> performOrderedMultiLoad(K[] naturalIds, MultiNaturalIdLoadOptions options, SharedSessionContractImplementor session) {
6377
if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
@@ -75,19 +89,18 @@ protected <K> List<E> orderedMultiLoad( K[] naturalIds, SharedSessionContractImp
7589

7690
unorderedMultiLoad( naturalIds, session, lockOptions );
7791

78-
return handleResults( naturalIds, session, lockOptions );
92+
return sortResults( naturalIds, session, lockOptions );
7993
}
8094

81-
protected <K> List<E> handleResults( K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions ) {
82-
List<E> results = new ArrayList<>(naturalIds.length);
95+
protected <K> List<E> sortResults( K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions ) {
96+
List<E> results = arrayList(naturalIds.length);
8397
for ( int i = 0; i < naturalIds.length; i++ ) {
8498
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
8599

86100
Object id = persistenceContext.getNaturalIdResolutions().findCachedIdByNaturalId( naturalIds[i], getEntityDescriptor() );
87101

88102
// Id can be null if a non-existent natural id is requested
89-
Object entity = id == null ? null
90-
: persistenceContext.getEntity( new EntityKey( id, getEntityDescriptor().getEntityPersister() ) );
103+
Object entity = (id == null) ? null : persistenceContext.getEntity( new EntityKey( id, getEntityDescriptor().getEntityPersister() ) );
91104
if ( entity != null && !options.isReturnOfDeletedEntitiesEnabled() ) {
92105
// make sure it is not DELETED
93106
final EntityEntry entry = persistenceContext.getEntry( entity );
@@ -105,6 +118,38 @@ protected <K> List<E> handleResults( K[] naturalIds, SharedSessionContractImplem
105118
return results;
106119
}
107120

121+
private <K> Object[] checkPersistenceContextForCachedResults( K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions, List<E> results ) {
122+
List<K> unresolvedIds = arrayList(naturalIds.length);
123+
124+
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
125+
for ( int i = 0; i < naturalIds.length; i++ ) {
126+
127+
final Object normalizedNaturalId = getEntityDescriptor().getNaturalIdMapping().normalizeInput( naturalIds[i] );
128+
Object id = persistenceContext.getNaturalIdResolutions().findCachedIdByNaturalId( normalizedNaturalId, getEntityDescriptor() );
129+
130+
// Id can be null if a non-existent natural id is requested, or a mutable natural id was changed and then deleted
131+
Object entity = id == null ? null : persistenceContext.getEntity( new EntityKey( id, getEntityDescriptor().getEntityPersister() ) );
132+
133+
if ( entity != null ) {
134+
// Entity is already in the persistence context
135+
final EntityEntry entry = persistenceContext.getEntry( entity );
136+
// either a managed entry, or a deleted one with returnDeleted enabled
137+
if ( !entry.getStatus().isDeletedOrGone() || options.isReturnOfDeletedEntitiesEnabled() ) {
138+
results.add( (E) persistenceContext.proxyFor(entity) );
139+
upgradeLock( entity, entry, lockOptions, session.getSession().asEventSource() );
140+
}
141+
}
142+
else {
143+
// entity either doesn't exist or hasn't been loaded in the PC yet, in both cases we add the natural id
144+
// to the ids that still need to be recovered, in case the id corresponds to a non-existent
145+
// instance nothing will be in the results for it, which is ok in unordered multiload
146+
unresolvedIds.add(naturalIds[i]);
147+
}
148+
}
149+
150+
return unresolvedIds.toArray( new Object[0] );
151+
}
152+
108153
@Override
109154
public EntityMappingType getLoadable() {
110155
return getEntityDescriptor();

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ protected BasicAttributeMapping getNaturalIdAttribute() {
4444
}
4545

4646
@Override
47-
public <K> List<E> unorderedMultiLoad( K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions ) {
47+
public List<E> loadEntitiesWithUnresolvedIds(Object[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions) {
4848

4949
final SessionFactoryImplementor sessionFactory = session.getFactory();
5050

@@ -77,17 +77,17 @@ public LockOptions getLockOptions() {
7777
} );
7878

7979
return LoaderHelper.loadByArrayParameter(
80-
naturalIds,
81-
sqlAst,
82-
jdbcSelectOperation,
83-
jdbcParameter,
84-
arrayJdbcMapping,
85-
null,
86-
null,
87-
null,
88-
lockOptions,
89-
session.isDefaultReadOnly(),
90-
session
80+
naturalIds,
81+
sqlAst,
82+
jdbcSelectOperation,
83+
jdbcParameter,
84+
arrayJdbcMapping,
85+
null,
86+
null,
87+
null,
88+
lockOptions,
89+
session.isDefaultReadOnly(),
90+
session
9191
);
9292
}
9393

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderInPredicate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public MultiNaturalIdLoaderInPredicate(EntityMappingType entityDescriptor) {
2222
}
2323

2424
@Override
25-
public <K> List<E> unorderedMultiLoad(K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions) {
25+
public List<E> loadEntitiesWithUnresolvedIds(Object[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions) {
2626

2727
final SessionFactoryImplementor sessionFactory = session.getFactory();
2828

0 commit comments

Comments
 (0)