diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 0e3e3571fa..adbb2474d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -533,7 +533,6 @@ public abstract class BaseSqmToSqlAstConverter private void consumeAttributeJoin(SqmAttributeJoin sqmJoin, TableGroup lhsTableGroup) { assert fromClauseIndex.findTableGroup( sqmJoin.getNavigablePath() ) == null; - assert fromClauseIndex.findTableGroupJoin( sqmJoin.getNavigablePath() ) == null; final SqmPathSource pathSource = sqmJoin.getReferencedPathSource(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java index bf929b0dc2..83ee17633b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java @@ -28,7 +28,7 @@ import org.jboss.logging.Logger; public class FromClauseIndex extends SimpleFromClauseAccessImpl { private static final Logger log = Logger.getLogger( FromClauseIndex.class ); - private Map tableGroupJoinMap; +// private Map tableGroupJoinMap; private final Map tableGroupByAliasXref = new HashMap<>(); /** @@ -76,9 +76,9 @@ public class FromClauseIndex extends SimpleFromClauseAccessImpl { return tableGroupMap.containsKey( fromElement.getNavigablePath() ); } - public TableGroupJoin findTableGroupJoin(NavigablePath navigablePath) { - return tableGroupJoinMap == null ? null : tableGroupJoinMap.get( navigablePath ); - } +// public TableGroupJoin findTableGroupJoin(NavigablePath navigablePath) { +// return tableGroupJoinMap == null ? null : tableGroupJoinMap.get( navigablePath ); +// } public SqmAttributeJoin findFetchedJoinByPath(NavigablePath path) { return fetchesByPath == null ? null : fetchesByPath.get( path ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java index 7d02a57a28..920316ca1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SimpleFromClauseAccessImpl.java @@ -20,26 +20,18 @@ import org.hibernate.sql.ast.tree.from.TableGroup; */ public class SimpleFromClauseAccessImpl implements FromClauseAccess { protected final Map tableGroupMap = new HashMap<>(); - private final Map tableGroupMapNoAlias = new HashMap<>(); public SimpleFromClauseAccessImpl() { } @Override public TableGroup findTableGroup(NavigablePath navigablePath) { - TableGroup tableGroup = tableGroupMap.get( navigablePath ); - if ( tableGroup == null && !containsAlias( navigablePath ) ) { - return tableGroupMapNoAlias.get( navigablePath ); - } - return tableGroup; + return tableGroupMap.get( navigablePath ); } @Override public void registerTableGroup(NavigablePath navigablePath, TableGroup tableGroup) { final TableGroup previous = tableGroupMap.put( navigablePath, tableGroup ); - if ( containsAlias( navigablePath ) ) { - tableGroupMapNoAlias.put( getPathWithoutAlias( navigablePath ), tableGroup ); - } if ( previous != null ) { SqlTreeCreationLogger.LOGGER.debugf( "Registration of TableGroup [%s] for NavigablePath [%s] overrode previous registration : %s", @@ -48,6 +40,9 @@ public class SimpleFromClauseAccessImpl implements FromClauseAccess { previous ); } + if ( containsAlias( navigablePath ) ) { + tableGroupMap.put( getPathWithoutAlias( navigablePath ), tableGroup ); + } } protected boolean containsAlias(NavigablePath navigablePath) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/manytoone/EntityWithLazyManyToOneSelfReferenceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/manytoone/EntityWithLazyManyToOneSelfReferenceTest.java new file mode 100644 index 0000000000..dd4bfdaad5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/manytoone/EntityWithLazyManyToOneSelfReferenceTest.java @@ -0,0 +1,225 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * 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 + */ +package org.hibernate.orm.test.sql.exec.manytoone; + +import java.util.List; + +import org.hibernate.Hibernate; +import org.hibernate.stat.spi.StatisticsImplementor; + +import org.hibernate.testing.orm.domain.gambit.EntityWithLazyManyToOneSelfReference; +import org.hibernate.testing.orm.domain.gambit.EntityWithManyToOneSelfReference; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.orm.junit.ServiceRegistry; +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.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Steve Ebersole + */ +@DomainModel( + annotatedClasses = { + EntityWithLazyManyToOneSelfReference.class + } +) +@ServiceRegistry +@SessionFactory(generateStatistics = true) +public class EntityWithLazyManyToOneSelfReferenceTest { + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + final EntityWithLazyManyToOneSelfReference entity1 = new EntityWithLazyManyToOneSelfReference( + 1, + "first", + Integer.MAX_VALUE + ); + + final EntityWithLazyManyToOneSelfReference entity2 = new EntityWithLazyManyToOneSelfReference( + 2, + "second", + Integer.MAX_VALUE, + entity1 + ); + + scope.inTransaction( session -> { + session.save( entity1 ); + session.save( entity2 ); + } ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "delete from EntityWithLazyManyToOneSelfReference" ).executeUpdate(); + } + ); + } + + @Test + public void testHqlSelectImplicitJoin(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithLazyManyToOneSelfReference queryResult = session.createQuery( + "select e from EntityWithLazyManyToOneSelfReference e where e.other.name = 'first'", + EntityWithLazyManyToOneSelfReference.class + ).uniqueResult(); + + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + assertThat( queryResult.getName(), is( "second" ) ); + + EntityWithLazyManyToOneSelfReference other = queryResult.getOther(); + assertFalse( Hibernate.isInitialized( other ) ); + assertThat( other.getName(), is( "first" ) ); + + assertThat( statistics.getPrepareStatementCount(), is( 2L ) ); + } + ); + } + + @Test + public void testGetEntity(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithLazyManyToOneSelfReference loaded = session.get( + EntityWithLazyManyToOneSelfReference.class, + 2 + ); + + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + + assertThat( loaded, notNullValue() ); + assertThat( loaded.getName(), is( "second" ) ); + + EntityWithLazyManyToOneSelfReference other = loaded.getOther(); + assertFalse( Hibernate.isInitialized( other ) ); + assertThat( other.getName(), is( "first" ) ); + + assertThat( statistics.getPrepareStatementCount(), is( 2L ) ); + } + ); + + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithLazyManyToOneSelfReference loaded = session.get( + EntityWithLazyManyToOneSelfReference.class, + 1 + ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + assertThat( loaded, notNullValue() ); + assertThat( loaded.getName(), is( "first" ) ); + + EntityWithLazyManyToOneSelfReference other = loaded.getOther(); + assertTrue( Hibernate.isInitialized( other ) ); + assertThat( other, nullValue() ); + } + ); + } + + @Test + public void testHqlSelectField(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final String value = session.createQuery( + "select e.name from EntityWithLazyManyToOneSelfReference e where e.other.name = 'first'", + String.class + ).uniqueResult(); + assertThat( value, equalTo( "second" ) ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + } + ); + } + + @Test + public void testHqlSelectWithJoin(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithLazyManyToOneSelfReference result = session.createQuery( + "select e from EntityWithLazyManyToOneSelfReference e join e.other o where o.name = 'first'", + EntityWithLazyManyToOneSelfReference.class + ).uniqueResult(); + assertThat( result.getName(), equalTo( "second" ) ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + + EntityWithLazyManyToOneSelfReference other = result.getOther(); + assertFalse( Hibernate.isInitialized( other ) ); + assertThat( other.getName(), is( "first" ) ); + + assertThat( statistics.getPrepareStatementCount(), is( 2L ) ); } + ); + } + + @Test + public void testHqlSelectWithFetchJoin(SessionFactoryScope scope) { + StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); + statistics.clear(); + scope.inTransaction( + session -> { + final EntityWithLazyManyToOneSelfReference result = session.createQuery( + "select e from EntityWithLazyManyToOneSelfReference e join fetch e.other k where k.name = 'first'", + EntityWithLazyManyToOneSelfReference.class + ).uniqueResult(); + assertThat( result.getName(), equalTo( "second" ) ); + assertThat( statistics.getPrepareStatementCount(), is( 1L ) ); + + assertTrue( Hibernate.isInitialized( result.getOther() ) ); + } + ); + } + + @Test + @FailureExpected + public void testGetByMultipleIds(SessionFactoryScope scope) { + + scope.inTransaction( + session -> { + final List list = session.byMultipleIds( + EntityWithLazyManyToOneSelfReference.class ) + .multiLoad( 1, 3 ); + assert list.size() == 1; + final EntityWithLazyManyToOneSelfReference loaded = list.get( 0 ); + assert loaded != null; + assertThat( loaded.getName(), equalTo( "first" ) ); + } + ); + + scope.inTransaction( + session -> { + final List list = session.byMultipleIds( + EntityWithLazyManyToOneSelfReference.class ) + .multiLoad( 2, 3 ); + assert list.size() == 1; + final EntityWithLazyManyToOneSelfReference loaded = list.get( 0 ); + assert loaded != null; + assertThat( loaded.getName(), equalTo( "second" ) ); + assert loaded.getOther() != null; + assertThat( loaded.getOther().getName(), equalTo( "first" ) ); + } + ); + } +}