Skip to content

Commit

Permalink
HHH-16339 - Unify entity and any discriminator handling
Browse files Browse the repository at this point in the history
  • Loading branch information
sebersole committed Mar 27, 2023
1 parent ba9ea8b commit 8f321f6
Show file tree
Hide file tree
Showing 39 changed files with 844 additions and 557 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.PropertyNotFoundException;
import org.hibernate.internal.EntityManagerMessageLogger;
import org.hibernate.internal.HEMLogging;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.List;
Expand Down Expand Up @@ -245,10 +246,12 @@ public static <Y> DomainType<Y> determineSimpleType(ValueContext typeContext, Me
.getJavaTypeRegistry()
.resolveDescriptor( anyType.getReturnedClass() );
return (DomainType<Y>) new AnyMappingDomainTypeImpl(
(Any) typeContext.getHibernateValue(),
anyType,
(JavaType<Class>) baseJtd,
context.getTypeConfiguration(),
context.getMetamodel()
context.getMetamodel(),
context.getRuntimeModelCreationContext().getSessionFactory()
);
}
case EMBEDDABLE: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* Things that have a discriminator associated with it.
*/
public interface Discriminatable {
DiscriminatorMapping getDiscriminatorMapping();

/**
* Apply the discriminator as a predicate via the {@code predicateConsumer}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.metamodel.mapping;

import org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart;
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.Fetchable;
Expand All @@ -21,8 +22,15 @@
*
* @author Steve Ebersole
*/
public interface DiscriminatedAssociationModelPart extends Fetchable, FetchableContainer, TableGroupJoinProducer {
BasicValuedModelPart getDiscriminatorPart();
public interface DiscriminatedAssociationModelPart extends Discriminatable, Fetchable, FetchableContainer, TableGroupJoinProducer {
/**
* @deprecated Use {@link #getDiscriminatorMapping} instead.
*/
@Deprecated( since = "6.2", forRemoval = true )
default BasicValuedModelPart getDiscriminatorPart() {
return getDiscriminatorMapping();
}

BasicValuedModelPart getKeyPart();

EntityMappingType resolveDiscriminatorValue(Object discriminatorValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* 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 java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.mapping.internal.DiscriminatorValueDetailsImpl;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.java.JavaType;

import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR;
import static org.hibernate.persister.entity.DiscriminatorHelper.NULL_DISCRIMINATOR;

/**
* Conversion of discriminator values between the entity name/Class domain form and
* its generally CHARACTER or INTEGER based relational form
*
* @param <O> The domain type - either <ul>
* <li>
* the {@linkplain EntityMappingType#getMappedJavaType() entity Class} for unnamed entities
* </li>
* <li>
* the {@linkplain EntityMappingType#getEntityName() entity name} for named entities
* </li>
* </ul>
* @param <R> The Java type of the relational form of the discriminator
*
* @author Steve Ebersole
*/
public class DiscriminatorConverter<O,R> implements BasicValueConverter<O,R> {
public static <O,R> DiscriminatorConverter<O,R> fromValueMappings(
NavigableRole role,
JavaType<O> domainJavaType,
BasicType<R> underlyingJdbcMapping,
Map<Object,String> valueMappings,
SessionFactoryImplementor sessionFactory) {
final MappingMetamodelImplementor mappingMetamodel = sessionFactory.getMappingMetamodel();
final List<DiscriminatorValueDetails> valueDetailsList = CollectionHelper.arrayList( valueMappings.size() );
valueMappings.forEach( (value, entityName) -> {
final DiscriminatorValueDetails valueDetails = new DiscriminatorValueDetailsImpl(
value,
mappingMetamodel.getEntityDescriptor( entityName )
);
valueDetailsList.add( valueDetails );
} );

return new DiscriminatorConverter<>(
role,
domainJavaType,
underlyingJdbcMapping.getJavaTypeDescriptor(),
valueDetailsList
);
}

private final NavigableRole discriminatorRole;
private final JavaType<O> domainJavaType;
private final JavaType<R> relationalJavaType;

private final Map<Object, DiscriminatorValueDetails> discriminatorValueToEntityNameMap;
private final Map<String,DiscriminatorValueDetails> entityNameToDiscriminatorValueMap;

public DiscriminatorConverter(
NavigableRole discriminatorRole,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
List<DiscriminatorValueDetails> valueMappings) {
this.discriminatorRole = discriminatorRole;
this.domainJavaType = domainJavaType;
this.relationalJavaType = relationalJavaType;

this.discriminatorValueToEntityNameMap = CollectionHelper.concurrentMap( valueMappings.size() );
this.entityNameToDiscriminatorValueMap = CollectionHelper.concurrentMap( valueMappings.size() );
valueMappings.forEach( (valueDetails) -> {
discriminatorValueToEntityNameMap.put( valueDetails.getValue(), valueDetails );
entityNameToDiscriminatorValueMap.put( valueDetails.getIndicatedEntityName(), valueDetails );
} );
}

public NavigableRole getNavigableRole() {
return discriminatorRole;
}

@Override
public JavaType<O> getDomainJavaType() {
return domainJavaType;
}

@Override
public JavaType<R> getRelationalJavaType() {
return relationalJavaType;
}

public DiscriminatorValueDetails getDetailsForRelationalForm(R relationalForm) {
return getDetailsForDiscriminatorValue( relationalForm );
}

@Override
public O toDomainValue(R relationalForm) {
assert relationalForm == null || relationalJavaType.isInstance( relationalForm );

final DiscriminatorValueDetails matchingValueDetails = getDetailsForRelationalForm( relationalForm );
if ( matchingValueDetails == null ) {
throw new IllegalStateException( "Could not resolve discriminator value" );
}

final EntityMappingType indicatedEntity = matchingValueDetails.getIndicatedEntity();
//noinspection unchecked
return indicatedEntity.getRepresentationStrategy().getMode() == RepresentationMode.POJO
&& indicatedEntity.getEntityName().equals( indicatedEntity.getJavaType().getJavaTypeClass().getName() )
? (O) indicatedEntity.getJavaType().getJavaTypeClass()
: (O) indicatedEntity.getEntityName();
}

public DiscriminatorValueDetails getDetailsForEntityName(String entityName) {
return entityNameToDiscriminatorValueMap.get( entityName );
}

public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object value) {
if ( value == null ) {
return discriminatorValueToEntityNameMap.get( NULL_DISCRIMINATOR );
}

final DiscriminatorValueDetails valueMatch = discriminatorValueToEntityNameMap.get( value );
if ( valueMatch != null ) {
return valueMatch;
}

return discriminatorValueToEntityNameMap.get( NOT_NULL_DISCRIMINATOR );
}

@Override
public R toRelationalValue(O domainForm) {
assert domainForm == null || domainForm instanceof String || domainForm instanceof Class;

if ( domainForm == null ) {
return null;
}

final String entityName;
if ( domainForm instanceof Class ) {
entityName = ( (Class<?>) domainForm ).getName();
}
else {
entityName = (String) domainForm;
}

final DiscriminatorValueDetails discriminatorValueDetails = getDetailsForEntityName( entityName );
//noinspection unchecked
return (R) discriminatorValueDetails.getValue();
}

@Override
public String toString() {
return "DiscriminatorConverter(" + discriminatorRole.getFullPath() + ")";
}

public void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer) {
discriminatorValueToEntityNameMap.forEach( (value, detail) -> consumer.accept( detail ) );
}

public <X> X fromValueDetails(Function<DiscriminatorValueDetails,X> handler) {
for ( DiscriminatorValueDetails detail : discriminatorValueToEntityNameMap.values() ) {
final X result = handler.apply( detail );
if ( result != null ) {
return result;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.type.descriptor.java.JavaType;

/**
* Mapping of a discriminator, for either {@linkplain EntityMappingType#getDiscriminatorMapping() entity} or
* {@linkplain DiscriminatedAssociationModelPart#getDiscriminatorMapping() association} (ANY) discrimination.
* <p/>
* Represents a composition of <ul>
* <li>a {@linkplain #getValueConverter() converter} between the domain and relational form</li>
* <li>a {@linkplain #getUnderlyingJdbcMapping JDBC mapping} to read and write the relational values</li>
* </ul>
*
* @author Steve Ebersole
*/
public interface DiscriminatorMapping extends VirtualModelPart, BasicValuedModelPart, Fetchable {
/**
* Information about the value mappings
*/
DiscriminatorConverter<?,?> getValueConverter();

JdbcMapping getUnderlyingJdbcMapping();

/**
* The domain Java form, which is either {@code JavaType<Class>} (entity class)
* or {@code JavaType<String>} (entity name).
*/
default JavaType<?> getDomainJavaType() {
return getValueConverter().getDomainJavaType();
}

/**
* The relational Java form. This will typically be some form of integer
* or character value.
*/
default JavaType<?> getRelationalJavaType() {
return getValueConverter().getRelationalJavaType();
}

/**
* Create the appropriate SQL expression for this discriminator
*
* @param jdbcMappingToUse The JDBC mapping to use. This allows opting between
* the "domain result type" (aka Class) and the "underlying type" (Integer, String, etc)
*/
Expression resolveSqlExpression(
NavigablePath navigablePath,
JdbcMapping jdbcMappingToUse,
TableGroup tableGroup,
SqlAstCreationState creationState);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.model.domain.BasicDomainType;
import org.hibernate.type.BasicType;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.descriptor.java.JavaType;

/**
* Union of {@link ConvertedBasicType} and {@link BasicDomainType} capabilities.
*
* @implNote We need the {@link BasicDomainType} aspect for handling in SQM trees.
*
* @param <O> The Java type of the domain form of the discriminator.
*
* @author Steve Ebersole
*/
public interface DiscriminatorType<O> extends ConvertedBasicType<O>, BasicDomainType<O> {
@Override
DiscriminatorConverter<O, ?> getValueConverter();

BasicType<?> getUnderlyingJdbcMapping();

@Override
default JavaType<O> getJavaTypeDescriptor() {
return ConvertedBasicType.super.getJavaTypeDescriptor();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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;

/**
* Details for a particular discriminator value.
*
* @apiNote For {@linkplain jakarta.persistence.InheritanceType#JOINED joined} and
* {@linkplain jakarta.persistence.InheritanceType#TABLE_PER_CLASS union} inheritance,
* the discriminator also effectively indicates a specific table. That table can be
* found via {@linkplain EntityMappingType#getMappedTableDetails()} for the
* {@linkplain #getIndicatedEntity() indicated entity}
*
* @see jakarta.persistence.DiscriminatorValue
*/
public interface DiscriminatorValueDetails {
/**
* The discriminator value
*/
Object getValue();

/**
* The name of the concrete entity-type mapped to this {@linkplain #getValue() discriminator value}
*/
default String getIndicatedEntityName() {
return getIndicatedEntity().getEntityName();
}

/**
* Form of {@link #getIndicatedEntityName()} returning the matched {@link EntityMappingType}
*/
EntityMappingType getIndicatedEntity();
}

0 comments on commit 8f321f6

Please sign in to comment.