HHH-16238 Add test for issue and handle generic non-id embaddable properties
This commit is contained in:
parent
b198259cef
commit
16c9b1f5b7
|
@ -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
|
||||
final Property actualProperty = prop.copy();
|
||||
actualProperty.setGeneric( true );
|
||||
actualProperty.setReturnedClassName( inferredData.getTypeName() );
|
||||
final Value value = actualProperty.getValue().copy();
|
||||
if ( value instanceof Collection ) {
|
||||
|
@ -320,7 +321,6 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
|
|||
setTypeName( value, inferredData.getTypeName() );
|
||||
}
|
||||
if ( value instanceof Component ) {
|
||||
actualProperty.setGenericEmbeddable( true );
|
||||
final Component component = ( (Component) value );
|
||||
final Iterator<Property> propertyIterator = component.getPropertyIterator();
|
||||
while ( propertyIterator.hasNext() ) {
|
||||
|
|
|
@ -57,7 +57,7 @@ public class Property implements Serializable, MetaAttributable {
|
|||
private java.util.Map metaAttributes;
|
||||
private PersistentClass persistentClass;
|
||||
private boolean naturalIdentifier;
|
||||
private boolean genericEmbeddable;
|
||||
private boolean isGeneric;
|
||||
private boolean lob;
|
||||
private java.util.List<CallbackDefinition> callbackDefinitions;
|
||||
private String returnedClassName;
|
||||
|
@ -418,12 +418,12 @@ public class Property implements Serializable, MetaAttributable {
|
|||
this.naturalIdentifier = naturalIdentifier;
|
||||
}
|
||||
|
||||
public boolean isGenericEmbeddable() {
|
||||
return genericEmbeddable;
|
||||
public boolean isGeneric() {
|
||||
return isGeneric;
|
||||
}
|
||||
|
||||
public void setGenericEmbeddable(boolean genericEmbeddable) {
|
||||
this.genericEmbeddable = genericEmbeddable;
|
||||
public void setGeneric(boolean generic) {
|
||||
this.isGeneric = generic;
|
||||
}
|
||||
|
||||
public boolean isLob() {
|
||||
|
|
|
@ -129,7 +129,7 @@ public class AttributeFactory {
|
|||
false,
|
||||
false,
|
||||
property.isOptional(),
|
||||
property.isGenericEmbeddable(),
|
||||
property.isGeneric(),
|
||||
metadataContext
|
||||
);
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ public class AttributeFactory {
|
|||
(SimpleDomainType<Y>) determineSimpleType( attributeMetadata.getValueContext() ),
|
||||
attributeMetadata.getMember(),
|
||||
attributeMetadata.getAttributeClassification(),
|
||||
property.isGenericEmbeddable(),
|
||||
property.isGeneric(),
|
||||
context
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.hibernate.internal.HEMLogging;
|
|||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.KeyValue;
|
||||
import org.hibernate.mapping.MappedSuperclass;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
|
@ -274,6 +273,7 @@ public class MetadataContext {
|
|||
|
||||
applyIdMetadata( safeMapping, jpaMapping );
|
||||
applyVersionAttribute( safeMapping, jpaMapping );
|
||||
applyGenericEmbeddableProperties( safeMapping, jpaMapping );
|
||||
|
||||
for ( Property property : safeMapping.getDeclaredProperties() ) {
|
||||
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(
|
||||
IdentifiableDomainType<X> ownerType,
|
||||
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