HHH-16238 Add test for issue and handle generic non-id embaddable properties

This commit is contained in:
Marco Belladelli 2023-03-02 15:11:22 +01:00 committed by Christian Beikov
parent b198259cef
commit 16c9b1f5b7
5 changed files with 312 additions and 9 deletions

View File

@ -288,6 +288,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
} }
// If the property depends on a type variable, we have to copy it and the Value // If the property depends on a type variable, we have to copy it and the Value
final Property actualProperty = prop.copy(); final Property actualProperty = prop.copy();
actualProperty.setGeneric( true );
actualProperty.setReturnedClassName( inferredData.getTypeName() ); actualProperty.setReturnedClassName( inferredData.getTypeName() );
final Value value = actualProperty.getValue().copy(); final Value value = actualProperty.getValue().copy();
if ( value instanceof Collection ) { if ( value instanceof Collection ) {
@ -320,7 +321,6 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
setTypeName( value, inferredData.getTypeName() ); setTypeName( value, inferredData.getTypeName() );
} }
if ( value instanceof Component ) { if ( value instanceof Component ) {
actualProperty.setGenericEmbeddable( true );
final Component component = ( (Component) value ); final Component component = ( (Component) value );
final Iterator<Property> propertyIterator = component.getPropertyIterator(); final Iterator<Property> propertyIterator = component.getPropertyIterator();
while ( propertyIterator.hasNext() ) { while ( propertyIterator.hasNext() ) {

View File

@ -57,7 +57,7 @@ public class Property implements Serializable, MetaAttributable {
private java.util.Map metaAttributes; private java.util.Map metaAttributes;
private PersistentClass persistentClass; private PersistentClass persistentClass;
private boolean naturalIdentifier; private boolean naturalIdentifier;
private boolean genericEmbeddable; private boolean isGeneric;
private boolean lob; private boolean lob;
private java.util.List<CallbackDefinition> callbackDefinitions; private java.util.List<CallbackDefinition> callbackDefinitions;
private String returnedClassName; private String returnedClassName;
@ -418,12 +418,12 @@ public class Property implements Serializable, MetaAttributable {
this.naturalIdentifier = naturalIdentifier; this.naturalIdentifier = naturalIdentifier;
} }
public boolean isGenericEmbeddable() { public boolean isGeneric() {
return genericEmbeddable; return isGeneric;
} }
public void setGenericEmbeddable(boolean genericEmbeddable) { public void setGeneric(boolean generic) {
this.genericEmbeddable = genericEmbeddable; this.isGeneric = generic;
} }
public boolean isLob() { public boolean isLob() {

View File

@ -129,7 +129,7 @@ public class AttributeFactory {
false, false,
false, false,
property.isOptional(), property.isOptional(),
property.isGenericEmbeddable(), property.isGeneric(),
metadataContext metadataContext
); );
} }
@ -177,7 +177,7 @@ public class AttributeFactory {
(SimpleDomainType<Y>) determineSimpleType( attributeMetadata.getValueContext() ), (SimpleDomainType<Y>) determineSimpleType( attributeMetadata.getValueContext() ),
attributeMetadata.getMember(), attributeMetadata.getMember(),
attributeMetadata.getAttributeClassification(), attributeMetadata.getAttributeClassification(),
property.isGenericEmbeddable(), property.isGeneric(),
context context
); );
} }

View File

@ -24,7 +24,6 @@ import org.hibernate.internal.HEMLogging;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass; import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
@ -274,6 +273,7 @@ public class MetadataContext {
applyIdMetadata( safeMapping, jpaMapping ); applyIdMetadata( safeMapping, jpaMapping );
applyVersionAttribute( safeMapping, jpaMapping ); applyVersionAttribute( safeMapping, jpaMapping );
applyGenericEmbeddableProperties( safeMapping, jpaMapping );
for ( Property property : safeMapping.getDeclaredProperties() ) { for ( Property property : safeMapping.getDeclaredProperties() ) {
if ( property.getValue() == safeMapping.getIdentifierMapper() ) { if ( property.getValue() == safeMapping.getIdentifierMapper() ) {
@ -573,6 +573,43 @@ public class MetadataContext {
} }
} }
private <X> void applyGenericEmbeddableProperties(
PersistentClass persistentClass,
EntityDomainType<X> entityType) {
MappedSuperclass mappedSuperclass = getMappedSuperclass( persistentClass );
while ( mappedSuperclass != null ) {
for ( Property superclassProperty : mappedSuperclass.getDeclaredProperties() ) {
if ( superclassProperty.isGeneric() && superclassProperty.isComposite() ) {
final Property property = persistentClass.getProperty( superclassProperty.getName() );
final SingularPersistentAttribute<X, ?> attribute = (SingularPersistentAttribute<X, ?>) attributeFactory.buildAttribute(
entityType,
property
);
//noinspection unchecked rawtypes
( (AttributeContainer) entityType ).getInFlightAccess().addConcreteEmbeddableAttribute( attribute );
}
}
mappedSuperclass = getMappedSuperclass( mappedSuperclass );
}
}
private MappedSuperclass getMappedSuperclass(PersistentClass persistentClass) {
while ( persistentClass != null ) {
final MappedSuperclass mappedSuperclass = persistentClass.getSuperMappedSuperclass();
if ( mappedSuperclass != null ) {
return mappedSuperclass;
}
persistentClass = persistentClass.getSuperclass();
}
return null;
}
private MappedSuperclass getMappedSuperclass(MappedSuperclass mappedSuperclass) {
return mappedSuperclass.getSuperMappedSuperclass() != null
? mappedSuperclass.getSuperMappedSuperclass()
: getMappedSuperclass( mappedSuperclass.getSuperPersistentClass() );
}
private <X> Set<SingularPersistentAttribute<? super X, ?>> buildIdClassAttributes( private <X> Set<SingularPersistentAttribute<? super X, ?>> buildIdClassAttributes(
IdentifiableDomainType<X> ownerType, IdentifiableDomainType<X> ownerType,
List<Property> properties) { List<Property> properties) {

View File

@ -0,0 +1,266 @@
/*
* 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.annotations.generics;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Root;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel(annotatedClasses = {
MultipleEmbeddedGenericsTest.Customer.class,
MultipleEmbeddedGenericsTest.Invoice.class
})
@SessionFactory
@Jira("https://hibernate.atlassian.net/browse/HHH-TODO") // todo marco : create specific issue for this
public class MultipleEmbeddedGenericsTest {
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final Customer customer = new Customer( "Marco" );
session.persist( customer );
final Invoice invoice = new Invoice( 123 );
session.persist( invoice );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.createMutationQuery( "delete from Customer" ).executeUpdate();
session.createMutationQuery( "delete from Invoice" ).executeUpdate();
} );
}
@Test
public void testCustomer(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final Customer customer = session.createQuery(
"from Customer c where c.firstEmbedded.genericPropertyA = '1' and c.secondEmbedded.customerPropertyB = 2",
Customer.class
).getSingleResult();
assertThat( customer.getName() ).isEqualTo( "Marco" );
} );
}
@Test
public void testCustomerCriteria(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Customer> query = cb.createQuery( Customer.class );
final Root<Customer> root = query.from( Customer.class );
final Path<CustomerEmbeddableOne> firstEmbedded = root.get( "firstEmbedded" );
assertThat( firstEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableOne.class );
assertThat( firstEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "firstEmbedded" ) );
final Path<CustomerEmbeddableTwo> secondEmbedded = root.get( "secondEmbedded" );
assertThat( secondEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableTwo.class );
assertThat( secondEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "secondEmbedded" ) );
query.select( root ).where( cb.and(
cb.equal( firstEmbedded.get( "genericPropertyA" ), "1" ),
cb.equal( secondEmbedded.get( "customerPropertyB" ), 2 )
) );
final Customer customer = session.createQuery( query ).getSingleResult();
assertThat( customer.getName() ).isEqualTo( "Marco" );
} );
}
@Test
public void testInvoice(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final Invoice invoice = session.createQuery(
"from Invoice i where i.firstEmbedded.invoicePropertyA = 1 and i.secondEmbedded.genericPropertyB = '2'",
Invoice.class
).getSingleResult();
assertThat( invoice.getSerial() ).isEqualTo( 123 );
} );
}
@Test
public void testInvoiceCriteria(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Invoice> query = cb.createQuery( Invoice.class );
final Root<Invoice> root = query.from( Invoice.class );
final Path<InvoiceEmbeddableOne> firstEmbedded = root.get( "firstEmbedded" );
assertThat( firstEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableOne.class );
assertThat( firstEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "firstEmbedded" ) );
final Path<InvoiceEmbeddableTwo> secondEmbedded = root.get( "secondEmbedded" );
assertThat( secondEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableTwo.class );
assertThat( secondEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "secondEmbedded" ) );
query.select( root ).where( cb.and(
cb.equal( firstEmbedded.get( "invoicePropertyA" ), 1 ),
cb.equal( secondEmbedded.get( "genericPropertyB" ), "2" )
) );
final Invoice invoice = session.createQuery( query ).getSingleResult();
assertThat( invoice.getSerial() ).isEqualTo( 123 );
} );
}
@Embeddable
@MappedSuperclass
public abstract static class GenericEmbeddableOne {
private String genericPropertyA;
public GenericEmbeddableOne() {
}
public GenericEmbeddableOne(String genericPropertyA) {
this.genericPropertyA = genericPropertyA;
}
}
@Embeddable
@MappedSuperclass
public abstract static class GenericEmbeddableTwo {
private String genericPropertyB;
public GenericEmbeddableTwo() {
}
public GenericEmbeddableTwo(String genericPropertyB) {
this.genericPropertyB = genericPropertyB;
}
}
@MappedSuperclass
public abstract static class GenericEntity<A extends GenericEmbeddableOne, B extends GenericEmbeddableTwo> {
@Embedded
private A firstEmbedded;
@Embedded
private B secondEmbedded;
public GenericEntity() {
}
public GenericEntity(A firstEmbedded, B secondEmbedded) {
this.firstEmbedded = firstEmbedded;
this.secondEmbedded = secondEmbedded;
}
}
@Embeddable
public static class CustomerEmbeddableOne extends GenericEmbeddableOne {
private int customerPropertyA;
public CustomerEmbeddableOne() {
}
public CustomerEmbeddableOne(String genericPropertyA, int customerPropertyA) {
super( genericPropertyA );
this.customerPropertyA = customerPropertyA;
}
}
@Embeddable
public static class CustomerEmbeddableTwo extends GenericEmbeddableTwo {
private int customerPropertyB;
public CustomerEmbeddableTwo() {
}
public CustomerEmbeddableTwo(String genericPropertyB, int customerPropertyB) {
super( genericPropertyB );
this.customerPropertyB = customerPropertyB;
}
}
@Entity(name = "Customer")
public static class Customer extends GenericEntity<CustomerEmbeddableOne, CustomerEmbeddableTwo> {
@Id
@GeneratedValue
private Long id;
private String name;
public Customer() {
}
public Customer(String name) {
super( new CustomerEmbeddableOne( "1", 1 ), new CustomerEmbeddableTwo( "2", 2 ) );
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Embeddable
public static class InvoiceEmbeddableOne extends GenericEmbeddableOne {
private int invoicePropertyA;
public InvoiceEmbeddableOne() {
}
public InvoiceEmbeddableOne(String genericPropertyA, int invoicePropertyA) {
super( genericPropertyA );
this.invoicePropertyA = invoicePropertyA;
}
}
@Embeddable
public static class InvoiceEmbeddableTwo extends GenericEmbeddableTwo {
private int invoicePropertyB;
public InvoiceEmbeddableTwo() {
}
public InvoiceEmbeddableTwo(String genericPropertyB, int invoicePropertyB) {
super( genericPropertyB );
this.invoicePropertyB = invoicePropertyB;
}
}
@Entity(name = "Invoice")
public static class Invoice extends GenericEntity<InvoiceEmbeddableOne, InvoiceEmbeddableTwo> {
@Id
@GeneratedValue
private Long id;
private Integer serial;
public Invoice() {
}
public Invoice(Integer serial) {
super( new InvoiceEmbeddableOne( "1", 1 ), new InvoiceEmbeddableTwo( "2", 2 ) );
this.serial = serial;
}
public Integer getSerial() {
return serial;
}
public void setSerial(Integer serial) {
this.serial = serial;
}
}
}