From 43f310da9298a9d599029cc49741ffc09277a086 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 11 Apr 2022 14:43:36 +0200 Subject: [PATCH] HHH-15167 IllegalArgumentException occurred while calling setter for property --- .../internal/ToOneAttributeMapping.java | 32 +- .../toone/CriteriaJoinFetchTest.java | 348 ++++++++++++++++++ 2 files changed, 372 insertions(+), 8 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/association/toone/CriteriaJoinFetchTest.java 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 16a72b5502..38685256a0 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 @@ -775,6 +775,7 @@ public class ToOneAttributeMapping fetchablePath, fetchParent, parentNavigablePath, + fetchTiming, creationState ); } @@ -935,6 +936,7 @@ public class ToOneAttributeMapping NavigablePath fetchablePath, FetchParent fetchParent, NavigablePath parentNavigablePath, + FetchTiming fetchTiming, DomainResultCreationState creationState) { final NavigablePath referencedNavigablePath; final boolean hasBidirectionalFetchParent; @@ -1019,17 +1021,29 @@ public class ToOneAttributeMapping realParent = parentNavigablePath; } final TableGroup tableGroup = fromClauseAccess.getTableGroup( realParent ); + final DomainResult domainResult = foreignKeyDescriptor.createDomainResult( + fetchablePath, + tableGroup, + sideNature, + fetchParent, + creationState + ); + if ( fetchTiming == FetchTiming.IMMEDIATE ) { + return new EntityFetchSelectImpl( + fetchParent, + this, + fetchablePath, + domainResult, + isSelectByUniqueKey( sideNature ), + creationState + ); + } + return new EntityDelayedFetchImpl( fetchParent, this, fetchablePath, - foreignKeyDescriptor.createDomainResult( - fetchablePath, - tableGroup, - sideNature, - fetchParent, - creationState - ), + domainResult, isSelectByUniqueKey( sideNature ) ); } @@ -1097,8 +1111,10 @@ public class ToOneAttributeMapping || ( partMappingType != entityMappingType && !entityMappingType.getEntityPersister().isSubclassEntityName( partMappingType.getMappedJavaType().getJavaType().getTypeName() ) && !( (EntityMappingType) partMappingType ).getEntityPersister().isSubclassEntityName( entityMappingType.getEntityName() ) ) ) { - referencedNavigablePath = referencedNavigablePath.getParent(); + if ( referencedNavigablePath == null ) { + return null; + } partMappingType = creationState.resolveModelPart( referencedNavigablePath ).getPartMappingType(); } return referencedNavigablePath; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/association/toone/CriteriaJoinFetchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/association/toone/CriteriaJoinFetchTest.java new file mode 100644 index 0000000000..9a32e8059e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/association/toone/CriteriaJoinFetchTest.java @@ -0,0 +1,348 @@ +package org.hibernate.orm.test.jpa.association.toone; + +import java.util.List; + +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.jdbc.DefaultSQLStatementInspectorSettingProvider; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.hibernate.testing.orm.junit.SettingProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Fetch; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.SingularAttribute; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +@Jpa( + annotatedClasses = { + CriteriaJoinFetchTest.Customer.class, + CriteriaJoinFetchTest.Address.class, + CriteriaJoinFetchTest.Note.class + }, + settingProviders = { + @SettingProvider( + settingName = AvailableSettings.STATEMENT_INSPECTOR, + provider = DefaultSQLStatementInspectorSettingProvider.class + ) + } + +) +public class CriteriaJoinFetchTest { + + private static SQLStatementInspector statementInspector; + + @BeforeEach + public void setUp(EntityManagerFactoryScope scope) { + statementInspector = scope.getStatementInspector( SQLStatementInspector.class ); + + scope.inTransaction( + entityManager -> { + + Customer customer0 = new Customer( 1, "William P. Keaton" ); + Customer customer1 = new Customer( 2, "Kate P. Hudson" ); + + entityManager.persist( customer0 ); + entityManager.persist( customer1 ); + + Note note0 = new Note( 3, "Note for address 0" ); + + Note note1 = new Note( 4, "Note for address 1" ); + + Address address0 = new Address( 5, "Flit street", "London", note0, customer0 ); + Address address1 = new Address( 6, "via Marconi", "Pavia", note1, customer1 ); + + entityManager.persist( address0 ); + entityManager.persist( address1 ); + + + customer0.setAddress( address0 ); + customer1.setAddress( address1 ); + } + ); + } + + @AfterEach + public void tearDown(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + entityManager.createQuery( "delete from Address" ).executeUpdate(); + entityManager.createQuery( "delete from Note" ).executeUpdate(); + entityManager.createQuery( "delete from Customer" ).executeUpdate(); + } + ); + } + + @Test + public void testCriteriaFetchSingularAttribute(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + statementInspector.clear(); + final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + final CriteriaQuery criteriaQuery = criteriaBuilder.createQuery( Customer.class ); + + final From customer = criteriaQuery.from( Customer.class ); + + final EntityType customerEntityType = entityManager.getMetamodel() + .entity( Customer.class ); + + final SingularAttribute address = (SingularAttribute) customerEntityType.getSingularAttribute( + "address" ); + customer.fetch( address, JoinType.INNER ); + criteriaQuery.select( customer ); + + final TypedQuery query = entityManager.createQuery( criteriaQuery ); + List result = query.getResultList(); + assertThat( result.size(), is( 2 ) ); + assertThat( statementInspector.getSqlQueries().size(), is( 3 ) ); + Customer customer1 = result.get( 0 ); + Note note = customer1.getAddress().getNote(); + assertThat( note, notNullValue() ); + if ( customer1.getId() == 1 ) { + assertThat( note.getId(), is( 3 ) ); + } + else { + assertThat( note.getId(), is( 4 ) ); + } + } + ); + } + + @Test + public void testCriteriaFetchSingularAttribute2(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + statementInspector.clear(); + final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + final CriteriaQuery criteriaQuery = criteriaBuilder.createQuery( Customer.class ); + + final From customer = criteriaQuery.from( Customer.class ); + + final EntityType customerEntityType = entityManager.getMetamodel() + .entity( Customer.class ); + + final SingularAttribute address = (SingularAttribute) customerEntityType.getSingularAttribute( + "address" ); + final Fetch fetch = customer.fetch( address, JoinType.INNER ); + + fetch.fetch( entityManager.getMetamodel() + .entity( Address.class ).getSingularAttribute( "note" ), JoinType.INNER ); + criteriaQuery.select( customer ); + + final TypedQuery query = entityManager.createQuery( criteriaQuery ); + + final List result = query.getResultList(); + + assertThat( result.size(), is( 2 ) ); + assertThat( statementInspector.getSqlQueries().size(), is( 1 ) ); + + final Customer customer1 = result.get( 0 ); + final Note note = customer1.getAddress().getNote(); + + assertThat( note, notNullValue() ); + if ( customer1.getId() == 1 ) { + assertThat( note.getId(), is( 3 ) ); + } + else { + assertThat( note.getId(), is( 4 ) ); + } + assertThat( statementInspector.getSqlQueries().size(), is( 1 ) ); + } + ); + } + + + @Test + public void testFind(EntityManagerFactoryScope scope) { + statementInspector.clear(); + scope.inTransaction( + entityManager -> { + Customer customer = entityManager.find( Customer.class, 2 ); + final Note note = customer.getAddress().getNote(); + + assertThat( note.getId(), is( 4 ) ); + assertThat( statementInspector.getSqlQueries().size(), is( 1 ) ); + } + ); + } + + @Entity(name = "Customer") + @Table(name = "CUSTOMER_TABLE") + public static class Customer { + + @Id + private Integer id; + + private String name; + + @OneToOne(cascade = CascadeType.ALL, mappedBy = "customer") + private Address address; + + public Customer() { + } + + public Customer(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 Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + } + + + @Entity(name = "Address") + @Table(name = "ADDRESS_TABLE") + public static class Address { + + @Id + private Integer id; + + private String street; + + private String city; + + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "NOTE_FK") + private Note note; + + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "CUSTOMER_FK") + private Customer customer; + + public Address() { + } + + public Address( + Integer id, + String street, + String city, + Note note, + Customer customer) { + this.id = id; + this.street = street; + this.city = city; + this.note = note; + this.note.setAddress( this ); + this.customer = customer; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Note getNote() { + return note; + } + + public void setNote(Note note) { + this.note = note; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + } + + @Entity(name = "Note") + @Table(name = "NOTE_TABLE") + public static class Note { + + @Id + private Integer id; + + private String line; + + @OneToOne(mappedBy = "note") + private Address address; + + public Note() { + } + + public Note(Integer id, String line) { + this.id = id; + this.line = line; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + } + +}