HHH-15884 - Clean-up EntityDiscriminatorMapping for API

HHH-15891 - Clean-up EntityVersionMapping for API
HHH-15892 - Clean-up EntityIdentifierMapping for API
This commit is contained in:
Steve Ebersole 2022-12-15 18:55:28 -06:00
parent e82c8fe911
commit ed4af59882
16 changed files with 223 additions and 80 deletions

View File

@ -0,0 +1,20 @@
/*
* 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.metamodel.mapping;
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
/**
* An "aggregated" composite identifier, which is another way to say that the
* identifier is represented as an {@linkplain jakarta.persistence.EmbeddedId embeddable}.
*
* @see jakarta.persistence.EmbeddedId
*
* @author Steve Ebersole
*/
public interface AggregatedIdentifierMapping extends SingleAttributeIdentifierMapping {
}

View File

@ -9,7 +9,7 @@ package org.hibernate.metamodel.mapping;
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
/**
* Mapping for a simple identifier
* Mapping for a simple, single-column identifier
*
* @author Steve Ebersole
*/

View File

@ -11,12 +11,17 @@ import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
/**
* Describes the mapping of an entity's identifier.
*
* @see jakarta.persistence.Id
* @see jakarta.persistence.EmbeddedId
* @see Id
* @see EmbeddedId
* @see Nature
*/
public interface EntityIdentifierMapping extends ValuedModelPart {
@ -40,20 +45,23 @@ public interface EntityIdentifierMapping extends ValuedModelPart {
/**
* The strategy for distinguishing between detached and transient
* state based on the identifier mapping
*
* @see EntityVersionMapping#getUnsavedStrategy()
*/
IdentifierValue getUnsavedStrategy();
/**
* Instantiate an instance of the identifier.
*
*
* @return the entity identifier value
*
* @deprecated Use {@link #getIdentifier(Object)}
* @apiNote This is really only valid on {@linkplain CompositeIdentifierMapping composite identifiers}
*/
@Deprecated
Object getIdentifier(Object entity, SharedSessionContractImplementor session);
Object instantiate();
/**
* Extract the identifier from an instance of the entity
*/
Object getIdentifier(Object entity);
/**
* Return the identifier of the persistent or transient object, or throw
* an exception if the instance is "unsaved"
@ -97,24 +105,39 @@ public interface EntityIdentifierMapping extends ValuedModelPart {
return id;
}
/**
* Inject an identifier value into an instance of the entity
*/
void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session);
Object instantiate();
/**
* The style of identifier used.
*/
enum Nature {
/**
* Single column id
* Simple, single-column identifier.
*
* @see Id
* @see BasicEntityIdentifierMapping
*/
SIMPLE,
/**
* @see jakarta.persistence.EmbeddedId
* An "aggregated" composite identifier, which is another way to say that the
* identifier is represented as an {@linkplain EmbeddedId embeddable}.
*
* @see EmbeddedId
* @see AggregatedIdentifierMapping
*/
COMPOSITE,
/**
* Composite identifier defined with multiple {@link jakarta.persistence.Id}
* mappings. Often used in conjunction with an {@link jakarta.persistence.IdClass}
* Composite identifier defined with multiple {@link Id}
* mappings. Often used in conjunction with an {@link IdClass}
*
* @see Id
* @see IdClass
* @see NonAggregatedIdentifierMapping
*/
VIRTUAL
}

View File

@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping;
import org.hibernate.engine.spi.VersionValue;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.VersionJavaType;
/**
@ -24,7 +23,9 @@ public interface EntityVersionMapping extends BasicValuedModelPart {
/**
* The strategy for distinguishing between detached and transient
* state based on the version mapping
* state based on the version mapping.
*
* @see EntityIdentifierMapping#getUnsavedStrategy()
*/
VersionValue getUnsavedStrategy();
@ -35,4 +36,9 @@ public interface EntityVersionMapping extends BasicValuedModelPart {
default VersionJavaType<?> getExpressibleJavaType() {
return (VersionJavaType<?>) getMappedType().getMappedJavaType();
}
@Override
default AttributeMapping asAttributeMapping() {
return getVersionAttribute();
}
}

View File

@ -16,7 +16,7 @@ import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
/**
* A "non-aggregated" composite identifier, which means that the entity itself
* does not define a singular representation of its identifier like an
* {@link jakarta.persistence.EmbeddedId} does.
* {@linkplain AggregatedIdentifierMapping aggregated mapping} does.
*
* An IdClass can be used to provide a simple, singular representation of the
* identifier for easier reference in API calls. JPA requires using an IdClass
@ -73,6 +73,7 @@ public interface NonAggregatedIdentifierMapping extends CompositeIdentifierMappi
* Convenience method to iterate the attributes for this mapper's representation
*/
default void forEachAttribute(IndexedConsumer<SingularAttributeMapping> consumer) {
//noinspection unchecked,rawtypes
getEmbeddedPart().getEmbeddableTypeDescriptor().forEachAttributeMapping( (IndexedConsumer) consumer );
}
}

View File

@ -47,6 +47,10 @@ import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
/**
* Mapping of a simple identifier
*
* @see jakarta.persistence.Id
*
* @author Andrea Boriero
*/
public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMapping, FetchOptions {
@ -96,6 +100,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
this.attributeName = attributeName;
this.rootTable = rootTable;
this.pkColumnName = pkColumnName;
//noinspection unchecked
this.idType = (BasicType<Object>) idType;
this.entityPersister = entityPersister;
@ -142,15 +147,6 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
return unsavedStrategy;
}
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( entity );
if ( lazyInitializer != null ) {
return lazyInitializer.getIdentifier();
}
return propertyAccess.getGetter().get( entity );
}
@Override
public Object getIdentifier(Object entity) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( entity );

View File

@ -10,6 +10,7 @@ import java.util.function.BiConsumer;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.AggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
@ -29,7 +30,7 @@ import org.hibernate.sql.results.graph.Fetchable;
*/
public class EmbeddedIdentifierMappingImpl
extends AbstractCompositeIdentifierMapping
implements SingleAttributeIdentifierMapping {
implements AggregatedIdentifierMapping {
private final String name;
private final EmbeddableMappingType embeddableDescriptor;
private final PropertyAccess propertyAccess;
@ -83,15 +84,6 @@ public class EmbeddedIdentifierMappingImpl
getEmbeddableTypeDescriptor().applySqlSelections( navigablePath, tableGroup, creationState, selectionConsumer );
}
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( entity );
if ( lazyInitializer != null ) {
return lazyInitializer.getIdentifier();
}
return propertyAccess.getGetter().get( entity );
}
@Override
public Object getIdentifier(Object entity) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( entity );

View File

@ -178,11 +178,6 @@ public class InverseNonAggregatedIdentifierMapping extends EmbeddedAttributeMapp
return super.toSqlExpression( tableGroup, clause, walker, sqlAstCreationState );
}
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
return getIdentifier( entity );
}
@Override
public Object getIdentifier(Object entity) {
if ( hasContainingClass() ) {

View File

@ -64,7 +64,6 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.PropertyBasedMapping;
import org.hibernate.metamodel.mapping.SelectableMapping;
@ -1386,12 +1385,12 @@ public class MappingModelCreationHelper {
SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) {
final EmbeddableMappingType embeddableTypeDescriptor = modelPart.getEmbeddableTypeDescriptor();
if ( modelPart instanceof NonAggregatedIdentifierMapping ) {
if ( modelPart instanceof org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping ) {
return new InverseNonAggregatedIdentifierMapping(
keyDeclaringType,
declaringTableGroupProducer,
selectableMappings,
(NonAggregatedIdentifierMapping) modelPart,
(org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping) modelPart,
embeddableTypeDescriptor,
creationProcess
);

View File

@ -107,13 +107,6 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
}
}
/**
* The entity whose identifier this mapping is the inverse of
*/
public EntityPersister getIdentifiedEntityDescriptor() {
return entityDescriptor;
}
@Override
public EmbeddableMappingType getMappedType() {
return virtualIdEmbeddable;
@ -209,11 +202,6 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
return null;
}
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
return getIdentifier( entity );
}
@Override
public Object getIdentifier(Object entity) {
if ( hasContainingClass() ) {

View File

@ -43,11 +43,6 @@ public class AnonymousTupleBasicEntityIdentifierMapping
return delegate.getUnsavedStrategy();
}
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
return delegate.getIdentifier( entity, session );
}
@Override
public Object getIdentifier(Object entity) {
return delegate.getIdentifier( entity );

View File

@ -13,7 +13,6 @@ import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
import org.hibernate.metamodel.model.domain.DomainType;
@ -37,7 +36,7 @@ public class AnonymousTupleEmbeddedEntityIdentifierMapping extends AnonymousTupl
modelParts,
domainType,
componentName,
(EmbeddableValuedModelPart) delegate,
delegate,
-1
);
this.delegate = delegate;
@ -53,11 +52,6 @@ public class AnonymousTupleEmbeddedEntityIdentifierMapping extends AnonymousTupl
return delegate.getUnsavedStrategy();
}
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
return delegate.getIdentifier( entity, session );
}
@Override
public Object getIdentifier(Object entity) {
return delegate.getIdentifier( entity );

View File

@ -59,11 +59,6 @@ public class AnonymousTupleNonAggregatedEntityIdentifierMapping extends Anonymou
return delegate.getUnsavedStrategy();
}
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
return delegate.getIdentifier( entity, session );
}
@Override
public Object getIdentifier(Object entity) {
return delegate.getIdentifier( entity );

View File

@ -5073,10 +5073,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final EntityIdentifierMapping identifierMapping = entityValuedModelPart.getEntityMappingType()
.getIdentifierMapping();
associationKeyPart = identifierMapping;
associationKey = identifierMapping.getIdentifier(
literal.getLiteralValue(),
null
);
associationKey = identifierMapping.getIdentifier( literal.getLiteralValue() );
}
if ( associationKeyPart instanceof BasicValuedMapping ) {
return new QueryLiteral<>(

View File

@ -0,0 +1,131 @@
/*
* 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.mapping;
import java.time.Instant;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.retail.CardPayment;
import org.hibernate.testing.orm.domain.retail.CashPayment;
import org.hibernate.testing.orm.domain.retail.DomesticVendor;
import org.hibernate.testing.orm.domain.retail.ForeignVendor;
import org.hibernate.testing.orm.domain.retail.Payment;
import org.hibernate.testing.orm.domain.retail.Product;
import org.hibernate.testing.orm.domain.retail.Vendor;
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.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests access to {@link MappingMetamodel} as API and SPI contracts
*
* @author Steve Ebersole
*/
@DomainModel(standardModels = StandardDomainModel.RETAIL)
@SessionFactory(exportSchema = false)
public class MappingModelAccessTests {
@Test
public void testUnwrapAccess(SessionFactoryScope scope) {
final SessionFactoryImplementor sf = scope.getSessionFactory();
final MappingMetamodel mappingMetamodel = sf.unwrap( MappingMetamodel.class );
assertThat( mappingMetamodel ).isNotNull();
final MappingMetamodelImplementor mappingMetamodelImplementor = sf.unwrap( MappingMetamodelImplementor.class );
assertThat( mappingMetamodelImplementor ).isSameAs( mappingMetamodel );
}
@Test
void testEntityMappingAccess(SessionFactoryScope scope) {
final SessionFactoryImplementor sf = scope.getSessionFactory();
final MappingMetamodel mappingMetamodel = sf.unwrap( MappingMetamodel.class );
final EntityMappingType productMapping = mappingMetamodel.getEntityDescriptor( Product.class );
assertThat( productMapping ).isNotNull();
final EntityIdentifierMapping productIdMapping = productMapping.getIdentifierMapping();
assertThat( productIdMapping ).isNotNull();
assertThat( productIdMapping.getJavaType().getJavaTypeClass() ).isEqualTo( Integer.class );
final EntityVersionMapping productVersionMapping = productMapping.getVersionMapping();
assertThat( productVersionMapping ).isNotNull();
assertThat( productVersionMapping.getVersionAttribute().getAttributeName() ).isEqualTo( "version" );
assertThat( productVersionMapping.getVersionAttribute().getJavaType().getJavaTypeClass() ).isEqualTo( Instant.class );
assertThat( productVersionMapping.asAttributeMapping() ).isSameAs( productVersionMapping.getVersionAttribute() );
}
@Test
void testJoinedSubclassInheritance(SessionFactoryScope scope) {
final SessionFactoryImplementor sf = scope.getSessionFactory();
final MappingMetamodel mappingMetamodel = sf.unwrap( MappingMetamodel.class );
final EntityMappingType paymentMapping = mappingMetamodel.getEntityDescriptor( Payment.class );
final EntityMappingType cardPaymentMapping = mappingMetamodel.getEntityDescriptor( CardPayment.class );
final EntityMappingType cashPaymentMapping = mappingMetamodel.getEntityDescriptor( CashPayment.class );
final EntityDiscriminatorMapping discriminatorMapping = paymentMapping.getDiscriminatorMapping();
assertThat( discriminatorMapping )
.isSameAs( cardPaymentMapping.getDiscriminatorMapping() )
.isSameAs( cashPaymentMapping.getDiscriminatorMapping() );
assertThat( discriminatorMapping.isPhysical() ).isFalse();
assertThat( discriminatorMapping.isVirtual() ).isTrue();
assertThat( discriminatorMapping.getJavaType().getJavaTypeClass() ).isEqualTo( Class.class );
assertThat( discriminatorMapping.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( Class.class );
assertThat( discriminatorMapping.getJdbcMapping().getJdbcJavaType().getJavaTypeClass() ).isEqualTo( Integer.class );
assertThat( paymentMapping.getDiscriminatorValue() ).isEqualTo( 0 );
assertThat( cashPaymentMapping.getDiscriminatorValue() ).isEqualTo( 1 );
assertThat( cardPaymentMapping.getDiscriminatorValue() ).isEqualTo( 2 );
assertThat( discriminatorMapping.resolveDiscriminatorValue( 0 ).getIndicatedEntity() ).isEqualTo( paymentMapping );
assertThat( discriminatorMapping.resolveDiscriminatorValue( 1 ).getIndicatedEntity() ).isEqualTo( cashPaymentMapping );
assertThat( discriminatorMapping.resolveDiscriminatorValue( 2 ).getIndicatedEntity() ).isEqualTo( cardPaymentMapping );
assertThat( discriminatorMapping.resolveDiscriminatorValue( 3 ) ).isNull();
}
@Test
void testSingleTableInheritance(SessionFactoryScope scope) {
final SessionFactoryImplementor sf = scope.getSessionFactory();
final MappingMetamodel mappingMetamodel = sf.unwrap( MappingMetamodel.class );
final EntityMappingType vendorMapping = mappingMetamodel.getEntityDescriptor( Vendor.class );
final EntityMappingType domesticVendorMapping = mappingMetamodel.getEntityDescriptor( DomesticVendor.class );
final EntityMappingType foreignVendorMapping = mappingMetamodel.getEntityDescriptor( ForeignVendor.class );
final EntityDiscriminatorMapping discriminatorMapping = vendorMapping.getDiscriminatorMapping();
assertThat( discriminatorMapping )
.isSameAs( domesticVendorMapping.getDiscriminatorMapping() )
.isSameAs( foreignVendorMapping.getDiscriminatorMapping() );
assertThat( discriminatorMapping.isPhysical() ).isTrue();
assertThat( discriminatorMapping.isVirtual() ).isTrue();
assertThat( discriminatorMapping.getJavaType().getJavaTypeClass() ).isEqualTo( Class.class );
assertThat( discriminatorMapping.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo( Class.class );
assertThat( discriminatorMapping.getJdbcMapping().getJdbcJavaType().getJavaTypeClass() ).isEqualTo( String.class );
assertThat( vendorMapping.getDiscriminatorValue() ).isEqualTo( "Vendor" );
assertThat( domesticVendorMapping.getDiscriminatorValue() ).isEqualTo( "domestic" );
assertThat( foreignVendorMapping.getDiscriminatorValue() ).isEqualTo( "foreign" );
assertThat( discriminatorMapping.resolveDiscriminatorValue( "Vendor" ).getIndicatedEntity() ).isEqualTo( vendorMapping );
assertThat( discriminatorMapping.resolveDiscriminatorValue( "domestic" ).getIndicatedEntity() ).isEqualTo( domesticVendorMapping );
assertThat( discriminatorMapping.resolveDiscriminatorValue( "foreign" ).getIndicatedEntity() ).isEqualTo( foreignVendorMapping );
assertThat( discriminatorMapping.resolveDiscriminatorValue( "invalid" ) ).isNull();
}
}

View File

@ -6,14 +6,20 @@
*/
package org.hibernate.testing.orm.domain.retail;
import java.time.Instant;
import java.util.UUID;
import javax.money.MonetaryAmount;
import org.hibernate.annotations.CurrentTimestamp;
import org.hibernate.annotations.NaturalId;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import org.hibernate.annotations.NaturalId;
import jakarta.persistence.Version;
/**
* @author Steve Ebersole
@ -27,6 +33,11 @@ public class Product {
private MonetaryAmount currentSellPrice;
@Access( AccessType.FIELD )
@Version
@CurrentTimestamp
private Instant version;
public Product() {
}