HHH-16238 Add test for issue and handle generic non-id embaddable properties
This commit is contained in:
parent
2421496c18
commit
ccdc1a9cac
|
@ -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() ) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue