Fix foreign key direction issues

This commit is contained in:
Christian Beikov 2021-05-06 16:54:47 +02:00
parent 0157af79f7
commit e122ac33dc
6 changed files with 36 additions and 52 deletions

View File

@ -90,6 +90,7 @@ import org.hibernate.type.CollectionType;
import org.hibernate.type.ComponentType; import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
@ -969,8 +970,10 @@ public class MappingModelCreationHelper {
.getEntityPersister( bootValueMapping.getReferencedEntityName() ); .getEntityPersister( bootValueMapping.getReferencedEntityName() );
String referencedPropertyName; String referencedPropertyName;
boolean swapDirection = false;
if ( bootValueMapping instanceof OneToOne ) { if ( bootValueMapping instanceof OneToOne ) {
OneToOne oneToOne = (OneToOne) bootValueMapping; OneToOne oneToOne = (OneToOne) bootValueMapping;
swapDirection = oneToOne.getForeignKeyType() == ForeignKeyDirection.TO_PARENT;
referencedPropertyName = oneToOne.getMappedByProperty(); referencedPropertyName = oneToOne.getMappedByProperty();
if ( referencedPropertyName == null ) { if ( referencedPropertyName == null ) {
referencedPropertyName = oneToOne.getReferencedPropertyName(); referencedPropertyName = oneToOne.getReferencedPropertyName();
@ -1054,7 +1057,8 @@ public class MappingModelCreationHelper {
keySelectableMapping, keySelectableMapping,
simpleFkTarget, simpleFkTarget,
(owner) -> ( (PropertyBasedMapping) simpleFkTarget ).getPropertyAccess().getGetter().get( owner ), (owner) -> ( (PropertyBasedMapping) simpleFkTarget ).getPropertyAccess().getGetter().get( owner ),
bootValueMapping.isReferenceToPrimaryKey() bootValueMapping.isReferenceToPrimaryKey(),
swapDirection
); );
attributeMapping.setForeignKeyDescriptor( foreignKeyDescriptor ); attributeMapping.setForeignKeyDescriptor( foreignKeyDescriptor );
} }
@ -1062,6 +1066,7 @@ public class MappingModelCreationHelper {
final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor( final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor(
(EmbeddableValuedModelPart) fkTarget, (EmbeddableValuedModelPart) fkTarget,
bootValueMapping, bootValueMapping,
swapDirection,
dialect, dialect,
creationProcess creationProcess
); );

View File

@ -68,12 +68,31 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
BasicValuedModelPart targetModelPart, BasicValuedModelPart targetModelPart,
Function<Object, Object> disassemblyValueExtractor, Function<Object, Object> disassemblyValueExtractor,
boolean refersToPrimaryKey) { boolean refersToPrimaryKey) {
this( keySelectableMapping, targetModelPart, disassemblyValueExtractor, refersToPrimaryKey, false );
}
public SimpleForeignKeyDescriptor(
SelectableMapping keySelectableMapping,
BasicValuedModelPart targetModelPart,
Function<Object, Object> disassemblyValueExtractor,
boolean refersToPrimaryKey,
boolean swapDirection) {
assert keySelectableMapping != null; assert keySelectableMapping != null;
assert targetModelPart != null; assert targetModelPart != null;
assert disassemblyValueExtractor != null; assert disassemblyValueExtractor != null;
this.keySide = BasicAttributeMapping.withSelectableMapping( targetModelPart, keySelectableMapping ); final BasicValuedModelPart keyModelPart = BasicAttributeMapping.withSelectableMapping(
this.targetSide = targetModelPart; targetModelPart,
keySelectableMapping
);
if ( swapDirection ) {
this.keySide = targetModelPart;
this.targetSide = keyModelPart;
}
else {
this.keySide = keyModelPart;
this.targetSide = targetModelPart;
}
this.disassemblyValueExtractor = disassemblyValueExtractor; this.disassemblyValueExtractor = disassemblyValueExtractor;
this.refersToPrimaryKey = refersToPrimaryKey; this.refersToPrimaryKey = refersToPrimaryKey;
} }

View File

@ -646,7 +646,7 @@ public class ToOneAttributeMapping
else { else {
// case 1.1 // case 1.1
keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, isKeyReferringSide, creationState ); keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, isKeyReferringSide, creationState );
selectByUniqueKey = true; selectByUniqueKey = bidirectionalAttributeName != null;
} }
if ( fetchTiming == FetchTiming.IMMEDIATE ) { if ( fetchTiming == FetchTiming.IMMEDIATE ) {

View File

@ -10,6 +10,7 @@ import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.AbstractFetchParent; import org.hibernate.sql.results.graph.AbstractFetchParent;
@ -67,7 +68,7 @@ public class EmbeddableForeignKeyResultImpl<T>
final ToOneAttributeMapping toOne = (ToOneAttributeMapping) fetchable; final ToOneAttributeMapping toOne = (ToOneAttributeMapping) fetchable;
shouldSelect = selected && !creationState.isAssociationKeyVisited( shouldSelect = selected && !creationState.isAssociationKeyVisited(
toOne.getForeignKeyDescriptor().getAssociationKey() toOne.getForeignKeyDescriptor().getAssociationKey()
); ) && !ForeignKeyDescriptor.PART_NAME.equals( getNavigablePath().getLocalName() );
} }
else { else {
shouldSelect = selected; shouldSelect = selected;

View File

@ -64,56 +64,14 @@ public class EagerKeyManyToOneTest {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
try { try {
/*
select
card0_.id as id1_0_0_,
card0_.field_card_id as field_ca2_0_0_,
card0_."field_key_id" as field_ke3_0_0_,
cardfield1_.card_id as card_id1_1_1_,
cardfield1_."key_id" as key_id2_1_1_,
card2_.id as id1_0_2_,
card2_.field_card_id as field_ca2_0_2_,
card2_."field_key_id" as field_ke3_0_2_,
key3_.id as id1_2_3_
from
Card card0_
left outer join
CardField cardfield1_
on card0_.field_card_id=cardfield1_.card_id
and card0_."field_key_id"=cardfield1_."key_id"
left outer join
Card card2_
on cardfield1_.card_id=card2_.id
left outer join
"key" key3_
on cardfield1_."key_id"=key3_.id
where
card0_.id=?
11:08:42,367 TRACE BasicBinder:64 - binding parameter [1] as [VARCHAR] - [cardId]
11:08:42,370 TRACE BasicExtractor:60 - extracted value ([id1_0_2_] : [VARCHAR]) - [cardId]
11:08:42,370 TRACE BasicExtractor:60 - extracted value ([id1_2_3_] : [VARCHAR]) - [keyId]
11:08:42,370 TRACE BasicExtractor:60 - extracted value ([card_id1_1_1_] : [VARCHAR]) - [cardId]
11:08:42,370 TRACE BasicExtractor:60 - extracted value ([key_id2_1_1_] : [VARCHAR]) - [keyId]
11:08:42,371 TRACE BasicExtractor:60 - extracted value ([field_ca2_0_2_] : [VARCHAR]) - [cardId]
11:08:42,372 TRACE BasicExtractor:60 - extracted value ([field_ke3_0_2_] : [VARCHAR]) - [keyId]
11:08:42,372 DEBUG SQL:144 -
select
key0_.id as id1_2_0_
from
"key" key0_
where
key0_.id=?
11:08:42,372 TRACE BasicBinder:64 - binding parameter [1] as [VARCHAR] - [keyId]
*/
Card card = session.get( Card.class, CARD_ID ); Card card = session.get( Card.class, CARD_ID );
CardField cf = card.getField(); CardField cf = card.getField();
assertSame( card, cf.getPrimaryKey().getCard() ); assertSame( card, cf.getPrimaryKey().getCard() );
statementInspector.assertExecutedCount( 2 ); statementInspector.assertExecutedCount( 2 );
// Since CardField and Key have no additional state, it's not necessary to join their tables // Since Key have no additional state, it's not necessary to join their tables
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 2 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
} }
catch (StackOverflowError soe) { catch (StackOverflowError soe) {

View File

@ -82,7 +82,7 @@ public class LazyManyToOneEmbeddedIdWithToOneFKTest {
System system = session.get( System.class, 1 ); System system = session.get( System.class, 1 );
assertThat( system, is( notNullValue() ) ); assertThat( system, is( notNullValue() ) );
statementInspector.assertExecutedCount( 1 ); statementInspector.assertExecutedCount( 2 );
assertThat( system.getId(), is( 1 ) ); assertThat( system.getId(), is( 1 ) );
@ -98,8 +98,9 @@ public class LazyManyToOneEmbeddedIdWithToOneFKTest {
SystemUser user = system.getUser(); SystemUser user = system.getUser();
assertThat( user, is( notNullValue() ) ); assertThat( user, is( notNullValue() ) );
statementInspector.assertExecutedCount( 1 ); statementInspector.assertExecutedCount( 2 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
statementInspector.clear(); statementInspector.clear();
assertThat( user.getName(), is( "Fab" ) ); assertThat( user.getName(), is( "Fab" ) );