Loading circula bidirectional fetch associations not referring to PK

This commit is contained in:
Andrea Boriero 2021-08-16 13:06:20 +02:00 committed by Andrea Boriero
parent 053153847b
commit 7e4275c46e
7 changed files with 70 additions and 30 deletions

View File

@ -225,7 +225,7 @@ public class ToOneAttributeMapping
if ( bidirectionalAttributeName == null ) { if ( bidirectionalAttributeName == null ) {
this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty( this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty(
bootValue.getReferencedPropertyName(), referencedPropertyName,
'.' '.'
); );
} }

View File

@ -10,6 +10,7 @@ import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.Association; import org.hibernate.metamodel.mapping.Association;
@ -18,8 +19,10 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.BiDirectionalFetch; import org.hibernate.sql.results.graph.BiDirectionalFetch;
@ -43,7 +46,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Association { public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Association {
private final FetchTiming timing; private final FetchTiming timing;
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final Fetchable fetchable; private final ToOneAttributeMapping fetchable;
private final FetchParent fetchParent; private final FetchParent fetchParent;
private final LockMode lockMode; private final LockMode lockMode;
@ -53,7 +56,7 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc
FetchTiming timing, FetchTiming timing,
NavigablePath navigablePath, NavigablePath navigablePath,
FetchParent fetchParent, FetchParent fetchParent,
Fetchable fetchable, ToOneAttributeMapping fetchable,
LockMode lockMode, LockMode lockMode,
NavigablePath referencedNavigablePath) { NavigablePath referencedNavigablePath) {
this.timing = timing; this.timing = timing;
@ -174,10 +177,10 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc
private static class CircularFetchAssembler implements DomainResultAssembler { private static class CircularFetchAssembler implements DomainResultAssembler {
private final NavigablePath circularPath; private final NavigablePath circularPath;
private final JavaTypeDescriptor javaTypeDescriptor; private final JavaTypeDescriptor javaTypeDescriptor;
private final Fetchable fetchable; private final ToOneAttributeMapping fetchable;
public CircularFetchAssembler( public CircularFetchAssembler(
Fetchable fetchable, ToOneAttributeMapping fetchable,
NavigablePath circularPath, NavigablePath circularPath,
JavaTypeDescriptor javaTypeDescriptor) { JavaTypeDescriptor javaTypeDescriptor) {
this.fetchable = fetchable; this.fetchable = fetchable;
@ -196,22 +199,28 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc
else { else {
assert parentInitializer instanceof CollectionInitializer; assert parentInitializer instanceof CollectionInitializer;
final CollectionInitializer circ = (CollectionInitializer) parentInitializer; final CollectionInitializer circ = (CollectionInitializer) parentInitializer;
final EntityPersister entityPersister = (EntityPersister) ( (AttributeMapping) fetchable ).getMappedType();
final CollectionKey collectionKey = circ.resolveCollectionKey( rowProcessingState ); final CollectionKey collectionKey = circ.resolveCollectionKey( rowProcessingState );
final EntityKey entityKey = new EntityKey( final Object key = collectionKey.getKey();
collectionKey.getKey(),
(EntityPersister) ( (AttributeMapping) fetchable ).getMappedType()
);
final SharedSessionContractImplementor session = rowProcessingState.getJdbcValuesSourceProcessingState() final SharedSessionContractImplementor session = rowProcessingState.getJdbcValuesSourceProcessingState()
.getSession(); .getSession();
final PersistenceContext persistenceContext = session.getPersistenceContext(); final PersistenceContext persistenceContext = session.getPersistenceContext();
final Object proxy = persistenceContext.getProxy( entityKey ); if ( fetchable.getReferencedPropertyName() != null ) {
// it is conceivable there is a proxy, so check that first return loadByUniqueKey( entityPersister, key, session, persistenceContext );
if ( proxy == null ) { }
// otherwise look for an initialized version else {
return persistenceContext.getEntity( entityKey ); final EntityKey entityKey = new EntityKey( key, entityPersister );
final Object proxy = persistenceContext.getProxy( entityKey );
// it is conceivable there is a proxy, so check that first
if ( proxy == null ) {
// otherwise look for an initialized version
return persistenceContext.getEntity( entityKey );
}
return proxy;
} }
return proxy;
} }
} }
if ( initializer.getInitializedInstance() == null ) { if ( initializer.getInitializedInstance() == null ) {
@ -221,6 +230,37 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc
return initializer.getInitializedInstance(); return initializer.getInitializedInstance();
} }
private Object loadByUniqueKey(
EntityPersister entityPersister,
Object key,
SharedSessionContractImplementor session,
PersistenceContext persistenceContext) {
String uniqueKeyPropertyName = fetchable.getReferencedPropertyName();
EntityUniqueKey euk = new EntityUniqueKey(
entityPersister.getEntityName(),
uniqueKeyPropertyName,
key,
entityPersister.getIdentifierType(),
entityPersister.getEntityMode(),
session.getFactory()
);
Object entityInstance = persistenceContext.getEntity( euk );
if ( entityInstance == null ) {
entityInstance = ( (UniqueKeyLoadable) entityPersister ).loadByUniqueKey(
uniqueKeyPropertyName,
key,
session
);
// If the entity was not in the Persistence Context, but was found now,
// add it to the Persistence Context
if ( entityInstance != null ) {
persistenceContext.addEntity( euk, entityInstance );
}
}
return entityInstance;
}
private EntityInitializer resolveCircularInitializer(RowProcessingState rowProcessingState) { private EntityInitializer resolveCircularInitializer(RowProcessingState rowProcessingState) {
final Initializer initializer = rowProcessingState.resolveInitializer( circularPath ); final Initializer initializer = rowProcessingState.resolveInitializer( circularPath );
if ( initializer instanceof EntityInitializer ) { if ( initializer instanceof EntityInitializer ) {

View File

@ -10,7 +10,7 @@
'-//Hibernate/Hibernate Mapping DTD 3.0//EN' '-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd'> 'http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd'>
<hibernate-mapping package="org.hibernate.test.orphan" > <hibernate-mapping package="org.hibernate.orm.test.orphan" >
<class name="Mail" table="t_mail"> <class name="Mail" table="t_mail">

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.orphan; package org.hibernate.orm.test.orphan;
public class Mail { public class Mail {

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.orphan; package org.hibernate.orm.test.orphan;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
@ -20,8 +20,8 @@ import org.junit.jupiter.api.Test;
@TestForIssue(jiraKey = "HHH-565") @TestForIssue(jiraKey = "HHH-565")
@DomainModel( @DomainModel(
xmlMappings = { xmlMappings = {
"org/hibernate/test/orphan/User.hbm.xml", "org/hibernate/orm/test/orphan/User.hbm.xml",
"org/hibernate/test/orphan/Mail.hbm.xml" "org/hibernate/orm/test/orphan/Mail.hbm.xml"
} }
) )
@SessionFactory @SessionFactory

View File

@ -10,7 +10,7 @@
'-//Hibernate/Hibernate Mapping DTD 3.0//EN' '-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd'> 'http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd'>
<hibernate-mapping package="org.hibernate.test.orphan" > <hibernate-mapping package="org.hibernate.orm.test.orphan" >
<class name="User" table="t_user"> <class name="User" table="t_user">
@ -22,7 +22,7 @@
<column name="userid" length="32" not-null="true"/> <column name="userid" length="32" not-null="true"/>
</property> </property>
<set name="mail" table="mail" cascade="all, delete-orphan" inverse="true" lazy="false"> <set name="mails" table="mail" cascade="all, delete-orphan" inverse="true" lazy="false">
<key column="userid" property-ref="userid" not-null="false"/> <key column="userid" property-ref="userid" not-null="false"/>
<one-to-many class="Mail"/> <one-to-many class="Mail"/>
</set> </set>

View File

@ -11,7 +11,7 @@
* Created on May 3, 2005, 9:42 AM * Created on May 3, 2005, 9:42 AM
*/ */
package org.hibernate.test.orphan; package org.hibernate.orm.test.orphan;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -23,7 +23,7 @@ public class User {
private Integer id; private Integer id;
private String userid; private String userid;
private Set<Mail> mail = new HashSet(); private Set<Mail> mails = new HashSet();
public User() { public User() {
} }
@ -48,21 +48,21 @@ public class User {
this.userid = userid; this.userid = userid;
} }
public Set<Mail> getMail() { public Set<Mail> getMails() {
return mail; return mails;
} }
private void setMail(Set<Mail> mail) { private void setMails(Set<Mail> mails) {
this.mail = mail; this.mails = mails;
} }
public Mail addMail(String alias) { public Mail addMail(String alias) {
Mail mail = new Mail( alias, this ); Mail mail = new Mail( alias, this );
getMail().add( mail ); getMails().add( mail );
return mail; return mail;
} }
public void removeMail(Mail mail) { public void removeMail(Mail mail) {
getMail().remove( mail ); getMails().remove( mail );
} }
} }