From 8d026b05d27540b0ef13b5b43fe48ec1888b1757 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 14 Apr 2020 17:49:03 +0100 Subject: [PATCH] Fix extra query executed for Embedded fk when embeddable has a lazy ToOne association --- .../BasicEntityIdentifierMappingImpl.java | 5 + .../EmbeddedIdentifierMappingImpl.java | 4 + .../hibernate/query/sqm/internal/SqmUtil.java | 3 - .../EmbeddableForeignKeyResultImpl.java | 32 +- ...azyManyToOneEmbeddedIdWithToOneFKTest.java | 339 +++++++++++++++ ...anyToOneEmbeddedIdWithLazyToOneFKTest.java | 390 ++++++++++++++++++ .../ManyToOneEmbeddedIdWithToOneFKTest.java | 16 +- 7 files changed, 771 insertions(+), 18 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithLazyToOneFKTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java index e63acd016d..ee43419570 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java @@ -12,6 +12,7 @@ import java.util.function.Consumer; import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.ManagedEntity; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.mapping.PersistentClass; @@ -26,6 +27,7 @@ import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.spi.SqlExpressionResolver; @@ -88,6 +90,9 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa @Override public Object getIdentifier(Object entity, SharedSessionContractImplementor session) { + if ( entity instanceof HibernateProxy ) { + return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier(); + } return propertyAccess.getGetter().get( entity ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java index fc4bd54711..d1dc744849 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedIdentifierMappingImpl.java @@ -29,6 +29,7 @@ import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.sql.ast.Clause; @@ -113,6 +114,9 @@ public class EmbeddedIdentifierMappingImpl implements CompositeIdentifierMapping @Override public Object getIdentifier(Object entity, SharedSessionContractImplementor session) { + if ( entity instanceof HibernateProxy ) { + return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier(); + } return propertyAccess.getGetter().get( entity ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index d4209b4b9b..3887e0cefe 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -318,9 +318,6 @@ public class SqmUtil { // assume we have (or can create) a mapping for the parameter's Java type BasicType basicType = typeConfiguration.standardBasicTypeForJavaType( parameter.getParameterType() ); - if ( basicType == null ) { - return StandardBasicTypes.SERIALIZABLE; - } return basicType; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java index cbd1000c74..e1d601f12a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java @@ -31,6 +31,7 @@ import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode; +import org.hibernate.sql.results.graph.entity.internal.EntityFetchDelayedImpl; import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl; /** @@ -74,15 +75,28 @@ public class EmbeddableForeignKeyResultImpl extends AbstractFetchParent null, associatedEntityMappingType.getIdentifierMapping().getJavaTypeDescriptor() ); - Fetch fetch = new EntityFetchSelectImpl( - this, - singularAssociationAttributeMapping, - null, - false, - navigablePath.append( fetchable.getFetchableName() ), - domainResult, - creationState - ); + Fetch fetch; + if ( singularAssociationAttributeMapping.getMappedFetchStrategy().getTiming() == FetchTiming.DELAYED ) { + fetch = new EntityFetchDelayedImpl( + this, + singularAssociationAttributeMapping, + null, + false, + navigablePath.append( fetchable.getFetchableName() ), + domainResult + ); + } + else { + fetch = new EntityFetchSelectImpl( + this, + singularAssociationAttributeMapping, + null, + false, + navigablePath.append( fetchable.getFetchableName() ), + domainResult, + creationState + ); + } fetches.add( fetch ); } else { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java new file mode 100644 index 0000000000..8cb8be6851 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java @@ -0,0 +1,339 @@ +/* + * 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.compositefk; + +import java.io.Serializable; +import java.util.Objects; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.Hibernate; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +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.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; + +/** + * @author Andrea Boriero + */ +@DomainModel( + annotatedClasses = { + LazyManyToOneEmbeddedIdWithToOneFKTest.System.class, + LazyManyToOneEmbeddedIdWithToOneFKTest.SystemUser.class, + LazyManyToOneEmbeddedIdWithToOneFKTest.Subsystem.class + } +) +@SessionFactory(statementInspectorClass = SQLStatementInspector.class) +public class LazyManyToOneEmbeddedIdWithToOneFKTest { + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Subsystem subsystem = new Subsystem( 2, "sub1" ); + PK userKey = new PK( subsystem, "Fab" ); + SystemUser user = new SystemUser( userKey, "Fab" ); + + System system = new System( 1, "sub1" ); + system.setUser( user ); + + session.save( subsystem ); + session.save( user ); + session.save( system ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "delete from System" ).executeUpdate(); + session.createQuery( "delete from SystemUser" ).executeUpdate(); + session.createQuery( "delete from Subsystem" ).executeUpdate(); + } + ); + } + + @Test + public void testGet(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + System system = session.get( System.class, 1 ); + assertThat( system, is( notNullValue() ) ); + + statementInspector.assertExecutedCount( 2 ); + + assertThat( system.getId(), is( 1 ) ); + + assertFalse( Hibernate.isInitialized( system.getUser() ) ); + + PK pk = system.getUser().getPk(); + assertTrue( Hibernate.isInitialized( pk.subsystem ) ); + + assertThat( pk.username, is( "Fab" ) ); + assertThat( pk.subsystem.id, is( 2 ) ); + assertThat( pk.subsystem.getDescription(), is( "sub1" ) ); + + SystemUser user = system.getUser(); + assertThat( user, is( notNullValue() ) ); + + statementInspector.assertExecutedCount( 2 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); + + statementInspector.clear(); + assertThat( user.getName(), is( "Fab" ) ); + assertTrue( Hibernate.isInitialized( system.getUser() ) ); + + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + + } + ); + } + + @Test + public void testHql(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + System system = (System) session.createQuery( "from System e where e.id = :id" ) + .setParameter( "id", 1 ).uniqueResult(); + + assertThat( system, is( notNullValue() ) ); + + statementInspector.assertExecutedCount( 2 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); + + assertFalse( Hibernate.isInitialized( system.getUser() ) ); + + final PK pk = system.getUser().getPk(); + assertTrue( Hibernate.isInitialized( pk.subsystem ) ); + + assertThat( pk.username, is( "Fab" ) ); + assertThat( pk.subsystem.id, is( 2 ) ); + assertThat( pk.subsystem.getDescription(), is( "sub1" ) ); + + SystemUser user = system.getUser(); + assertThat( user, is( notNullValue() ) ); + statementInspector.assertExecutedCount( 2 ); + + statementInspector.clear(); + assertThat( user.getName(), is( "Fab" ) ); + assertTrue( Hibernate.isInitialized( system.getUser() ) ); + + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + + } + ); + } + + @Test + public void testHqlJoin(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + System system = session.createQuery( "from System e join e.user where e.id = :id", System.class ) + .setParameter( "id", 1 ).uniqueResult(); + statementInspector.assertExecutedCount( 2 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); + + assertFalse( Hibernate.isInitialized( system.getUser() ) ); + + assertThat( system, is( notNullValue() ) ); + SystemUser user = system.getUser(); + assertThat( user, is( notNullValue() ) ); + } + ); + } + + @Test + public void testHqlJoinFetch(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + System system = session.createQuery( + "from System e join fetch e.user where e.id = :id", + System.class + ) + .setParameter( "id", 1 ).uniqueResult(); + statementInspector.assertExecutedCount( 2 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); + + assertTrue( Hibernate.isInitialized( system.getUser() ) ); + + + assertThat( system, is( notNullValue() ) ); + SystemUser user = system.getUser(); + assertThat( user, is( notNullValue() ) ); + } + ); + } + + @Entity(name = "System") + public static class System { + @Id + private Integer id; + private String name; + + @ManyToOne(fetch = FetchType.LAZY) + SystemUser user; + + public System() { + } + + public System(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public SystemUser getUser() { + return user; + } + + public void setUser(SystemUser user) { + this.user = user; + } + } + + @Entity(name = "SystemUser") + public static class SystemUser { + + @EmbeddedId + private PK pk; + + private String name; + + public SystemUser() { + } + + public SystemUser(PK pk, String name) { + this.pk = pk; + this.name = name; + } + + public PK getPk() { + return pk; + } + + public void setPk(PK pk) { + this.pk = pk; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Embeddable + public static class PK implements Serializable { + + @ManyToOne + private Subsystem subsystem; + + private String username; + + public PK(Subsystem subsystem, String username) { + this.subsystem = subsystem; + this.username = username; + } + + private PK() { + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + PK pk = (PK) o; + return Objects.equals( subsystem, pk.subsystem ) && + Objects.equals( username, pk.username ); + } + + @Override + public int hashCode() { + return Objects.hash( subsystem, username ); + } + } + + @Entity(name = "Subsystem") + public static class Subsystem { + + @Id + private Integer id; + + private String description; + + public Subsystem() { + } + + public Subsystem(Integer id, String description) { + this.id = id; + this.description = description; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + +// public Integer getId() { +// return id; +// } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithLazyToOneFKTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithLazyToOneFKTest.java new file mode 100644 index 0000000000..a1a59c6458 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithLazyToOneFKTest.java @@ -0,0 +1,390 @@ +/* + * 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.compositefk; + +import java.io.Serializable; +import java.util.Objects; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.Hibernate; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +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.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Andrea Boriero + */ +@DomainModel( + annotatedClasses = { + ManyToOneEmbeddedIdWithLazyToOneFKTest.System.class, + ManyToOneEmbeddedIdWithLazyToOneFKTest.SystemUser.class, + ManyToOneEmbeddedIdWithLazyToOneFKTest.Subsystem.class + } +) +@SessionFactory(statementInspectorClass = SQLStatementInspector.class) +public class ManyToOneEmbeddedIdWithLazyToOneFKTest { + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Subsystem subsystem = new Subsystem( 2, "sub1" ); + PK userKey = new PK( subsystem, "Fab" ); + SystemUser user = new SystemUser( userKey, "Fab" ); + + System system = new System( 1, "sub1" ); + system.setUser( user ); + + session.save( subsystem ); + session.save( user ); + session.save( system ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "delete from System" ).executeUpdate(); + session.createQuery( "delete from SystemUser" ).executeUpdate(); + session.createQuery( "delete from Subsystem" ).executeUpdate(); + } + ); + } + + @Test + public void testGet(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + System system = session.get( System.class, 1 ); + assertThat( system, is( notNullValue() ) ); + assertThat( system.getId(), is( 1 ) ); + + assertTrue( Hibernate.isInitialized( system.getUser() ) ); + + PK pk = system.getUser().getPk(); + assertFalse( Hibernate.isInitialized( pk.subsystem ) ); + + SystemUser user = system.getUser(); + assertThat( user, is( notNullValue() ) ); + + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + + statementInspector.clear(); + assertThat( pk.username, is( "Fab" ) ); + + Subsystem subsystem = pk.getSubsystem(); + assertThat( subsystem.getId(), is( 2 ) ); + assertThat( subsystem.getDescription(), is( "sub1" ) ); + + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + } + ); + } + + @Test + public void testHql(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + /* + select + s1_0.id, + s1_0.name, + s1_0.user_subsystem_id, + s1_0.user_username + from + System as s1_0 + where + s1_0.id = ? + + select + s1_0.subsystem_id, + s1_0.username, + s1_0.name + from + SystemUser as s1_0 + where + ( + s1_0.subsystem_id, s1_0.username + ) in ( + ( + ?, ? + ) + ) + */ + scope.inTransaction( + session -> { + System system = (System) session.createQuery( "from System e where e.id = :id" ) + .setParameter( "id", 1 ).uniqueResult(); + + assertThat( system, is( notNullValue() ) ); + + statementInspector.assertExecutedCount( 2 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); + + assertTrue( Hibernate.isInitialized( system.getUser() ) ); + + final PK pk = system.getUser().getPk(); + assertFalse( Hibernate.isInitialized( pk.subsystem ) ); + + statementInspector.clear(); + assertThat( pk.username, is( "Fab" ) ); + + Subsystem subsystem = pk.getSubsystem(); + assertThat( subsystem.getId(), is( 2 ) ); + assertThat( subsystem.getDescription(), is( "sub1" ) ); + + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + } + ); + } + + @Test + public void testHqlJoin(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + System system = session.createQuery( "from System e join e.user where e.id = :id", System.class ) + .setParameter( "id", 1 ).uniqueResult(); + + statementInspector.assertExecutedCount( 2 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); + + assertThat( system, is( notNullValue() ) ); + + SystemUser user = system.getUser(); + assertThat( user, is( notNullValue() ) ); + + assertTrue( Hibernate.isInitialized( system.getUser() ) ); + assertFalse( Hibernate.isInitialized( system.getUser().getPk().subsystem ) ); + statementInspector.clear(); + PK pk = system.getUser().getPk(); + + assertThat( pk.username, is( "Fab" ) ); + + Subsystem subsystem = pk.getSubsystem(); + assertThat( subsystem.getId(), is( 2 ) ); + assertThat( subsystem.getDescription(), is( "sub1" ) ); + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + } + ); + } + + @Test + public void testHqlJoinFetch(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + System system = session.createQuery( + "from System e join fetch e.user where e.id = :id", + System.class + ).setParameter( "id", 1 ).uniqueResult(); + + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); + + assertThat( system, is( notNullValue() ) ); + SystemUser user = system.getUser(); + assertThat( user, is( notNullValue() ) ); + + statementInspector.clear(); + PK pk = system.getUser().getPk(); + + assertThat( pk.username, is( "Fab" ) ); + + Subsystem subsystem = pk.getSubsystem(); + assertThat( subsystem.getId(), is( 2 ) ); + assertThat( subsystem.getDescription(), is( "sub1" ) ); + statementInspector.assertExecutedCount( 1 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + + } + ); + } + + @Entity(name = "System") + public static class System { + @Id + private Integer id; + private String name; + + @ManyToOne + SystemUser user; + + public System() { + } + + public System(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public SystemUser getUser() { + return user; + } + + public void setUser(SystemUser user) { + this.user = user; + } + } + + @Entity(name = "SystemUser") + public static class SystemUser { + + @EmbeddedId + private PK pk; + + private String name; + + public SystemUser() { + } + + public SystemUser(PK pk, String name) { + this.pk = pk; + this.name = name; + } + + public PK getPk() { + return pk; + } + + public void setPk(PK pk) { + this.pk = pk; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Embeddable + public static class PK implements Serializable { + + @ManyToOne(fetch = FetchType.LAZY) + private Subsystem subsystem; + + private String username; + + public PK(Subsystem subsystem, String username) { + this.subsystem = subsystem; + this.username = username; + } + + private PK() { + } + + public Subsystem getSubsystem() { + return subsystem; + } + + public void setSubsystem(Subsystem subsystem) { + this.subsystem = subsystem; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + PK pk = (PK) o; + return Objects.equals( subsystem, pk.subsystem ) && + Objects.equals( username, pk.username ); + } + + @Override + public int hashCode() { + return Objects.hash( subsystem, username ); + } + } + + @Entity(name = "Subsystem") + public static class Subsystem { + + @Id + private Integer id; + + private String description; + + public Subsystem() { + } + + public Subsystem(Integer id, String description) { + this.id = id; + this.description = description; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getId() { + return id; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java index 9c38517af7..7234528f67 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java @@ -124,14 +124,18 @@ public class ManyToOneEmbeddedIdWithToOneFKTest { s2_0.id=? select - manytoonee0_.subsystem_id as subsyste3_2_0_, - manytoonee0_.username as username1_2_0_, - manytoonee0_.name as name2_2_0_ + s1_0.subsystem_id, + s1_0.username from - SystemUser manytoonee0_ + SystemUser as s1_0 where - manytoonee0_.subsystem_id=? - and manytoonee0_.username=? + ( + s1_0.subsystem_id, s1_0.username + ) in ( + ( + ?, ? + ) + ) */ scope.inTransaction( session -> {