Introduce `VirtualIdEmbeddable` and `IdClassEmbeddable` + instantiators

- shared `#finishInitialization` handling for `VirtualIdEmbeddable` and `IdClassEmbeddable`
      note: was not yet able to get that working with `EmbeddableMappingType`
  - clean up ComponentType, esp wrt its use of ComponentTuplizer

Still need to
  - integrate embedded forms.  `VirtualIdEmbeddable` does not really need it as it can use the id-mapping itself as the embedded form.  But `IdClassEmbedded` should really be integrated
  - integrate `VirtualKeyEmbeddable` and `VirtualKeyEmbedded` for use as inverse composite fks
  - ability to use the containing composite owner as the parent of a composite (legacy behavior is to always use the "first" entity
This commit is contained in:
Steve Ebersole 2021-12-01 13:48:51 -06:00
parent 55bfc59db1
commit 8ab27a0ff0
4 changed files with 399 additions and 541 deletions

View File

@ -6,13 +6,48 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Locale;
import java.util.function.Consumer;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SharedSessionContract;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Base support for EmbeddableMappingType implementations
@ -71,4 +106,286 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
} );
}
}
@FunctionalInterface
protected interface ConcreteTableResolver {
String resolve(Column column, JdbcEnvironment jdbcEnvironment);
}
@FunctionalInterface
protected interface SuccessfulCompletionCallback {
void success();
}
protected static class IllegalAttributeType extends RuntimeException {
public IllegalAttributeType(String message) {
super( message );
}
}
@FunctionalInterface
protected interface AttributeTypeValidator {
void check(String name, Type type) throws IllegalAttributeType;
}
protected static boolean finishInitialization(
NavigableRole navigableRole,
Component bootDescriptor,
CompositeType compositeType,
String rootTableExpression,
String[] rootTableKeyColumnNames,
EmbeddableMappingType declarer,
EmbeddableRepresentationStrategy representationStrategy,
AttributeTypeValidator attributeTypeValidator,
ConcreteTableResolver concreteTableResolver,
Consumer<AttributeMapping> attributeConsumer,
SuccessfulCompletionCallback completionCallback,
MappingModelCreationProcess creationProcess) {
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final Dialect dialect = jdbcEnvironment.getDialect();
final Type[] subtypes = compositeType.getSubtypes();
int attributeIndex = 0;
int columnPosition = 0;
final Iterator<Property> propertyIterator = bootDescriptor.getPropertyIterator();
while ( propertyIterator.hasNext() ) {
final Property bootPropertyDescriptor = propertyIterator.next();
final Type subtype = subtypes[ attributeIndex ];
attributeTypeValidator.check( bootPropertyDescriptor.getName(), subtype );
final PropertyAccess propertyAccess = representationStrategy.resolvePropertyAccess( bootPropertyDescriptor );
final AttributeMapping attributeMapping;
if ( subtype instanceof BasicType ) {
final BasicValue basicValue = (BasicValue) bootPropertyDescriptor.getValue();
final Selectable selectable = basicValue.getColumn();
final String containingTableExpression;
final String columnExpression;
if ( rootTableKeyColumnNames == null ) {
if ( selectable.isFormula() ) {
columnExpression = selectable.getTemplate( dialect, creationProcess.getSqmFunctionRegistry() );
}
else {
columnExpression = selectable.getText( dialect );
}
if ( selectable instanceof Column ) {
containingTableExpression = concreteTableResolver.resolve( (Column) selectable, jdbcEnvironment );
}
else {
containingTableExpression = rootTableExpression;
}
}
else {
containingTableExpression = rootTableExpression;
columnExpression = rootTableKeyColumnNames[ columnPosition ];
}
attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping(
bootPropertyDescriptor.getName(),
navigableRole.append( bootPropertyDescriptor.getName() ),
attributeIndex,
bootPropertyDescriptor,
declarer,
(BasicType<?>) subtype,
containingTableExpression,
columnExpression,
selectable.isFormula(),
selectable.getCustomReadExpression(),
selectable.getCustomWriteExpression(),
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition++;
}
else if ( subtype instanceof AnyType ) {
final Any bootValueMapping = (Any) bootPropertyDescriptor.getValue();
final AnyType anyType = (AnyType) subtype;
final boolean nullable = bootValueMapping.isNullable();
final boolean insertable = bootPropertyDescriptor.isInsertable();
final boolean updateable = bootPropertyDescriptor.isUpdateable();
final boolean includeInOptimisticLocking = bootPropertyDescriptor.isOptimisticLocked();
final CascadeStyle cascadeStyle = compositeType.getCascadeStyle( attributeIndex );
final MutabilityPlan<?> mutabilityPlan;
if ( updateable ) {
mutabilityPlan = new MutabilityPlan<Object>() {
@Override
public boolean isMutable() {
return true;
}
@Override
public Object deepCopy(Object value) {
if ( value == null ) {
return null;
}
return anyType.deepCopy( value, creationProcess.getCreationContext().getSessionFactory() );
}
@Override
public Serializable disassemble(Object value, SharedSessionContract session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public Object assemble(Serializable cached, SharedSessionContract session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
};
}
else {
mutabilityPlan = ImmutableMutabilityPlan.INSTANCE;
}
final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() {
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
@Override
public MutabilityPlan<?> getMutabilityPlan() {
return mutabilityPlan;
}
@Override
public boolean isNullable() {
return nullable;
}
@Override
public boolean isInsertable() {
return insertable;
}
@Override
public boolean isUpdatable() {
return updateable;
}
@Override
public boolean isIncludedInDirtyChecking() {
// todo (6.0) : do not believe this is correct
return updateable;
}
@Override
public boolean isIncludedInOptimisticLocking() {
return includeInOptimisticLocking;
}
@Override
public CascadeStyle getCascadeStyle() {
return cascadeStyle;
}
};
attributeMapping = new DiscriminatedAssociationAttributeMapping(
navigableRole.append( bootPropertyDescriptor.getName() ),
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ),
declarer,
attributeIndex,
attributeMetadataAccess,
bootPropertyDescriptor.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE,
propertyAccess,
bootPropertyDescriptor,
anyType,
bootValueMapping,
creationProcess
);
}
else if ( subtype instanceof CompositeType ) {
final CompositeType subCompositeType = (CompositeType) subtype;
final int columnSpan = subCompositeType.getColumnSpan( sessionFactory );
final String subTableExpression;
final String[] subRootTableKeyColumnNames;
if ( rootTableKeyColumnNames == null ) {
subTableExpression = rootTableExpression;
subRootTableKeyColumnNames = null;
}
else {
subTableExpression = rootTableExpression;
subRootTableKeyColumnNames = new String[ columnSpan ];
System.arraycopy( rootTableKeyColumnNames, columnPosition, subRootTableKeyColumnNames, 0, columnSpan );
}
attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping(
bootPropertyDescriptor.getName(),
attributeIndex,
bootPropertyDescriptor,
declarer,
subCompositeType,
subTableExpression,
subRootTableKeyColumnNames,
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition += columnSpan;
}
else if ( subtype instanceof CollectionType ) {
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner()
.getEntityName() );
attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping(
bootPropertyDescriptor.getName(),
attributeIndex,
bootPropertyDescriptor,
entityPersister,
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
compositeType.getFetchMode( attributeIndex ),
creationProcess
);
}
else if ( subtype instanceof EntityType ) {
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner()
.getEntityName() );
attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping(
bootPropertyDescriptor.getName(),
navigableRole.append( bootPropertyDescriptor.getName() ),
attributeIndex,
bootPropertyDescriptor,
entityPersister,
entityPersister,
(EntityType) subtype,
representationStrategy.resolvePropertyAccess( bootPropertyDescriptor ),
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition += bootPropertyDescriptor.getColumnSpan();
}
else {
throw new MappingException(
String.format(
Locale.ROOT,
"Unable to determine attribute nature : %s#%s",
bootDescriptor.getOwner().getEntityName(),
bootPropertyDescriptor.getName()
)
);
}
attributeConsumer.accept( attributeMapping );
attributeIndex++;
}
completionCallback.success();
return true;
}
}

View File

@ -275,6 +275,32 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
String rootTableExpression,
String[] rootTableKeyColumnNames,
MappingModelCreationProcess creationProcess) {
// for some reason I cannot get this to work, though only a single test fails - `CompositeElementTest`
// return finishInitialization(
// getNavigableRole(),
// bootDescriptor,
// compositeType,
// rootTableExpression,
// rootTableKeyColumnNames,
// this,
// representationStrategy,
// (name, type) -> {},
// (column, jdbcEnvironment) -> getTableIdentifierExpression(
// column.getValue().getTable(),
// jdbcEnvironment
// ),
// this::addAttribute,
// () -> {
// // We need the attribute mapping types to finish initialization first before we can build the column mappings
// creationProcess.registerInitializationCallback(
// "EmbeddableMappingType(" + getEmbeddedValueMapping().getNavigableRole().getFullPath() + ")#initColumnMappings",
// this::initColumnMappings
// );
// },
// creationProcess
// );
// todo (6.0) - get this ^^ to work, or drop the comment
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();

View File

@ -6,33 +6,20 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SharedSessionContract;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@ -49,7 +36,6 @@ import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
@ -63,16 +49,10 @@ import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.spi.CompositeTypeImplementor;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.getStateArrayContributorMetadataAccess;
@ -468,8 +448,6 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// init
@ -479,263 +457,38 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
String rootTableExpression,
String[] rootTableKeyColumnNames,
MappingModelCreationProcess creationProcess) {
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final Dialect dialect = jdbcEnvironment.getDialect();
final Type[] subtypes = compositeType.getSubtypes();
int attributeIndex = 0;
int columnPosition = 0;
// Reset the attribute mappings that were added in previous attempts
this.attributeMappings.clear();
final Iterator<Property> propertyIterator = bootDescriptor.getPropertyIterator();
while ( propertyIterator.hasNext() ) {
final Property bootPropertyDescriptor = propertyIterator.next();
final PropertyAccess propertyAccess = getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor );
final AttributeMapping attributeMapping;
final Type subtype = subtypes[ attributeIndex ];
if ( subtype instanceof BasicType ) {
final BasicValue basicValue = (BasicValue) bootPropertyDescriptor.getValue();
final Selectable selectable = basicValue.getColumn();
final String containingTableExpression;
final String columnExpression;
if ( rootTableKeyColumnNames == null ) {
if ( selectable.isFormula() ) {
columnExpression = selectable.getTemplate( dialect, creationProcess.getSqmFunctionRegistry() );
return finishInitialization(
navigableRole,
bootDescriptor,
compositeType,
rootTableExpression,
rootTableKeyColumnNames,
this,
representationStrategy,
(attributeName, attributeType) -> {
if ( attributeType instanceof CollectionType ) {
throw new IllegalAttributeType( "An IdClass cannot define collection attributes : " + attributeName );
}
else {
columnExpression = selectable.getText( dialect );
if ( attributeType instanceof AnyType ) {
throw new IllegalAttributeType( "An IdClass cannot define <any/> attributes : " + attributeName );
}
if ( selectable instanceof Column ) {
containingTableExpression = getTableIdentifierExpression(
( (Column) selectable ).getValue().getTable(),
jdbcEnvironment
);
}
else {
containingTableExpression = rootTableExpression;
}
}
else {
containingTableExpression = rootTableExpression;
columnExpression = rootTableKeyColumnNames[ columnPosition ];
}
attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping(
bootPropertyDescriptor.getName(),
navigableRole.append( bootPropertyDescriptor.getName() ),
attributeIndex,
bootPropertyDescriptor,
this,
(BasicType<?>) subtype,
containingTableExpression,
columnExpression,
selectable.isFormula(),
selectable.getCustomReadExpression(),
selectable.getCustomWriteExpression(),
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition++;
}
else if ( subtype instanceof AnyType ) {
final Any bootValueMapping = (Any) bootPropertyDescriptor.getValue();
final AnyType anyType = (AnyType) subtype;
final boolean nullable = bootValueMapping.isNullable();
final boolean insertable = bootPropertyDescriptor.isInsertable();
final boolean updateable = bootPropertyDescriptor.isUpdateable();
final boolean includeInOptimisticLocking = bootPropertyDescriptor.isOptimisticLocked();
final CascadeStyle cascadeStyle = compositeType.getCascadeStyle( attributeIndex );
final MutabilityPlan<?> mutabilityPlan;
if ( updateable ) {
mutabilityPlan = new MutabilityPlan<Object>() {
@Override
public boolean isMutable() {
return true;
}
@Override
public Object deepCopy(Object value) {
if ( value == null ) {
return null;
}
return anyType.deepCopy( value, creationProcess.getCreationContext().getSessionFactory() );
}
@Override
public Serializable disassemble(Object value, SharedSessionContract session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public Object assemble(Serializable cached, SharedSessionContract session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
};
}
else {
mutabilityPlan = ImmutableMutabilityPlan.INSTANCE;
}
final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() {
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
@Override
public MutabilityPlan<?> getMutabilityPlan() {
return mutabilityPlan;
}
@Override
public boolean isNullable() {
return nullable;
}
@Override
public boolean isInsertable() {
return insertable;
}
@Override
public boolean isUpdatable() {
return updateable;
}
@Override
public boolean isIncludedInDirtyChecking() {
// todo (6.0) : do not believe this is correct
return updateable;
}
@Override
public boolean isIncludedInOptimisticLocking() {
return includeInOptimisticLocking;
}
@Override
public CascadeStyle getCascadeStyle() {
return cascadeStyle;
}
};
attributeMapping = new DiscriminatedAssociationAttributeMapping(
navigableRole.append( bootPropertyDescriptor.getName() ),
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ),
this,
attributeIndex,
attributeMetadataAccess,
bootPropertyDescriptor.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE,
propertyAccess,
bootPropertyDescriptor,
anyType,
bootValueMapping,
creationProcess
);
}
else if ( subtype instanceof CompositeType ) {
final CompositeType subCompositeType = (CompositeType) subtype;
final int columnSpan = subCompositeType.getColumnSpan( sessionFactory );
final String subTableExpression;
final String[] subRootTableKeyColumnNames;
if ( rootTableKeyColumnNames == null ) {
subTableExpression = rootTableExpression;
subRootTableKeyColumnNames = null;
}
else {
subTableExpression = rootTableExpression;
subRootTableKeyColumnNames = new String[ columnSpan ];
System.arraycopy( rootTableKeyColumnNames, columnPosition, subRootTableKeyColumnNames, 0, columnSpan );
}
attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping(
bootPropertyDescriptor.getName(),
attributeIndex,
bootPropertyDescriptor,
this,
subCompositeType,
subTableExpression,
subRootTableKeyColumnNames,
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition += columnSpan;
}
else if ( subtype instanceof CollectionType ) {
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner()
.getEntityName() );
attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping(
bootPropertyDescriptor.getName(),
attributeIndex,
bootPropertyDescriptor,
entityPersister,
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
compositeType.getFetchMode( attributeIndex ),
creationProcess
);
}
else if ( subtype instanceof EntityType ) {
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner()
.getEntityName() );
attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping(
bootPropertyDescriptor.getName(),
navigableRole.append( bootPropertyDescriptor.getName() ),
attributeIndex,
bootPropertyDescriptor,
entityPersister,
entityPersister,
(EntityType) subtype,
getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ),
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition += bootPropertyDescriptor.getColumnSpan();
}
else {
throw new MappingException(
String.format(
Locale.ROOT,
"Unable to determine attribute nature : %s#%s",
bootDescriptor.getOwner().getEntityName(),
bootPropertyDescriptor.getName()
)
);
}
addAttribute( (SingularAttributeMapping) attributeMapping );
attributeIndex++;
}
// We need the attribute mapping types to finish initialization first before we can build the column mappings
creationProcess.registerInitializationCallback(
"EmbeddableMappingType(" + navigableRole + ")#initColumnMappings",
this::initColumnMappings
},
(column, jdbcEnvironment) -> getTableIdentifierExpression( column.getValue().getTable(), jdbcEnvironment ),
this::addAttribute,
() -> {
// We need the attribute mapping types to finish initialization first before we can build the column mappings
creationProcess.registerInitializationCallback(
"IdClassEmbeddable(" + getNavigableRole() + ")#initColumnMappings",
this::initColumnMappings
);
},
creationProcess
);
return true;
}
private static String getTableIdentifierExpression(Table table, JdbcEnvironment jdbcEnvironment) {
return jdbcEnvironment
.getQualifiedObjectNameFormatter().format(
@ -749,6 +502,10 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
return true;
}
private void addAttribute(AttributeMapping attributeMapping) {
addAttribute( (SingularAttributeMapping) attributeMapping );
}
private void addAttribute(SingularAttributeMapping attributeMapping) {
// check if we've already seen this attribute...
for ( int i = 0; i < attributeMappings.size(); i++ ) {

View File

@ -6,29 +6,14 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SharedSessionContract;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@ -44,11 +29,8 @@ import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.tree.from.TableGroup;
@ -56,15 +38,9 @@ import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.spi.CompositeTypeImplementor;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
import static org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping.IdentifierValueMapper;
@ -375,259 +351,37 @@ public class VirtualIdEmbeddable extends AbstractEmbeddableMapping implements Id
String rootTableExpression,
String[] rootTableKeyColumnNames,
MappingModelCreationProcess creationProcess) {
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final Dialect dialect = jdbcEnvironment.getDialect();
final Type[] subtypes = compositeType.getSubtypes();
int attributeIndex = 0;
int columnPosition = 0;
// Reset the attribute mappings that were added in previous attempts
this.attributeMappings.clear();
final Iterator<Property> propertyIterator = bootDescriptor.getPropertyIterator();
while ( propertyIterator.hasNext() ) {
final Property bootPropertyDescriptor = propertyIterator.next();
final PropertyAccess propertyAccess = getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor );
final AttributeMapping attributeMapping;
final Type subtype = subtypes[ attributeIndex ];
if ( subtype instanceof BasicType ) {
final BasicValue basicValue = (BasicValue) bootPropertyDescriptor.getValue();
final Selectable selectable = basicValue.getColumn();
final String containingTableExpression;
final String columnExpression;
if ( rootTableKeyColumnNames == null ) {
if ( selectable.isFormula() ) {
columnExpression = selectable.getTemplate( dialect, creationProcess.getSqmFunctionRegistry() );
return finishInitialization(
navigableRole,
bootDescriptor,
compositeType,
rootTableExpression,
rootTableKeyColumnNames,
this,
representationStrategy,
(attributeName, attributeType) -> {
if ( attributeType instanceof CollectionType ) {
throw new IllegalAttributeType( "A \"virtual id\" cannot define collection attributes : " + attributeName );
}
else {
columnExpression = selectable.getText( dialect );
if ( attributeType instanceof AnyType ) {
throw new IllegalAttributeType( "A \"virtual id\" cannot define <any/> attributes : " + attributeName );
}
if ( selectable instanceof Column ) {
containingTableExpression = getTableIdentifierExpression(
( (Column) selectable ).getValue().getTable(),
jdbcEnvironment
);
}
else {
containingTableExpression = rootTableExpression;
}
}
else {
containingTableExpression = rootTableExpression;
columnExpression = rootTableKeyColumnNames[ columnPosition ];
}
attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping(
bootPropertyDescriptor.getName(),
navigableRole.append( bootPropertyDescriptor.getName() ),
attributeIndex,
bootPropertyDescriptor,
this,
(BasicType<?>) subtype,
containingTableExpression,
columnExpression,
selectable.isFormula(),
selectable.getCustomReadExpression(),
selectable.getCustomWriteExpression(),
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition++;
}
else if ( subtype instanceof AnyType ) {
final Any bootValueMapping = (Any) bootPropertyDescriptor.getValue();
final AnyType anyType = (AnyType) subtype;
final boolean nullable = bootValueMapping.isNullable();
final boolean insertable = bootPropertyDescriptor.isInsertable();
final boolean updateable = bootPropertyDescriptor.isUpdateable();
final boolean includeInOptimisticLocking = bootPropertyDescriptor.isOptimisticLocked();
final CascadeStyle cascadeStyle = compositeType.getCascadeStyle( attributeIndex );
final MutabilityPlan<?> mutabilityPlan;
if ( updateable ) {
mutabilityPlan = new MutabilityPlan<Object>() {
@Override
public boolean isMutable() {
return true;
}
@Override
public Object deepCopy(Object value) {
if ( value == null ) {
return null;
}
return anyType.deepCopy( value, creationProcess.getCreationContext().getSessionFactory() );
}
@Override
public Serializable disassemble(Object value, SharedSessionContract session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public Object assemble(Serializable cached, SharedSessionContract session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
};
}
else {
mutabilityPlan = ImmutableMutabilityPlan.INSTANCE;
}
final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() {
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
@Override
public MutabilityPlan<?> getMutabilityPlan() {
return mutabilityPlan;
}
@Override
public boolean isNullable() {
return nullable;
}
@Override
public boolean isInsertable() {
return insertable;
}
@Override
public boolean isUpdatable() {
return updateable;
}
@Override
public boolean isIncludedInDirtyChecking() {
// todo (6.0) : do not believe this is correct
return updateable;
}
@Override
public boolean isIncludedInOptimisticLocking() {
return includeInOptimisticLocking;
}
@Override
public CascadeStyle getCascadeStyle() {
return cascadeStyle;
}
};
attributeMapping = new DiscriminatedAssociationAttributeMapping(
navigableRole.append( bootPropertyDescriptor.getName() ),
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ),
this,
attributeIndex,
attributeMetadataAccess,
bootPropertyDescriptor.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE,
propertyAccess,
bootPropertyDescriptor,
anyType,
bootValueMapping,
creationProcess
);
}
else if ( subtype instanceof CompositeType ) {
final CompositeType subCompositeType = (CompositeType) subtype;
final int columnSpan = subCompositeType.getColumnSpan( sessionFactory );
final String subTableExpression;
final String[] subRootTableKeyColumnNames;
if ( rootTableKeyColumnNames == null ) {
subTableExpression = rootTableExpression;
subRootTableKeyColumnNames = null;
}
else {
subTableExpression = rootTableExpression;
subRootTableKeyColumnNames = new String[ columnSpan ];
System.arraycopy( rootTableKeyColumnNames, columnPosition, subRootTableKeyColumnNames, 0, columnSpan );
}
attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping(
bootPropertyDescriptor.getName(),
attributeIndex,
bootPropertyDescriptor,
this,
subCompositeType,
subTableExpression,
subRootTableKeyColumnNames,
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition += columnSpan;
}
else if ( subtype instanceof CollectionType ) {
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner()
.getEntityName() );
attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping(
bootPropertyDescriptor.getName(),
attributeIndex,
bootPropertyDescriptor,
entityPersister,
propertyAccess,
compositeType.getCascadeStyle( attributeIndex ),
compositeType.getFetchMode( attributeIndex ),
creationProcess
);
}
else if ( subtype instanceof EntityType ) {
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner()
.getEntityName() );
attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping(
bootPropertyDescriptor.getName(),
navigableRole.append( bootPropertyDescriptor.getName() ),
attributeIndex,
bootPropertyDescriptor,
entityPersister,
entityPersister,
(EntityType) subtype,
getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ),
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
columnPosition += bootPropertyDescriptor.getColumnSpan();
}
else {
throw new MappingException(
String.format(
Locale.ROOT,
"Unable to determine attribute nature : %s#%s",
bootDescriptor.getOwner().getEntityName(),
bootPropertyDescriptor.getName()
)
);
}
addAttribute( (SingularAttributeMapping) attributeMapping );
attributeIndex++;
}
// We need the attribute mapping types to finish initialization first before we can build the column mappings
creationProcess.registerInitializationCallback(
"EmbeddableMappingType(" + navigableRole + ")#initColumnMappings",
this::initColumnMappings
},
(column, jdbcEnvironment) -> getTableIdentifierExpression( column.getValue().getTable(), jdbcEnvironment ),
this::addAttribute,
() -> {
// We need the attribute mapping types to finish initialization first before we can build the column mappings
creationProcess.registerInitializationCallback(
"VirtualIdEmbeddable(" + navigableRole + ")#initColumnMappings",
this::initColumnMappings
);
},
creationProcess
);
return true;
}
@ -645,6 +399,10 @@ public class VirtualIdEmbeddable extends AbstractEmbeddableMapping implements Id
return true;
}
private void addAttribute(AttributeMapping attributeMapping) {
addAttribute( (SingularAttributeMapping) attributeMapping );
}
private void addAttribute(SingularAttributeMapping attributeMapping) {
// check if we've already seen this attribute...
for ( int i = 0; i < attributeMappings.size(); i++ ) {