HHH-18728 - Allow mixed discriminator-value mappings for ANY
This commit is contained in:
parent
f82c581990
commit
b1135b537c
|
@ -8,11 +8,13 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
import jakarta.persistence.DiscriminatorType;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.hibernate.type.AnyDiscriminatorValueStrategy.AUTO;
|
||||
|
||||
/**
|
||||
* A simplified way to specify the type of the discriminator in an {@link Any}
|
||||
|
@ -44,4 +46,12 @@ public @interface AnyDiscriminator {
|
|||
* or {@link JdbcTypeCode}.
|
||||
*/
|
||||
DiscriminatorType value() default DiscriminatorType.STRING;
|
||||
|
||||
/**
|
||||
* How the discriminator value should be handled in regard to explicit
|
||||
* {@linkplain AnyDiscriminatorValue} mappings, if any.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
AnyDiscriminatorValueStrategy valueStrategy() default AUTO;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.Locale;
|
|||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.annotations.AnyDiscriminator;
|
||||
import org.hibernate.annotations.Cascade;
|
||||
import org.hibernate.annotations.Columns;
|
||||
import org.hibernate.annotations.Formula;
|
||||
|
@ -107,6 +108,11 @@ public class AnyBinder {
|
|||
context
|
||||
);
|
||||
|
||||
final AnyDiscriminator anyDiscriminator = property.getDirectAnnotationUsage( AnyDiscriminator.class );
|
||||
if ( anyDiscriminator != null ) {
|
||||
value.setDiscriminatorValueStrategy( anyDiscriminator.valueStrategy() );
|
||||
}
|
||||
|
||||
final PropertyBinder binder = new PropertyBinder();
|
||||
binder.setName( inferredData.getPropertyName() );
|
||||
binder.setValue( value );
|
||||
|
|
|
@ -9,17 +9,20 @@ import java.util.Map;
|
|||
|
||||
import org.hibernate.annotations.AnyDiscriminator;
|
||||
import org.hibernate.models.spi.SourceModelBuildingContext;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
|
||||
@SuppressWarnings({ "ClassExplicitlyAnnotation", "unused" })
|
||||
@jakarta.annotation.Generated("org.hibernate.orm.build.annotations.ClassGeneratorProcessor")
|
||||
public class AnyDiscriminatorAnnotation implements AnyDiscriminator {
|
||||
private jakarta.persistence.DiscriminatorType value;
|
||||
private AnyDiscriminatorValueStrategy valueStrategy;
|
||||
|
||||
/**
|
||||
* Used in creating dynamic annotation instances (e.g. from XML)
|
||||
*/
|
||||
public AnyDiscriminatorAnnotation(SourceModelBuildingContext modelContext) {
|
||||
this.value = jakarta.persistence.DiscriminatorType.STRING;
|
||||
this.valueStrategy = AnyDiscriminatorValueStrategy.AUTO;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,6 +30,7 @@ public class AnyDiscriminatorAnnotation implements AnyDiscriminator {
|
|||
*/
|
||||
public AnyDiscriminatorAnnotation(AnyDiscriminator annotation, SourceModelBuildingContext modelContext) {
|
||||
this.value = annotation.value();
|
||||
this.valueStrategy = annotation.valueStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,5 +54,14 @@ public class AnyDiscriminatorAnnotation implements AnyDiscriminator {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyDiscriminatorValueStrategy valueStrategy() {
|
||||
return valueStrategy;
|
||||
}
|
||||
|
||||
public void valueStrategy(AnyDiscriminatorValueStrategy valueStrategy) {
|
||||
this.valueStrategy = valueStrategy;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
|
@ -34,6 +35,7 @@ public class Any extends SimpleValue {
|
|||
|
||||
// common
|
||||
private Map<Object,String> metaValueToEntityNameMap;
|
||||
private AnyDiscriminatorValueStrategy discriminatorValueStrategy = AnyDiscriminatorValueStrategy.AUTO;
|
||||
private boolean lazy = true;
|
||||
|
||||
private AnyType resolvedType;
|
||||
|
@ -73,6 +75,7 @@ public class Any extends SimpleValue {
|
|||
this.metaValueToEntityNameMap = original.metaValueToEntityNameMap == null
|
||||
? null
|
||||
: new HashMap<>(original.metaValueToEntityNameMap);
|
||||
this.discriminatorValueStrategy = original.discriminatorValueStrategy;
|
||||
this.lazy = original.lazy;
|
||||
}
|
||||
|
||||
|
@ -128,6 +131,28 @@ public class Any extends SimpleValue {
|
|||
this.keyMapping.setTypeName( identifierType );
|
||||
}
|
||||
|
||||
/**
|
||||
* Current strategy for interpreting {@linkplain org.hibernate.annotations.AnyDiscriminatorValue} definitions,
|
||||
* especially in terms of implicit, explicit and potentially missing values.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
public AnyDiscriminatorValueStrategy getDiscriminatorValueStrategy() {
|
||||
return discriminatorValueStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the strategy
|
||||
*
|
||||
* @see #getDiscriminatorValueStrategy
|
||||
* @since 7.0
|
||||
*/
|
||||
@Incubating
|
||||
public void setDiscriminatorValueStrategy(AnyDiscriminatorValueStrategy discriminatorValueStrategy) {
|
||||
this.discriminatorValueStrategy = discriminatorValueStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyType getType() throws MappingException {
|
||||
if ( resolvedType == null ) {
|
||||
|
@ -150,6 +175,7 @@ public class Any extends SimpleValue {
|
|||
resolvedType = MappingHelper.anyMapping(
|
||||
discriminatorType,
|
||||
identifierType,
|
||||
discriminatorValueStrategy,
|
||||
metaValueToEntityNameMap,
|
||||
isLazy(),
|
||||
getBuildingContext()
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.metamodel.RepresentationMode;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
|
@ -16,7 +18,6 @@ import java.util.function.Function;
|
|||
* @author Gavin King
|
||||
*/
|
||||
public abstract class DiscriminatorConverter<O,R> implements BasicValueConverter<O,R> {
|
||||
|
||||
private final String discriminatorName;
|
||||
private final JavaType<O> domainJavaType;
|
||||
private final JavaType<R> relationalJavaType;
|
||||
|
@ -30,6 +31,9 @@ public abstract class DiscriminatorConverter<O,R> implements BasicValueConverter
|
|||
this.relationalJavaType = relationalJavaType;
|
||||
}
|
||||
|
||||
@Incubating
|
||||
public abstract AnyDiscriminatorValueStrategy getValueStrategy();
|
||||
|
||||
public String getDiscriminatorName() {
|
||||
return discriminatorName;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,15 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddableDiscriminatorValueDetailsImpl;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -11,14 +20,6 @@ import java.util.Map;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddableDiscriminatorValueDetailsImpl;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
/**
|
||||
* Handles conversion of discriminator values for embeddable subtype classes
|
||||
* to their domain typed form.
|
||||
|
@ -65,6 +66,12 @@ public class EmbeddableDiscriminatorConverter<O, R> extends DiscriminatorConvert
|
|||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyDiscriminatorValueStrategy getValueStrategy() {
|
||||
// discriminators for embeddables are always explicit
|
||||
return AnyDiscriminatorValueStrategy.EXPLICIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public O toDomainValue(R relationalForm) {
|
||||
assert relationalForm == null || getRelationalJavaType().isInstance( relationalForm );
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
|
|||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
import org.hibernate.type.descriptor.java.CharacterJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.StringJavaType;
|
||||
|
@ -60,6 +61,19 @@ public class ExplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<
|
|||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyDiscriminatorValueStrategy getValueStrategy() {
|
||||
return AnyDiscriminatorValueStrategy.EXPLICIT;
|
||||
}
|
||||
|
||||
public Map<Object, DiscriminatorValueDetails> getDetailsByValue() {
|
||||
return detailsByValue;
|
||||
}
|
||||
|
||||
public Map<String, DiscriminatorValueDetails> getDetailsByEntityName() {
|
||||
return detailsByEntityName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object relationalForm) {
|
||||
if ( relationalForm == null ) {
|
||||
|
@ -80,13 +94,13 @@ public class ExplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<
|
|||
if ( relationalForm.getClass().isEnum() ) {
|
||||
final Object enumValue;
|
||||
if ( getRelationalJavaType() instanceof StringJavaType ) {
|
||||
enumValue = ( (Enum) relationalForm ).name();
|
||||
enumValue = ( (Enum<?>) relationalForm ).name();
|
||||
}
|
||||
else if ( getRelationalJavaType() instanceof CharacterJavaType ) {
|
||||
enumValue = ( (Enum) relationalForm ).name().charAt( 0 );
|
||||
enumValue = ( (Enum<?>) relationalForm ).name().charAt( 0 );
|
||||
}
|
||||
else {
|
||||
enumValue = ( (Enum) relationalForm ).ordinal();
|
||||
enumValue = ( (Enum<?>) relationalForm ).ordinal();
|
||||
}
|
||||
final DiscriminatorValueDetails enumMatch = detailsByValue.get( enumValue );
|
||||
if ( enumMatch != null ) {
|
||||
|
@ -108,7 +122,7 @@ public class ExplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<
|
|||
if ( valueDetails != null) {
|
||||
return valueDetails;
|
||||
}
|
||||
throw new HibernateException( "Unknown entity name (" + discriminatorRole + ") : " + entityName );
|
||||
throw new HibernateException( "Entity not explicitly mapped for ANY discriminator (" + discriminatorRole + ") : " + entityName );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
|
|||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import java.util.Map;
|
||||
|
@ -51,6 +52,19 @@ public class ImplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<
|
|||
this.detailsByEntityName = CollectionHelper.concurrentMap( 8 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyDiscriminatorValueStrategy getValueStrategy() {
|
||||
return AnyDiscriminatorValueStrategy.IMPLICIT;
|
||||
}
|
||||
|
||||
public Map<Object, DiscriminatorValueDetails> getDetailsByValue() {
|
||||
return detailsByValue;
|
||||
}
|
||||
|
||||
public Map<String, DiscriminatorValueDetails> getDetailsByEntityName() {
|
||||
return detailsByEntityName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object value) {
|
||||
if ( value instanceof String incoming ) {
|
||||
|
@ -58,10 +72,8 @@ public class ImplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<
|
|||
if ( existingDetails != null ) {
|
||||
return existingDetails;
|
||||
}
|
||||
final String entityName = mappingMetamodel.getImportedName( incoming );
|
||||
final EntityPersister persister = mappingMetamodel.findEntityDescriptor( entityName );
|
||||
final EntityPersister persister = mappingMetamodel.findEntityDescriptor( incoming );
|
||||
if ( persister != null ) {
|
||||
assert persister.getImportedName().equals( incoming );
|
||||
return register( incoming, persister );
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +88,7 @@ public class ImplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<
|
|||
private DiscriminatorValueDetails register(Object value, EntityPersister entityDescriptor) {
|
||||
final DiscriminatorValueDetails details = new DiscriminatorValueDetailsImpl( value, entityDescriptor );
|
||||
detailsByValue.put( value, details );
|
||||
detailsByEntityName.put( entityDescriptor.getImportedName(), details );
|
||||
detailsByEntityName.put( entityDescriptor.getEntityName(), details );
|
||||
return details;
|
||||
}
|
||||
|
||||
|
@ -88,7 +100,7 @@ public class ImplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<
|
|||
}
|
||||
final EntityPersister persister = mappingMetamodel.findEntityDescriptor( entityName );
|
||||
if ( persister!= null ) {
|
||||
return register( persister.getImportedName(), persister );
|
||||
return register( persister.getEntityName(), persister );
|
||||
}
|
||||
throw new HibernateException( String.format(
|
||||
ROOT,
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
|
|||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
import org.hibernate.type.descriptor.java.CharacterJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.StringJavaType;
|
||||
|
@ -59,6 +60,19 @@ public class MixedDiscriminatorConverter<O,R> extends DiscriminatorConverter<O,R
|
|||
return details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyDiscriminatorValueStrategy getValueStrategy() {
|
||||
return AnyDiscriminatorValueStrategy.MIXED;
|
||||
}
|
||||
|
||||
public Map<Object, DiscriminatorValueDetails> getDetailsByValue() {
|
||||
return detailsByValue;
|
||||
}
|
||||
|
||||
public Map<String, DiscriminatorValueDetails> getDetailsByEntityName() {
|
||||
return detailsByEntityName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object relationalForm) {
|
||||
if ( relationalForm == null ) {
|
||||
|
@ -79,13 +93,13 @@ public class MixedDiscriminatorConverter<O,R> extends DiscriminatorConverter<O,R
|
|||
if ( relationalForm.getClass().isEnum() ) {
|
||||
final Object enumValue;
|
||||
if ( getRelationalJavaType() instanceof StringJavaType ) {
|
||||
enumValue = ( (Enum) relationalForm ).name();
|
||||
enumValue = ( (Enum<?>) relationalForm ).name();
|
||||
}
|
||||
else if ( getRelationalJavaType() instanceof CharacterJavaType ) {
|
||||
enumValue = ( (Enum) relationalForm ).name().charAt( 0 );
|
||||
enumValue = ( (Enum<?>) relationalForm ).name().charAt( 0 );
|
||||
}
|
||||
else {
|
||||
enumValue = ( (Enum) relationalForm ).ordinal();
|
||||
enumValue = ( (Enum<?>) relationalForm ).ordinal();
|
||||
}
|
||||
final DiscriminatorValueDetails enumMatch = detailsByValue.get( enumValue );
|
||||
if ( enumMatch != null ) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.annotations.AnyDiscriminatorValue;
|
||||
|
||||
/**
|
||||
|
@ -12,8 +13,10 @@ import org.hibernate.annotations.AnyDiscriminatorValue;
|
|||
*
|
||||
* @see AnyDiscriminatorValue
|
||||
*
|
||||
* @since 7.0
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Incubating
|
||||
public enum AnyDiscriminatorValueStrategy {
|
||||
/**
|
||||
* Pick between {@link #IMPLICIT} and {@link #EXPLICIT} based on
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.any.mixed;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
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.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Tests for {@link org.hibernate.type.AnyDiscriminatorValueStrategy}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("JUnitMalformedDeclaration")
|
||||
public class AnyDiscriminatorValueHandlingTests {
|
||||
@Test
|
||||
@DomainModel(annotatedClasses = {Payment.class, CashPayment.class, CardPayment.class, CheckPayment.class, Order.class})
|
||||
@SessionFactory
|
||||
void verifyImplicitMappingHandling(SessionFactoryScope sessions) {
|
||||
sessions.inTransaction( (session) -> {
|
||||
final Order order = new Order( 1, "1" );
|
||||
final CashPayment cashPayment = new CashPayment( 1, 50.00 );
|
||||
final CardPayment cardPayment = new CardPayment( 1, 150.00, "123-456-789" );
|
||||
final CheckPayment checkPayment = new CheckPayment( 1, 250.00, 1001, "123", "987" );
|
||||
session.persist( order );
|
||||
session.persist( cashPayment );
|
||||
session.persist( cardPayment );
|
||||
session.persist( checkPayment );
|
||||
session.flush();
|
||||
|
||||
order.implicitPayment = cardPayment;
|
||||
session.flush();
|
||||
|
||||
order.implicitPayment = checkPayment;
|
||||
session.flush();
|
||||
|
||||
order.implicitPayment = cashPayment;
|
||||
session.flush();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@DomainModel(annotatedClasses = {Payment.class, CashPayment.class, CardPayment.class, CheckPayment.class, Order.class})
|
||||
@SessionFactory
|
||||
void verifyExplicitMappingHandling(SessionFactoryScope sessions) {
|
||||
sessions.inTransaction( (session) -> {
|
||||
final Order order = new Order( 1, "1" );
|
||||
final CashPayment cashPayment = new CashPayment( 1, 50.00 );
|
||||
final CardPayment cardPayment = new CardPayment( 1, 150.00, "123-456-789" );
|
||||
final CheckPayment checkPayment = new CheckPayment( 1, 250.00, 1001, "123", "987" );
|
||||
session.persist( order );
|
||||
session.persist( cashPayment );
|
||||
session.persist( cardPayment );
|
||||
session.persist( checkPayment );
|
||||
session.flush();
|
||||
|
||||
order.explicitPayment = cardPayment;
|
||||
session.flush();
|
||||
|
||||
order.explicitPayment = checkPayment;
|
||||
session.flush();
|
||||
|
||||
// NOTE : cash is not explicitly mapped
|
||||
try {
|
||||
order.explicitPayment = cashPayment;
|
||||
session.flush();
|
||||
fail( "Expecting an error" );
|
||||
}
|
||||
catch (HibernateException expected) {
|
||||
assertThat( expected ).hasMessageContaining( "Entity not explicitly mapped for ANY discriminator" );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@DomainModel(annotatedClasses = {Payment.class, CashPayment.class, CardPayment.class, CheckPayment.class, Order.class})
|
||||
@SessionFactory
|
||||
void verifyMixedMappingHandling(SessionFactoryScope sessions) {
|
||||
sessions.inTransaction( (session) -> {
|
||||
final Order order = new Order( 1, "1" );
|
||||
final CashPayment cashPayment = new CashPayment( 1, 50.00 );
|
||||
final CardPayment cardPayment = new CardPayment( 1, 150.00, "123-456-789" );
|
||||
final CheckPayment checkPayment = new CheckPayment( 1, 250.00, 1001, "123", "987" );
|
||||
session.persist( order );
|
||||
session.persist( cashPayment );
|
||||
session.persist( cardPayment );
|
||||
session.persist( checkPayment );
|
||||
session.flush();
|
||||
|
||||
order.mixedPayment = cardPayment;
|
||||
session.flush();
|
||||
|
||||
order.mixedPayment = checkPayment;
|
||||
session.flush();
|
||||
|
||||
order.mixedPayment = cashPayment;
|
||||
session.flush();
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@DomainModel(annotatedClasses = {Payment.class, CashPayment.class, CardPayment.class, CheckPayment.class, Order.class})
|
||||
@SessionFactory
|
||||
void dropTestData(SessionFactoryScope sessions) {
|
||||
sessions.inTransaction( (session) -> {
|
||||
session.createMutationQuery( "delete Order" ).executeUpdate();
|
||||
session.createMutationQuery( "delete CashPayment" ).executeUpdate();
|
||||
session.createMutationQuery( "delete CardPayment" ).executeUpdate();
|
||||
session.createMutationQuery( "delete CheckPayment" ).executeUpdate();
|
||||
} );
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.any.mixed;
|
||||
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
|
||||
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.ExplicitDiscriminatorConverter;
|
||||
import org.hibernate.metamodel.mapping.internal.ImplicitDiscriminatorConverter;
|
||||
import org.hibernate.metamodel.mapping.internal.MixedDiscriminatorConverter;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Tests for {@link org.hibernate.type.AnyDiscriminatorValueStrategy}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("JUnitMalformedDeclaration")
|
||||
public class AnyDiscriminatorValueStrategyTests {
|
||||
@Test
|
||||
@DomainModel(annotatedClasses = {Payment.class, CashPayment.class, CardPayment.class, CheckPayment.class, Order.class})
|
||||
@SessionFactory
|
||||
void verifyImplicitMappingModel(SessionFactoryScope sessions) {
|
||||
sessions.withSessionFactory( (factory) -> {
|
||||
final EntityPersister entityDescriptor = factory.getMappingMetamodel().getEntityDescriptor( Order.class );
|
||||
final DiscriminatedAssociationAttributeMapping implicitMapping = (DiscriminatedAssociationAttributeMapping) entityDescriptor.findAttributeMapping( "implicitPayment" );
|
||||
final DiscriminatorMapping discriminatorMapping = implicitMapping.getDiscriminatorMapping();
|
||||
final DiscriminatorConverter<?, ?> discriminatorConverter = discriminatorMapping.getValueConverter();
|
||||
assertThat( discriminatorConverter.getValueStrategy() ).isEqualTo( AnyDiscriminatorValueStrategy.IMPLICIT );
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// check discriminator -> entity
|
||||
|
||||
final DiscriminatorValueDetails cash = discriminatorConverter.getDetailsForDiscriminatorValue( CashPayment.class.getName() );
|
||||
assertThat( cash.getIndicatedEntity().getEntityName() ).isEqualTo( CashPayment.class.getName() );
|
||||
assertThat( cash.getIndicatedEntityName() ).isEqualTo( CashPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails card = discriminatorConverter.getDetailsForDiscriminatorValue( CardPayment.class.getName() );
|
||||
assertThat( card.getIndicatedEntity().getEntityName() ).isEqualTo( CardPayment.class.getName() );
|
||||
assertThat( card.getIndicatedEntityName() ).isEqualTo( CardPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails check = discriminatorConverter.getDetailsForDiscriminatorValue( CheckPayment.class.getName() );
|
||||
assertThat( check.getIndicatedEntity().getEntityName() ).isEqualTo( CheckPayment.class.getName() );
|
||||
assertThat( check.getIndicatedEntityName() ).isEqualTo( CheckPayment.class.getName() );
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// check entity -> discriminator
|
||||
|
||||
final DiscriminatorValueDetails cashDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CashPayment.class.getName() );
|
||||
assertThat( cashDiscriminatorValue.getValue() ).isEqualTo( CashPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails cardDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CardPayment.class.getName() );
|
||||
assertThat( cardDiscriminatorValue.getValue() ).isEqualTo( CardPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails checkDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CheckPayment.class.getName() );
|
||||
assertThat( checkDiscriminatorValue.getValue() ).isEqualTo( CheckPayment.class.getName() );
|
||||
|
||||
final Map<String,?> detailsByEntityName = ((ImplicitDiscriminatorConverter<?,?>) discriminatorConverter).getDetailsByEntityName();
|
||||
assertThat( detailsByEntityName.keySet() ).containsOnly(
|
||||
CashPayment.class.getName(),
|
||||
CardPayment.class.getName(),
|
||||
CheckPayment.class.getName()
|
||||
);
|
||||
|
||||
final Map<Object,?> detailsByValue = ((ImplicitDiscriminatorConverter<?,?>) discriminatorConverter).getDetailsByValue();
|
||||
assertThat( detailsByValue.keySet() ).containsOnly(
|
||||
CashPayment.class.getName(),
|
||||
CardPayment.class.getName(),
|
||||
CheckPayment.class.getName()
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@DomainModel(annotatedClasses = {Payment.class, CashPayment.class, CardPayment.class, CheckPayment.class, Order.class})
|
||||
@SessionFactory
|
||||
void verifyExplicitMappingModel(SessionFactoryScope sessions) {
|
||||
sessions.withSessionFactory( (factory) -> {
|
||||
final EntityPersister entityDescriptor = factory.getMappingMetamodel().getEntityDescriptor( Order.class );
|
||||
final DiscriminatedAssociationAttributeMapping explicitMapping = (DiscriminatedAssociationAttributeMapping) entityDescriptor.findAttributeMapping( "explicitPayment" );
|
||||
final DiscriminatorMapping discriminatorMapping = explicitMapping.getDiscriminatorMapping();
|
||||
final DiscriminatorConverter<?, ?> discriminatorConverter = discriminatorMapping.getValueConverter();
|
||||
assertThat( discriminatorConverter.getValueStrategy() ).isEqualTo( AnyDiscriminatorValueStrategy.EXPLICIT );
|
||||
|
||||
// NOTE : cash is NOT mapped
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// check discriminator -> entity
|
||||
|
||||
try {
|
||||
discriminatorConverter.getDetailsForDiscriminatorValue( "CASH" );
|
||||
fail( "Expecting an error" );
|
||||
}
|
||||
catch (HibernateException expected) {
|
||||
assertThat( expected ).hasMessageContaining( "Unknown discriminator value" );
|
||||
}
|
||||
|
||||
try {
|
||||
discriminatorConverter.getDetailsForDiscriminatorValue( CashPayment.class.getName() );
|
||||
fail( "Expecting an error" );
|
||||
}
|
||||
catch (HibernateException expected) {
|
||||
assertThat( expected ).hasMessageContaining( "Unknown discriminator value" );
|
||||
}
|
||||
|
||||
final DiscriminatorValueDetails card = discriminatorConverter.getDetailsForDiscriminatorValue( "CARD" );
|
||||
assertThat( card.getIndicatedEntity().getEntityName() ).isEqualTo( CardPayment.class.getName() );
|
||||
assertThat( card.getIndicatedEntityName() ).isEqualTo( CardPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails check = discriminatorConverter.getDetailsForDiscriminatorValue( "CHECK" );
|
||||
assertThat( check.getIndicatedEntity().getEntityName() ).isEqualTo( CheckPayment.class.getName() );
|
||||
assertThat( check.getIndicatedEntityName() ).isEqualTo( CheckPayment.class.getName() );
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// check entity -> discriminator
|
||||
|
||||
try {
|
||||
discriminatorConverter.getDetailsForDiscriminatorValue( CashPayment.class.getName() );
|
||||
fail( "Expecting an error" );
|
||||
}
|
||||
catch (HibernateException expected) {
|
||||
assertThat( expected ).hasMessageContaining( "Unknown discriminator value" );
|
||||
}
|
||||
|
||||
final DiscriminatorValueDetails cardDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CardPayment.class.getName() );
|
||||
assertThat( cardDiscriminatorValue.getValue() ).isEqualTo( "CARD" );
|
||||
|
||||
final DiscriminatorValueDetails checkDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CheckPayment.class.getName() );
|
||||
assertThat( checkDiscriminatorValue.getValue() ).isEqualTo( "CHECK" );
|
||||
|
||||
|
||||
final Map<String,?> detailsByEntityName = ((ExplicitDiscriminatorConverter<?,?>) discriminatorConverter).getDetailsByEntityName();
|
||||
assertThat( detailsByEntityName.keySet() ).containsOnly(
|
||||
CardPayment.class.getName(),
|
||||
CheckPayment.class.getName()
|
||||
);
|
||||
|
||||
final Map<Object,?> detailsByValue = ((ExplicitDiscriminatorConverter<?,?>) discriminatorConverter).getDetailsByValue();
|
||||
assertThat( detailsByValue.keySet() ).containsOnly(
|
||||
"CARD",
|
||||
"CHECK"
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@DomainModel(annotatedClasses = {Payment.class, CashPayment.class, CardPayment.class, CheckPayment.class, Order.class})
|
||||
@SessionFactory
|
||||
void verifyMixedMappingModel(SessionFactoryScope sessions) {
|
||||
sessions.withSessionFactory( (factory) -> {
|
||||
final EntityPersister entityDescriptor = factory.getMappingMetamodel().getEntityDescriptor( Order.class );
|
||||
final DiscriminatedAssociationAttributeMapping mixedMapping = (DiscriminatedAssociationAttributeMapping) entityDescriptor.findAttributeMapping( "mixedPayment" );
|
||||
final DiscriminatorMapping discriminatorMapping = mixedMapping.getDiscriminatorMapping();
|
||||
final DiscriminatorConverter<?, ?> discriminatorConverter = discriminatorMapping.getValueConverter();
|
||||
// historically this operated as if EXPLICIT
|
||||
assertThat( discriminatorConverter.getValueStrategy() ).isEqualTo( AnyDiscriminatorValueStrategy.MIXED );
|
||||
|
||||
// NOTE : cash is NOT mapped
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// check discriminator -> entity
|
||||
|
||||
final DiscriminatorValueDetails cash = discriminatorConverter.getDetailsForDiscriminatorValue( CashPayment.class.getName() );
|
||||
assertThat( cash.getIndicatedEntity().getEntityName() ).isEqualTo( CashPayment.class.getName() );
|
||||
assertThat( cash.getIndicatedEntityName() ).isEqualTo( CashPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails card = discriminatorConverter.getDetailsForDiscriminatorValue( "CARD" );
|
||||
assertThat( card.getIndicatedEntity().getEntityName() ).isEqualTo( CardPayment.class.getName() );
|
||||
assertThat( card.getIndicatedEntityName() ).isEqualTo( CardPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails check = discriminatorConverter.getDetailsForDiscriminatorValue( "CHECK" );
|
||||
assertThat( check.getIndicatedEntity().getEntityName() ).isEqualTo( CheckPayment.class.getName() );
|
||||
assertThat( check.getIndicatedEntityName() ).isEqualTo( CheckPayment.class.getName() );
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// check entity -> discriminator
|
||||
|
||||
final DiscriminatorValueDetails cashDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CashPayment.class.getName() );
|
||||
assertThat( cashDiscriminatorValue.getValue() ).isEqualTo( CashPayment.class.getName() );
|
||||
|
||||
final DiscriminatorValueDetails cardDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CardPayment.class.getName() );
|
||||
assertThat( cardDiscriminatorValue.getValue() ).isEqualTo( "CARD" );
|
||||
|
||||
final DiscriminatorValueDetails checkDiscriminatorValue = discriminatorConverter.getDetailsForEntityName( CheckPayment.class.getName() );
|
||||
assertThat( checkDiscriminatorValue.getValue() ).isEqualTo( "CHECK" );
|
||||
|
||||
final Map<String,?> detailsByEntityName = ((MixedDiscriminatorConverter<?,?>) discriminatorConverter).getDetailsByEntityName();
|
||||
assertThat( detailsByEntityName.keySet() ).containsOnly(
|
||||
CashPayment.class.getName(),
|
||||
CardPayment.class.getName(),
|
||||
CheckPayment.class.getName()
|
||||
);
|
||||
|
||||
final Map<Object,?> detailsByValue = ((MixedDiscriminatorConverter<?,?>) discriminatorConverter).getDetailsByValue();
|
||||
assertThat( detailsByValue.keySet() ).containsOnly(
|
||||
CashPayment.class.getName(),
|
||||
"CARD",
|
||||
"CHECK"
|
||||
);
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.any.mixed;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Entity(name = "CardPayment")
|
||||
public class CardPayment implements Payment {
|
||||
@Id
|
||||
private Integer id;
|
||||
private Double amount;
|
||||
private String authorizationCode;
|
||||
|
||||
public CardPayment() {
|
||||
}
|
||||
|
||||
public CardPayment(Integer id, Double amount, String authorizationCode) {
|
||||
this.id = id;
|
||||
this.amount = amount;
|
||||
this.authorizationCode = authorizationCode;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(Double amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getAuthorizationCode() {
|
||||
return authorizationCode;
|
||||
}
|
||||
|
||||
public void setAuthorizationCode(String authorizationCode) {
|
||||
this.authorizationCode = authorizationCode;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.any.mixed;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Entity(name = "CashPayment")
|
||||
public class CashPayment implements Payment {
|
||||
@Id
|
||||
private Integer id;
|
||||
private Double amount;
|
||||
|
||||
public CashPayment() {
|
||||
}
|
||||
|
||||
public CashPayment(Integer id, Double amount) {
|
||||
this.id = id;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(Double amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.any.mixed;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Entity
|
||||
public class CheckPayment implements Payment {
|
||||
@Id
|
||||
public Integer id;
|
||||
public Double amount;
|
||||
public int checkNumber;
|
||||
public String routingNumber;
|
||||
public String accountNumber;
|
||||
|
||||
public CheckPayment() {
|
||||
}
|
||||
|
||||
public CheckPayment(Integer id, Double amount, int checkNumber, String routingNumber, String accountNumber) {
|
||||
this.id = id;
|
||||
this.amount = amount;
|
||||
this.checkNumber = checkNumber;
|
||||
this.routingNumber = routingNumber;
|
||||
this.accountNumber = accountNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAmount() {
|
||||
return amount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.any.mixed;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.Table;
|
||||
import org.hibernate.annotations.Any;
|
||||
import org.hibernate.annotations.AnyDiscriminator;
|
||||
import org.hibernate.annotations.AnyDiscriminatorValue;
|
||||
import org.hibernate.annotations.AnyKeyJavaClass;
|
||||
import org.hibernate.type.AnyDiscriminatorValueStrategy;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "orders")
|
||||
public class Order {
|
||||
@Id
|
||||
public Integer id;
|
||||
@Basic
|
||||
public String name;
|
||||
|
||||
@Any
|
||||
@AnyKeyJavaClass( Integer.class )
|
||||
@JoinColumn(name = "explicit_fk")
|
||||
@AnyDiscriminatorValue( discriminator = "CARD", entity = CardPayment.class )
|
||||
@AnyDiscriminatorValue( discriminator = "CHECK", entity = CheckPayment.class )
|
||||
public Payment explicitPayment;
|
||||
|
||||
@Any
|
||||
@AnyKeyJavaClass( Integer.class )
|
||||
@JoinColumn(name = "implicit_fk")
|
||||
public Payment implicitPayment;
|
||||
|
||||
@Any
|
||||
@AnyKeyJavaClass( Integer.class )
|
||||
@JoinColumn(name = "mixed_fk")
|
||||
@AnyDiscriminator(valueStrategy = AnyDiscriminatorValueStrategy.MIXED)
|
||||
@AnyDiscriminatorValue( discriminator = "CARD", entity = CardPayment.class )
|
||||
@AnyDiscriminatorValue( discriminator = "CHECK", entity = CheckPayment.class )
|
||||
public Payment mixedPayment;
|
||||
|
||||
protected Order() {
|
||||
// for Hibernate use
|
||||
}
|
||||
|
||||
public Order(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.any.mixed;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface Payment {
|
||||
Double getAmount();
|
||||
}
|
Loading…
Reference in New Issue