diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java index a94483ac66..5972de4f43 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java @@ -49,6 +49,7 @@ import org.hibernate.sql.results.graph.entity.EntityValuedFetchable; import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchImpl; import org.hibernate.sql.results.graph.entity.internal.EntityFetchJoinedImpl; import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl; +import org.hibernate.sql.results.graph.entity.internal.EntityResultJoinedSubclassImpl; import org.hibernate.sql.results.internal.domain.CircularFetchImpl; import org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl; import org.hibernate.type.ForeignKeyDirection; @@ -167,8 +168,8 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping } in such case the mappedBy is "primaryKey.card" - the the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does not contain the "primaryKey" part - so in ored to recognize the bidirectionality the "primaryKey." is removed from the otherSidePropertyName value. + the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does not contain the "primaryKey" part, + so in order to recognize the bidirectionality the "primaryKey." is removed from the otherSidePropertyName value. */ // todo (6.0): find a better solution for the embeddable part name not in the NavigablePath bidirectionalAttributeName = StringHelper.subStringNullIfEmpty( @@ -399,30 +400,23 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping ); if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) { - fromClauseAccess.resolveTableGroup( - fetchablePath, - np -> { - final SqlAstJoinType sqlAstJoinType; - if ( isNullable ) { - sqlAstJoinType = SqlAstJoinType.LEFT; - } - else { - sqlAstJoinType = SqlAstJoinType.INNER; - } - - - final TableGroupJoin tableGroupJoin = createTableGroupJoin( - fetchablePath, - parentTableGroup, - null, - sqlAstJoinType, - lockMode, - creationState.getSqlAstCreationState() - ); - - return tableGroupJoin.getJoinedGroup(); - } - ); + if ( fetchParent instanceof EntityResultJoinedSubclassImpl && + ( (EntityPersister) fetchParent.getReferencedModePart() ).findDeclaredAttributeMapping( getPartName() ) == null ) { + final TableGroup tableGroupJoin = createTableGroupJoin( + fetchablePath, + lockMode, + creationState, + parentTableGroup + ); + fromClauseAccess.registerTableGroup( fetchablePath, tableGroupJoin ); + } + else { + fromClauseAccess.resolveTableGroup( + fetchablePath, + np -> + createTableGroupJoin( fetchablePath, lockMode, creationState, parentTableGroup ) + ); + } creationState.registerVisitedAssociationKey( foreignKeyDescriptor.getAssociationKey() ); return new EntityFetchJoinedImpl( @@ -496,6 +490,30 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping ); } + private TableGroup createTableGroupJoin( + NavigablePath fetchablePath, + LockMode lockMode, + DomainResultCreationState creationState, TableGroup parentTableGroup) { + final SqlAstJoinType sqlAstJoinType; + if ( isNullable ) { + sqlAstJoinType = SqlAstJoinType.LEFT; + } + else { + sqlAstJoinType = SqlAstJoinType.INNER; + } + + final TableGroupJoin tableGroupJoin = createTableGroupJoin( + fetchablePath, + parentTableGroup, + null, + sqlAstJoinType, + lockMode, + creationState.getSqlAstCreationState() + ); + + return tableGroupJoin.getJoinedGroup(); + } + @Override public int getNumberOfFetchables() { return getEntityMappingType().getNumberOfFetchables(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceEagerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceEagerTest.java new file mode 100644 index 0000000000..b4ef353c70 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceEagerTest.java @@ -0,0 +1,238 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.orm.test.inheritance.discriminator; + +import java.util.List; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.hibernate.Hibernate; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test cases for joined inheritance with eager fetching. + * + * @author Christian Beikov + */ +@DomainModel( + annotatedClasses = { + JoinedInheritanceEagerTest.BaseEntity.class, + JoinedInheritanceEagerTest.EntityA.class, + JoinedInheritanceEagerTest.EntityB.class, + JoinedInheritanceEagerTest.EntityC.class, + JoinedInheritanceEagerTest.EntityD.class + } +) +@SessionFactory +public class JoinedInheritanceEagerTest { + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + EntityC entityC = new EntityC( 1L ); + EntityD entityD = new EntityD( 2L ); + + EntityB entityB = new EntityB( 3L ); + entityB.setRelation( entityD ); + + EntityA entityA = new EntityA( 4L ); + entityA.setRelation( entityC ); + + session.persist( entityC ); + session.persist( entityD ); + session.persist( entityA ); + session.persist( entityB ); + } ); + } + + @AfterEach + public void cleanUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + EntityA entityA = session.get( EntityA.class, 4L ); + EntityB entityB = session.get( EntityB.class, 3L ); + EntityD entityD = session.get( EntityD.class, 2L ); + EntityC entityC = session.get( EntityC.class, 1L ); + + session.delete( entityD ); + session.delete( entityC ); + session.delete( entityA ); + session.delete( entityB ); + } + ); + } + + @Test + @TestForIssue(jiraKey = "HHH-12375") + public void joinFindEntity(SessionFactoryScope scope) { + scope.inTransaction( session -> { + EntityA entityA = session.get( EntityA.class, 4L ); + assertTrue( Hibernate.isInitialized( entityA.getRelation() ) ); + assertFalse( Hibernate.isInitialized( entityA.getAttributes() ) ); + } ); + } + + @Test + @TestForIssue(jiraKey = "HHH-12375") + public void joinFindParenEntity(SessionFactoryScope scope) { + scope.inTransaction( session -> { + BaseEntity baseEntity = session.get( BaseEntity.class, 4L ); + assertThat( baseEntity, notNullValue() ); + assertThat( baseEntity, instanceOf( EntityA.class ) ); + assertTrue( Hibernate.isInitialized( ( (EntityA) baseEntity ).getRelation() ) ); + assertFalse( Hibernate.isInitialized( ( (EntityA) baseEntity ).getAttributes() ) ); + } ); + + scope.inTransaction( session -> { + BaseEntity baseEntity = session.get( BaseEntity.class, 3L ); + assertThat( baseEntity, notNullValue() ); + assertThat( baseEntity, instanceOf( EntityB.class ) ); + assertTrue( Hibernate.isInitialized( ( (EntityB) baseEntity ).getRelation() ) ); + assertFalse( Hibernate.isInitialized( ( (EntityB) baseEntity ).getAttributes() ) ); + } ); + } + + @Test + @TestForIssue(jiraKey = "HHH-12375") + public void selectBaseType(SessionFactoryScope scope) { + scope.inTransaction( session -> { + List result = session.createQuery( "from BaseEntity" ).list(); + assertEquals( 2, result.size() ); + } ); + } + + @Entity(name = "BaseEntity") + @Inheritance(strategy = InheritanceType.JOINED) + public static class BaseEntity { + @Id + private Long id; + + public BaseEntity() { + } + + public BaseEntity(Long id) { + this.id = id; + } + } + + @Entity(name = "EntityA") + public static class EntityA extends BaseEntity { + @OneToMany(fetch = FetchType.LAZY) + private Set attributes; + @ManyToOne(fetch = FetchType.EAGER) + private EntityC relation; + + public EntityA() { + } + + public EntityA(Long id) { + super( id ); + } + + public void setRelation(EntityC relation) { + this.relation = relation; + } + + public EntityC getRelation() { + return relation; + } + + public Set getAttributes() { + return attributes; + } + } + + @Entity(name = "EntityB") + public static class EntityB extends BaseEntity { + @OneToMany(fetch = FetchType.LAZY) + private Set attributes; + @ManyToOne(fetch = FetchType.EAGER) + private EntityD relation; + + public EntityB() { + } + + public EntityB(Long id) { + super( id ); + } + + public void setRelation(EntityD relation) { + this.relation = relation; + } + + public EntityD getRelation() { + return relation; + } + + public Set getAttributes() { + return attributes; + } + } + + @Entity(name = "EntityC") + public static class EntityC { + @Id + private Long id; + + public EntityC() { + } + + public EntityC(Long id) { + this.id = id; + } + } + + @Entity(name = "EntityD") + public static class EntityD { + @Id + private Long id; + + public EntityD() { + } + + public EntityD(Long id) { + this.id = id; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java b/hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java index 29b699b1b9..25ce90f3db 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/inheritance/discriminator/JoinedInheritanceEagerTest.java @@ -23,9 +23,7 @@ */ package org.hibernate.test.inheritance.discriminator; -import java.util.List; import java.util.Set; - import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; @@ -34,7 +32,7 @@ import javax.persistence.InheritanceType; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; -import org.hibernate.Hibernate; +import org.hibernate.query.sqm.InterpretationException; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.DomainModel; @@ -44,11 +42,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -105,36 +98,6 @@ public class JoinedInheritanceEagerTest { ); } - @Test - @TestForIssue(jiraKey = "HHH-12375") - public void joinFindEntity(SessionFactoryScope scope) { - scope.inTransaction( session -> { - EntityA entityA = session.get( EntityA.class, 4L ); - assertTrue( Hibernate.isInitialized( entityA.getRelation() ) ); - assertFalse( Hibernate.isInitialized( entityA.getAttributes() ) ); - } ); - } - - @Test - @TestForIssue(jiraKey = "HHH-12375") - public void joinFindParenEntity(SessionFactoryScope scope) { - scope.inTransaction( session -> { - BaseEntity baseEntity = session.get( BaseEntity.class, 4L ); - assertThat( baseEntity, notNullValue() ); - assertThat( baseEntity, instanceOf( EntityA.class ) ); - assertTrue( Hibernate.isInitialized( ( (EntityA) baseEntity ).getRelation() ) ); - assertFalse( Hibernate.isInitialized( ( (EntityA) baseEntity ).getAttributes() ) ); - } ); - - scope.inTransaction( session -> { - BaseEntity baseEntity = session.get( BaseEntity.class, 3L ); - assertThat( baseEntity, notNullValue() ); - assertThat( baseEntity, instanceOf( EntityB.class ) ); - assertTrue( Hibernate.isInitialized( ( (EntityB) baseEntity ).getRelation() ) ); - assertFalse( Hibernate.isInitialized( ( (EntityB) baseEntity ).getAttributes() ) ); - } ); - } - @Test @TestForIssue(jiraKey = "HHH-12375") public void joinUnrelatedCollectionOnBaseType(SessionFactoryScope scope) { @@ -146,7 +109,7 @@ public class JoinedInheritanceEagerTest { session.createQuery( "from BaseEntity b join b.attributes" ).list(); fail( "Expected a resolution exception for property 'attributes'!" ); } - catch (IllegalArgumentException ex) { + catch (InterpretationException ex) { assertTrue( ex.getMessage().contains( "could not resolve property: attributes " ) ); } finally { @@ -157,15 +120,6 @@ public class JoinedInheritanceEagerTest { } - @Test - @TestForIssue(jiraKey = "HHH-12375") - public void selectBaseType(SessionFactoryScope scope) { - scope.inTransaction( session -> { - List result = session.createQuery( "from BaseEntity" ).list(); - assertEquals( result.size(), 2 ); - } ); - } - @Entity(name = "BaseEntity") @Inheritance(strategy = InheritanceType.JOINED) public static class BaseEntity {