From 1580613f8ab7d80423c133d5859a9b4fa059ed23 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 11 Dec 2019 11:31:43 +0000 Subject: [PATCH] HHH-13777 UnsupportedOperationException is thrown for MappedSuperclass if the id is declared on subclasses --- .../domain/AbstractIdentifiableType.java | 10 +- .../internal/MappedSuperclassTypeImpl.java | 5 + ...appedSuperclassWithIdOnSubclassesTest.java | 201 ++++++++++++++++++ 3 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/inheritance/MappedSuperclassWithIdOnSubclassesTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java index 89c8f09fb6..3daec10a20 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java @@ -20,7 +20,6 @@ import org.hibernate.metamodel.model.domain.internal.AttributeContainer; import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource; import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; import org.hibernate.query.sqm.SqmPathSource; -import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.jboss.logging.Logger; @@ -389,7 +388,14 @@ public abstract class AbstractIdentifiableType throw new NotYetImplementedFor6Exception( getClass() ); } else { - throw new UnsupportedOperationException( "Could not build SqmPathSource for entity identifier : " + getTypeName() ); + if ( isIdMappingRequired() ) { + throw new UnsupportedOperationException( "Could not build SqmPathSource for entity identifier : " + getTypeName() ); + } + return null; } } + + protected boolean isIdMappingRequired() { + return true; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java index 7e30bbca77..e9e683d1eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java @@ -54,4 +54,9 @@ public class MappedSuperclassTypeImpl extends AbstractIdentifiableType imp public SubGraphImplementor makeSubGraph(Class subType) { throw new NotYetImplementedException( ); } + + @Override + protected boolean isIdMappingRequired() { + return false; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/inheritance/MappedSuperclassWithIdOnSubclassesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/inheritance/MappedSuperclassWithIdOnSubclassesTest.java new file mode 100644 index 0000000000..a5ed960474 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/inheritance/MappedSuperclassWithIdOnSubclassesTest.java @@ -0,0 +1,201 @@ +/* + * 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.metamodel.mapping.inheritance; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +import org.hibernate.persister.entity.EntityPersister; + +import org.hibernate.testing.orm.junit.DomainModel; +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.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Andrea Boriero + */ +@DomainModel( + annotatedClasses = { + MappedSuperclassWithIdOnSubclassesTest.DomesticCustomer.class, + MappedSuperclassWithIdOnSubclassesTest.ForeignCustomer.class + } +) +@ServiceRegistry +@SessionFactory +public class MappedSuperclassWithIdOnSubclassesTest { + @Test + public void basicTest(SessionFactoryScope scope) { + final EntityPersister customerDescriptor = scope.getSessionFactory() + .getMetamodel() + .findEntityDescriptor( Customer.class ); + final EntityPersister domesticCustomerDescriptor = scope.getSessionFactory() + .getMetamodel() + .findEntityDescriptor( DomesticCustomer.class ); + final EntityPersister foreignCustomerDescriptor = scope.getSessionFactory() + .getMetamodel() + .findEntityDescriptor( ForeignCustomer.class ); + + assert customerDescriptor == null; + + assert domesticCustomerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor ); + assert !domesticCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor ); + + assert !foreignCustomerDescriptor.isTypeOrSuperType( domesticCustomerDescriptor ); + assert foreignCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor ); + } + + @Test + public void subclassQueryExecutionTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + { + final DomesticCustomer result = session.createQuery( + "select c from DomesticCustomer c", + DomesticCustomer.class + ).uniqueResult(); + + assertThat( result, notNullValue() ); + assertThat( result.getId(), is( 1 ) ); + assertThat( result.getName(), is( "domestic" ) ); + assertThat( result.getTaxId(), is( "123" ) ); + } + + { + final ForeignCustomer result = session.createQuery( + "select c from ForeignCustomer c", + ForeignCustomer.class + ).uniqueResult(); + + assertThat( result, notNullValue() ); + assertThat( result.getId(), is( 2 ) ); + assertThat( result.getName(), is( "foreign" ) ); + assertThat( result.getVat(), is( "987" ) ); + } + } + ); + } + + @BeforeEach + public void createTestData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist( new DomesticCustomer( 1, "domestic", "123" ) ); + session.persist( new ForeignCustomer( 2, "foreign", "987" ) ); + } + ); + } + + @AfterEach + public void cleanupTestData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "from DomesticCustomer", DomesticCustomer.class ) + .list() + .forEach( cust -> session.delete( cust ) ); + session.createQuery( "from ForeignCustomer", ForeignCustomer.class ) + .list() + .forEach( cust -> session.delete( cust ) ); + } + ); + } + + @MappedSuperclass + public static abstract class Customer { + + private String name; + + public Customer() { + } + + public Customer(String name) { + + this.name = name; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Entity(name = "DomesticCustomer") + public static class DomesticCustomer extends Customer { + private Integer id; + private String taxId; + + public DomesticCustomer() { + } + + public DomesticCustomer(Integer id, String name, String taxId) { + super( name ); + this.id = id; + this.taxId = taxId; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTaxId() { + return taxId; + } + + public void setTaxId(String taxId) { + this.taxId = taxId; + } + } + + @Entity(name = "ForeignCustomer") + public static class ForeignCustomer extends Customer { + private Integer id; + private String vat; + + public ForeignCustomer() { + } + + public ForeignCustomer(Integer id, String name, String vat) { + super( name ); + this.id = id; + this.vat = vat; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getVat() { + return vat; + } + + public void setVat(String vat) { + this.vat = vat; + } + } +}