Add inverse NonAggregatedIdentifierMapping that uses VirtualIdEmbeddable as model part
This commit is contained in:
parent
3da5571867
commit
246f1048a0
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue