Add inverse NonAggregatedIdentifierMapping that uses VirtualIdEmbeddable as model part

This commit is contained in:
Christian Beikov 2022-03-05 17:14:41 +01:00
parent 3da5571867
commit 246f1048a0
9 changed files with 572 additions and 79 deletions

View File

@ -7,6 +7,7 @@
package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
@ -26,10 +27,15 @@ import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata;
import org.hibernate.metamodel.mapping.AttributeMetadataAccess;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
@ -37,6 +43,7 @@ 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.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType;
@ -127,6 +134,79 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
void check(String name, Type type) throws IllegalAttributeType;
}
protected static boolean inverseInitializeCallback(
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings,
EmbeddableMappingType inverseMappingType,
MappingModelCreationProcess creationProcess,
ManagedMappingType declaringType,
List<? extends AttributeMapping> attributeMappings) {
if ( inverseMappingType.getAttributeMappings().isEmpty() ) {
return false;
}
//noinspection unchecked
final List<AttributeMapping> mappings = (List<AttributeMapping>) attributeMappings;
// Reset the attribute mappings that were added in previous attempts
mappings.clear();
int currentIndex = 0;
// We copy the attributes from the inverse mappings and replace the selection mappings
for ( AttributeMapping attributeMapping : inverseMappingType.getAttributeMappings() ) {
if ( attributeMapping instanceof BasicAttributeMapping ) {
final BasicAttributeMapping original = (BasicAttributeMapping) attributeMapping;
final SelectableMapping selectableMapping = selectableMappings.getSelectable( currentIndex );
attributeMapping = BasicAttributeMapping.withSelectableMapping(
declaringType,
original,
original.getPropertyAccess(),
original.getValueGeneration(),
selectableMapping
);
currentIndex++;
}
else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping original = (ToOneAttributeMapping) attributeMapping;
ForeignKeyDescriptor foreignKeyDescriptor = original.getForeignKeyDescriptor();
if ( foreignKeyDescriptor==null ) {
// This is expected to happen when processing a
// PostInitCallbackEntry because the callbacks
// are not ordered. The exception is caught in
// MappingModelCreationProcess.executePostInitCallbacks()
// and the callback is re-queued.
throw new IllegalStateException( "Not yet ready: " + original );
}
final ToOneAttributeMapping toOne = original.copy(
declaringType,
declaringTableGroupProducer
);
final int offset = currentIndex;
toOne.setIdentifyingColumnsTableExpression(
selectableMappings.getSelectable( offset ).getContainingTableExpression()
);
toOne.setForeignKeyDescriptor(
foreignKeyDescriptor.withKeySelectionMapping(
declaringType,
declaringTableGroupProducer,
index -> selectableMappings.getSelectable( offset + index ),
creationProcess
)
);
attributeMapping = toOne;
currentIndex += attributeMapping.getJdbcTypeCount();
}
else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
attributeMapping = ( (EmbeddedAttributeMapping) attributeMapping ).copy( declaringType );
currentIndex = attributeMapping.getJdbcTypeCount();
}
else {
throw new UnsupportedMappingException(
"Only basic and to-one attributes are supported in composite fks" );
}
mappings.add( attributeMapping );
}
return true;
}
protected static boolean finishInitialization(
NavigableRole navigableRole,
Component bootDescriptor,

View File

@ -33,16 +33,13 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata;
import org.hibernate.metamodel.mapping.AttributeMetadataAccess;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
@ -170,73 +167,16 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
this.valueMapping = valueMapping;
this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled();
this.selectableMappings = selectableMappings;
final ManagedMappingType declaringType = valueMapping.getDeclaringType();
creationProcess.registerInitializationCallback(
"EmbeddableMappingType(" + inverseMappingType.getNavigableRole().getFullPath() + ".{inverse})#finishInitialization",
() -> {
if ( inverseMappingType.getAttributeMappings().isEmpty() ) {
return false;
}
// Reset the attribute mappings that were added in previous attempts
this.attributeMappings.clear();
int currentIndex = 0;
// We copy the attributes from the inverse mappings and replace the selection mappings
for ( AttributeMapping attributeMapping : inverseMappingType.getAttributeMappings() ) {
if ( attributeMapping instanceof BasicAttributeMapping ) {
final BasicAttributeMapping original = (BasicAttributeMapping) attributeMapping;
final SelectableMapping selectableMapping = selectableMappings.getSelectable( currentIndex );
attributeMapping = BasicAttributeMapping.withSelectableMapping(
declaringType,
original,
original.getPropertyAccess(),
original.getValueGeneration(),
selectableMapping
);
currentIndex++;
}
else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping original = (ToOneAttributeMapping) attributeMapping;
ForeignKeyDescriptor foreignKeyDescriptor = original.getForeignKeyDescriptor();
if ( foreignKeyDescriptor==null ) {
// This is expected to happen when processing a
// PostInitCallbackEntry because the callbacks
// are not ordered. The exception is caught in
// MappingModelCreationProcess.executePostInitCallbacks()
// and the callback is re-queued.
throw new IllegalStateException( "Not yet ready: " + original );
}
final ToOneAttributeMapping toOne = original.copy(
declaringType,
declaringTableGroupProducer
);
final int offset = currentIndex;
toOne.setIdentifyingColumnsTableExpression(
selectableMappings.getSelectable( offset ).getContainingTableExpression()
);
toOne.setForeignKeyDescriptor(
foreignKeyDescriptor.withKeySelectionMapping(
declaringType,
declaringTableGroupProducer,
index -> selectableMappings.getSelectable( offset + index ),
creationProcess
)
);
attributeMapping = toOne;
currentIndex += attributeMapping.getJdbcTypeCount();
}
else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
attributeMapping = ( (EmbeddedAttributeMapping) attributeMapping ).copy( declaringType );
currentIndex = attributeMapping.getJdbcTypeCount();
}
else {
throw new UnsupportedMappingException(
"Only basic and to-one attributes are supported in composite fks" );
}
this.attributeMappings.add( attributeMapping );
}
return true;
}
() -> inverseInitializeCallback(
declaringTableGroupProducer,
selectableMappings,
inverseMappingType,
creationProcess,
valueMapping.getDeclaringType(),
this.attributeMappings
)
);
}

View File

@ -123,7 +123,13 @@ public class EntityCollectionPart
if ( referencedPropertyName == null ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
final Type propertyType = entityBinding.getIdentifier().getType();
final Type propertyType;
if ( entityBinding.getIdentifierMapper() == null ) {
propertyType = entityBinding.getIdentifier().getType();
}
else {
propertyType = entityBinding.getIdentifierMapper().getType();
}
if ( entityBinding.getIdentifierProperty() == null ) {
final CompositeType compositeType;
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()

View File

@ -134,6 +134,36 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
}
public IdClassEmbeddable(
EmbeddedAttributeMapping valueMapping,
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings,
IdClassEmbeddable inverseMappingType,
MappingModelCreationProcess creationProcess) {
super( creationProcess );
this.navigableRole = inverseMappingType.getNavigableRole();
this.idMapping = (NonAggregatedIdentifierMapping) valueMapping;;
this.virtualIdEmbeddable = (VirtualIdEmbeddable) valueMapping.getEmbeddableTypeDescriptor();
this.javaType = inverseMappingType.javaType;
this.representationStrategy = new IdClassRepresentationStrategy( this );
this.attributeMappings = arrayList( inverseMappingType.attributeMappings.size() );
this.embedded = valueMapping;
this.selectableMappings = selectableMappings;
creationProcess.registerInitializationCallback(
"IdClassEmbeddable(" + inverseMappingType.getNavigableRole().getFullPath() + ".{inverse})#finishInitialization",
() -> inverseInitializeCallback(
declaringTableGroupProducer,
selectableMappings,
inverseMappingType,
creationProcess,
valueMapping.getDeclaringType(),
this.attributeMappings
)
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// IdentifierValueMapper
@ -427,7 +457,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) {
return new EmbeddableMappingTypeImpl(
return new IdClassEmbeddable(
valueMapping,
declaringTableGroupProducer,
selectableMappings,

View File

@ -0,0 +1,324 @@
/*
* 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.internal;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
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.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.RootClass;
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.NavigablePath;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.graph.DomainResultCreationState;
/**
* The inverse part of a "non-aggregated" composite identifier.
*
* Exposes the virtual id embeddable as mapping type, which requires the attribute mapping to implement {@link NonAggregatedIdentifierMapping}.
*/
public class InverseNonAggregatedIdentifierMapping extends EmbeddedAttributeMapping implements NonAggregatedIdentifierMapping {
private final IdClassEmbeddable idClassEmbeddable;
private final NonAggregatedIdentifierMapping.IdentifierValueMapper identifierValueMapper;
// Constructor is only used for creating the inverse attribute mapping
InverseNonAggregatedIdentifierMapping(
ManagedMappingType keyDeclaringType,
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings,
NonAggregatedIdentifierMapping inverseModelPart,
EmbeddableMappingType embeddableTypeDescriptor,
MappingModelCreationProcess creationProcess) {
super(
keyDeclaringType,
declaringTableGroupProducer,
selectableMappings,
inverseModelPart,
embeddableTypeDescriptor,
creationProcess
);
if ( inverseModelPart.getIdClassEmbeddable() == null ) {
this.idClassEmbeddable = null;
this.identifierValueMapper = (NonAggregatedIdentifierMapping.IdentifierValueMapper) super.getEmbeddableTypeDescriptor();
}
else {
this.idClassEmbeddable = (IdClassEmbeddable) inverseModelPart.getIdClassEmbeddable().createInverseMappingType(
this,
declaringTableGroupProducer,
selectableMappings,
creationProcess
);
identifierValueMapper = idClassEmbeddable;
}
}
@Override
public Object instantiate() {
return null;
}
@Override
public String getPartName() {
return super.getPartName();
}
@Override
public EmbeddableMappingType getPartMappingType() {
return (EmbeddableMappingType) super.getPartMappingType();
}
// --------------
@Override
public IdClassEmbeddable getIdClassEmbeddable() {
return idClassEmbeddable;
}
@Override
public VirtualIdEmbeddable getVirtualIdEmbeddable() {
return (VirtualIdEmbeddable) getMappedType();
}
@Override
public NonAggregatedIdentifierMapping.IdentifierValueMapper getIdentifierValueMapper() {
return identifierValueMapper;
}
@Override
public boolean hasContainingClass() {
return idClassEmbeddable != null;
}
@Override
public EmbeddableMappingType getMappedIdEmbeddableTypeDescriptor() {
return identifierValueMapper;
}
@Override
public Object disassemble(Object value, SharedSessionContractImplementor session) {
return identifierValueMapper.disassemble( value, session );
}
@Override
public int forEachJdbcValue(
Object value,
Clause clause,
int offset,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
return identifierValueMapper.forEachJdbcValue( value, clause, offset, valuesConsumer, session );
}
@Override
public SqlTuple toSqlExpression(
TableGroup tableGroup,
Clause clause,
SqmToSqlAstConverter walker,
SqlAstCreationState sqlAstCreationState) {
if ( hasContainingClass() ) {
final SelectableMappings selectableMappings = getEmbeddableTypeDescriptor();
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( selectableMappings.getJdbcTypeCount() );
final NavigablePath navigablePath = tableGroup.getNavigablePath()
.append( getNavigableRole().getNavigableName() );
final TableReference defaultTableReference = tableGroup.resolveTableReference(
navigablePath,
getContainingTableExpression()
);
int offset = 0;
for ( AttributeMapping attributeMapping : identifierValueMapper.getAttributeMappings() ) {
offset += attributeMapping.forEachSelectable(
offset,
(columnIndex, selection) -> {
final TableReference tableReference = defaultTableReference.resolveTableReference( selection.getContainingTableExpression() ) != null
? defaultTableReference
: tableGroup.resolveTableReference( navigablePath, selection.getContainingTableExpression() );
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver()
.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
selection.getSelectionExpression()
),
sqlAstProcessingState -> new ColumnReference(
tableReference.getIdentificationVariable(),
selection,
sqlAstCreationState.getCreationContext().getSessionFactory()
)
);
columnReferences.add( (ColumnReference) columnReference );
}
);
}
return new SqlTuple( columnReferences, this );
}
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() ) {
final Object id = identifierValueMapper.getRepresentationStrategy().getInstantiator().instantiate(
null,
null//sessionFactory
);
final List<AttributeMapping> attributeMappings = getEmbeddableTypeDescriptor().getAttributeMappings();
final List<AttributeMapping> idClassAttributeMappings = identifierValueMapper.getAttributeMappings();
final Object[] propertyValues = new Object[attributeMappings.size()];
for ( int i = 0; i < propertyValues.length; i++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( i );
final Object o = attributeMapping.getPropertyAccess().getGetter().get( entity );
if ( o == null ) {
final AttributeMapping idClassAttributeMapping = idClassAttributeMappings.get( i );
if ( idClassAttributeMapping.getPropertyAccess().getGetter().getReturnTypeClass().isPrimitive() ) {
propertyValues[i] = idClassAttributeMapping.getExpressibleJavaType().getDefaultValue();
}
else {
propertyValues[i] = null;
}
}
//JPA 2 @MapsId + @IdClass points to the pk of the entity
else if ( attributeMapping instanceof ToOneAttributeMapping
&& !( idClassAttributeMappings.get( i ) instanceof ToOneAttributeMapping ) ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart(
toOneAttributeMapping.getSideNature().inverse()
);
if ( targetPart instanceof EntityIdentifierMapping ) {
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o );
}
else {
propertyValues[i] = o;
assert false;
}
}
else {
propertyValues[i] = o;
}
}
identifierValueMapper.setValues( id, propertyValues );
return id;
}
else {
return entity;
}
}
@Override
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
final List<AttributeMapping> mappedIdAttributeMappings = identifierValueMapper.getAttributeMappings();
final Object[] propertyValues = new Object[mappedIdAttributeMappings.size()];
final SessionFactoryImplementor factory = session.getFactory();
final EntityPersister entityDescriptor = factory.getRuntimeMetamodels()
.getMappingMetamodel()
.getEntityDescriptor( entity.getClass() );
getEmbeddableTypeDescriptor().forEachAttributeMapping(
(position, attribute) -> {
final AttributeMapping mappedIdAttributeMapping = mappedIdAttributeMappings.get( position );
final Object o = mappedIdAttributeMapping.getPropertyAccess().getGetter().get( id );
if ( attribute instanceof ToOneAttributeMapping && !( mappedIdAttributeMapping instanceof ToOneAttributeMapping ) ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attribute;
final EntityPersister entityPersister = toOneAttributeMapping.getEntityMappingType()
.getEntityPersister();
final EntityKey entityKey = session.generateEntityKey( o, entityPersister );
final PersistenceContext persistenceContext = session.getPersistenceContext();
// it is conceivable there is a proxy, so check that first
propertyValues[position] = persistenceContext.getProxy( entityKey );
if ( propertyValues[position] == null ) {
// otherwise look for an initialized version
propertyValues[position] = persistenceContext.getEntity( entityKey );
if ( propertyValues[position] == null ) {
// get the association out of the entity itself
propertyValues[position] = entityDescriptor.getPropertyValue(
entity,
toOneAttributeMapping.getAttributeName()
);
}
}
}
else {
propertyValues[position] = o;
}
}
);
getEmbeddableTypeDescriptor().setValues( entity, propertyValues );
}
@Override
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
assert domainValue instanceof Object[];
identifierValueMapper.breakDownJdbcValues( domainValue, valueConsumer, session );
}
@Override
public void applySqlSelections(
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
identifierValueMapper.applySqlSelections( navigablePath, tableGroup, creationState );
}
@Override
public void applySqlSelections(
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState,
BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
identifierValueMapper.applySqlSelections( navigablePath, tableGroup, creationState, selectionConsumer );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// EmbeddableValuedFetchable
@Override
public String getSqlAliasStem() {
return "id";
}
@Override
public String getFetchableName() {
return EntityIdentifierMapping.ROLE_LOCAL_NAME;
}
@Override
public int getNumberOfFetchables() {
return identifierValueMapper.getNumberOfFetchables();
}
}

View File

@ -66,6 +66,7 @@ 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;
@ -1532,14 +1533,18 @@ public class MappingModelCreationHelper {
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) {
final EmbeddableMappingType embeddableTypeDescriptor;
if ( modelPart instanceof CompositeIdentifierMapping ) {
embeddableTypeDescriptor = ( (CompositeIdentifierMapping) modelPart ).getMappedIdEmbeddableTypeDescriptor();
final EmbeddableMappingType embeddableTypeDescriptor = modelPart.getEmbeddableTypeDescriptor();
if ( modelPart instanceof NonAggregatedIdentifierMapping ) {
return new InverseNonAggregatedIdentifierMapping(
keyDeclaringType,
declaringTableGroupProducer,
selectableMappings,
(NonAggregatedIdentifierMapping) modelPart,
embeddableTypeDescriptor,
creationProcess
);
}
else {
embeddableTypeDescriptor = modelPart.getEmbeddableTypeDescriptor();
}
if ( modelPart instanceof VirtualModelPart ) {
else if ( modelPart instanceof VirtualModelPart ) {
return new VirtualEmbeddedAttributeMapping(
keyDeclaringType,
declaringTableGroupProducer,

View File

@ -371,7 +371,13 @@ public class ToOneAttributeMapping
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
final PersistentClass entityBinding = bootValue.getBuildingContext().getMetadataCollector()
.getEntityBinding( entityMappingType.getEntityName() );
final Type propertyType = entityBinding.getIdentifier().getType();
final Type propertyType;
if ( entityBinding.getIdentifierMapper() == null ) {
propertyType = entityBinding.getIdentifier().getType();
}
else {
propertyType = entityBinding.getIdentifierMapper().getType();
}
if ( entityBinding.getIdentifierProperty() == null ) {
final CompositeType compositeType;
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()

View File

@ -89,6 +89,32 @@ public class VirtualIdEmbeddable extends AbstractEmbeddableMapping implements Id
);
}
public VirtualIdEmbeddable(
EmbeddedAttributeMapping valueMapping,
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings,
VirtualIdEmbeddable inverseMappingType,
MappingModelCreationProcess creationProcess) {
super( creationProcess );
this.navigableRole = inverseMappingType.getNavigableRole();
this.idMapping = (NonAggregatedIdentifierMapping) valueMapping;
this.representationStrategy = inverseMappingType.representationStrategy;
this.attributeMappings = arrayList( inverseMappingType.attributeMappings.size() );
this.selectableMappings = selectableMappings;
creationProcess.registerInitializationCallback(
"VirtualIdEmbeddable(" + inverseMappingType.getNavigableRole().getFullPath() + ".{inverse})#finishInitialization",
() -> inverseInitializeCallback(
declaringTableGroupProducer,
selectableMappings,
inverseMappingType,
creationProcess,
valueMapping.getDeclaringType(),
this.attributeMappings
)
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// IdentifierValueMapper
@ -319,7 +345,7 @@ public class VirtualIdEmbeddable extends AbstractEmbeddableMapping implements Id
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) {
return new EmbeddableMappingTypeImpl(
return new VirtualIdEmbeddable(
valueMapping,
declaringTableGroupProducer,
selectableMappings,

View File

@ -0,0 +1,76 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.annotations.cid.keymanytoone;
import java.io.Serializable;
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 jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.ManyToOne;
/**
* @author Christian Beikov
*/
@DomainModel(
annotatedClasses = {
NestedKeyManyToOneTest.BasicEntity.class, NestedKeyManyToOneTest.IdClassEntity.class, NestedKeyManyToOneTest.NestedIdClassEntity.class
})
@SessionFactory
public class NestedKeyManyToOneTest {
@Test
public void testNestedIdClassAssociations(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "SELECT idClassEntity_1.basicEntity.key1 FROM NestedIdClassEntity a JOIN a.idClassEntity idClassEntity_1" ).getResultList();
}
);
}
@Entity(name = "BasicEntity")
public static class BasicEntity {
@Id
Long key1;
}
@Entity(name = "IdClassEntity")
@IdClass(IdClassEntity.IdClassEntityId.class)
public static class IdClassEntity {
@Id
@ManyToOne
BasicEntity basicEntity;
@Id
Long key2;
public static class IdClassEntityId implements Serializable {
Long basicEntity;
Long key2;
}
}
@Entity(name = "NestedIdClassEntity")
@IdClass(NestedIdClassEntity.NestedIdClassEntityId.class)
public static class NestedIdClassEntity {
@Id
@ManyToOne
IdClassEntity idClassEntity;
@Id
Long key3;
public static class NestedIdClassEntityId implements Serializable {
IdClassEntity.IdClassEntityId idClassEntity;
Long key3;
}
}
}