diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java index fb7179be75..35faacdd16 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java @@ -137,6 +137,8 @@ public interface EntityMappingType extends ManagedMappingType, Loadable { EntityVersionMapping getVersionMapping(); + EntityRowIdMapping getRowIdMapping(); + default EntityDiscriminatorMapping getDiscriminatorMapping() { throw new NotYetImplementedFor6Exception( getClass() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityRowIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityRowIdMapping.java new file mode 100644 index 0000000000..c28b4b5558 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityRowIdMapping.java @@ -0,0 +1,11 @@ +/* + * 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; + +public interface EntityRowIdMapping extends VirtualModelPart { + String getRowIdName(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityRowIdMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityRowIdMappingImpl.java new file mode 100644 index 0000000000..34f5706416 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityRowIdMappingImpl.java @@ -0,0 +1,119 @@ +/* + * 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.function.BiConsumer; + +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityRowIdMapping; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.MappingType; +import org.hibernate.metamodel.model.domain.NavigableRole; +import org.hibernate.query.NavigablePath; +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.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.sql.results.graph.basic.BasicResult; +import org.hibernate.type.JavaObjectType; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * @author Nathan Xu + */ +public class EntityRowIdMappingImpl implements EntityRowIdMapping { + private final String rowIdName; + private final EntityMappingType declaringType; + private final String tableExpression; + + public EntityRowIdMappingImpl(String rowIdName, String tableExpression, EntityMappingType declaringType) { + this.rowIdName = rowIdName; + this.tableExpression = tableExpression; + this.declaringType = declaringType; + } + + @Override + public String getRowIdName() { + return rowIdName; + } + + @Override + public MappingType getPartMappingType() { + return this::getJavaTypeDescriptor; + } + + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return JavaObjectType.INSTANCE.getJavaTypeDescriptor(); + } + + @Override + public String getPartName() { + return rowIdName; + } + + @Override + public NavigableRole getNavigableRole() { + return null; + } + + @Override + public EntityMappingType findContainingEntityMapping() { + return declaringType; + } + + @Override + public DomainResult createDomainResult( + NavigablePath navigablePath, + TableGroup tableGroup, + String resultVariable, + DomainResultCreationState creationState) { + final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); + + final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); + final TableReference columnTableReference = tableGroup.resolveTableReference( tableExpression ); + + final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection( + sqlExpressionResolver.resolveSqlExpression( + SqlExpressionResolver.createColumnReferenceKey( columnTableReference, rowIdName ), + sqlAstProcessingState -> new ColumnReference( + columnTableReference, + rowIdName, + JavaObjectType.INSTANCE, + sqlAstCreationState.getCreationContext().getSessionFactory() + ) + ), + JavaObjectType.INSTANCE.getJdbcMapping().getJavaTypeDescriptor(), + sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration() + ); + + return new BasicResult( + sqlSelection.getValuesArrayPosition(), + resultVariable, + getJavaTypeDescriptor(), + navigablePath + ); + } + + @Override + public void applySqlSelections( + NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { + } + + @Override + public void applySqlSelections( + NavigablePath navigablePath, + TableGroup tableGroup, + DomainResultCreationState creationState, + BiConsumer selectionConsumer) { + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index cf780a3500..43a759f849 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -137,6 +137,7 @@ import org.hibernate.metamodel.mapping.AttributeMetadataAccess; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.ModelPart; @@ -146,6 +147,7 @@ import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMapping; import org.hibernate.metamodel.mapping.internal.BasicEntityIdentifierMappingImpl; import org.hibernate.metamodel.mapping.internal.EntityDiscriminatorMappingImpl; +import org.hibernate.metamodel.mapping.internal.EntityRowIdMappingImpl; import org.hibernate.metamodel.mapping.internal.EntityVersionMappingImpl; import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; @@ -198,7 +200,6 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.FetchableContainer; -import org.hibernate.sql.results.graph.entity.internal.RootEntityResultImpl; import org.hibernate.sql.results.graph.entity.internal.EntityResultImpl; import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.tuple.GenerationTiming; @@ -1228,9 +1229,6 @@ public abstract class AbstractEntityPersister String resultVariable, DomainResultCreationState creationState) { //noinspection unchecked - if ( navigablePath.getParent() == null && !creationState.forceIdentifierSelection()) { - return new RootEntityResultImpl( navigablePath, this, resultVariable, creationState ); - } return new EntityResultImpl( navigablePath, this, resultVariable, creationState ); } @@ -5777,6 +5775,7 @@ public abstract class AbstractEntityPersister private EntityIdentifierMapping identifierMapping; private NaturalIdMapping naturalIdMapping; private EntityVersionMapping versionMapping; + private EntityRowIdMapping rowIdMapping; private EntityDiscriminatorMapping discriminatorMapping; private Map declaredAttributeMappings = new LinkedHashMap<>(); @@ -5807,6 +5806,7 @@ public abstract class AbstractEntityPersister this.identifierMapping = superMappingType.getIdentifierMapping(); this.versionMapping = superMappingType.getVersionMapping(); + this.rowIdMapping = superMappingType.getRowIdMapping(); this.naturalIdMapping = superMappingType.getNaturalIdMapping(); } else { @@ -5833,14 +5833,22 @@ public abstract class AbstractEntityPersister ); } + if ( rowIdName == null ) { + rowIdMapping = null; + } + else { + rowIdMapping = creationProcess.processSubPart( + rowIdName, + (role, creationProcess1) -> new EntityRowIdMappingImpl( rowIdName, this.getRootTableName(), this) + ); + } + buildDiscriminatorMapping(); // todo (6.0) : support for natural-id not yet implemented naturalIdMapping = null; } - - final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel(); int stateArrayPosition = getStateArrayInitialPosition( creationProcess ); @@ -6219,6 +6227,11 @@ public abstract class AbstractEntityPersister return versionMapping; } + @Override + public EntityRowIdMapping getRowIdMapping() { + return rowIdMapping; + } + @Override public EntityDiscriminatorMapping getDiscriminatorMapping() { return discriminatorMapping; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java index 94d0489268..392e236388 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java @@ -54,6 +54,7 @@ import static org.hibernate.internal.log.LoggingHelper.toLoggableString; /** * @author Steve Ebersole + * @author Nathan Xu */ public abstract class AbstractEntityInitializer extends AbstractFetchParentAccess implements EntityInitializer { @@ -74,6 +75,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces private final DomainResultAssembler identifierAssembler; private final DomainResultAssembler discriminatorAssembler; private final DomainResultAssembler versionAssembler; + private final DomainResultAssembler rowIdAssembler; private final Map assemblerMap; @@ -94,6 +96,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces DomainResult identifierResult, DomainResult discriminatorResult, DomainResult versionResult, + DomainResult rowIdResult, AssemblerCreationState creationState) { super( ); @@ -164,6 +167,15 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces this.versionAssembler = null; } + if ( rowIdResult != null ) { + this.rowIdAssembler = rowIdResult.createResultAssembler( + creationState + ); + } + else { + this.rowIdAssembler = null; + } + assemblerMap = new IdentityHashMap<>( entityDescriptor.getNumberOfAttributeMappings() ); entityDescriptor.visitFetchables( @@ -506,23 +518,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces return; } } - final Object rowId = null; -// todo (6.0) : rowId -// final Object rowId; -// if ( concreteDescriptor.getHierarchy().getRowIdDescriptor() != null ) { -// rowId = ro sqlSelectionMappings.getRowIdSqlSelection().hydrateStateArray( rowProcessingState ); -// -// if ( rowId == null ) { -// throw new HibernateException( -// "Could not read entity row-id from JDBC : " + entityKey -// ); -// } -// } -// else { -// rowId = null; -// } - - entityDescriptor.setIdentifier( entityInstance, entityIdentifier, session ); @@ -547,6 +542,15 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces version = null; } + final Object rowId; + + if ( rowIdAssembler != null ) { + rowId = rowIdAssembler.assemble( rowProcessingState ); + } + else { + rowId = null; + } + final EntityEntry entityEntry = persistenceContext.addEntry( entityInstance, Status.LOADING, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java index 377523ed9c..3a44ef8216 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java @@ -6,12 +6,19 @@ */ package org.hibernate.sql.results.graph.entity; +import java.util.ArrayList; + import org.hibernate.LockMode; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityVersionMapping; +import org.hibernate.metamodel.mapping.ManagedMappingType; +import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; +import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.results.graph.AbstractFetchParent; @@ -29,6 +36,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent private final DomainResult identifierResult; private final DomainResult discriminatorResult; private final DomainResult versionResult; + private final DomainResult rowIdResult; private final LockMode lockMode; private final EntityMappingType targetType; @@ -57,12 +65,30 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup( navigablePath ); - identifierResult = entityDescriptor.getIdentifierMapping().createDomainResult( - navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), - entityTableGroup, - null, - creationState - ); + final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); + + if ( navigablePath.getParent() == null && !creationState.forceIdentifierSelection() ) { + identifierResult = null; + if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) { + identifierMapping.createDomainResult( + navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), + entityTableGroup, + null, + creationState + ); + } + else { + visitCompositeIdentifierMapping( navigablePath, creationState, identifierMapping, entityTableGroup ); + } + } + else { + identifierResult = entityDescriptor.getIdentifierMapping().createDomainResult( + navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), + entityTableGroup, + null, + creationState + ); + } final EntityDiscriminatorMapping discriminatorMapping = getDiscriminatorMapping( entityDescriptor, entityTableGroup ); if ( discriminatorMapping != null ) { @@ -89,6 +115,48 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent creationState ); } + + final EntityRowIdMapping rowIdMapping = entityDescriptor.getRowIdMapping(); + if ( rowIdMapping == null ) { + rowIdResult = null; + } + else { + rowIdResult = rowIdMapping.createDomainResult( + navigablePath.append( rowIdMapping.getRowIdName() ), + entityTableGroup, + AbstractEntityPersister.ROWID_ALIAS, + creationState + ); + } + } + + private void visitCompositeIdentifierMapping( + NavigablePath navigablePath, + DomainResultCreationState creationState, + EntityIdentifierMapping identifierMapping, + TableGroup entityTableGroup) { + ManagedMappingType mappingType = (ManagedMappingType) identifierMapping.getPartMappingType(); + fetches = new ArrayList<>(); + mappingType.visitAttributeMappings( + attributeMapping -> { + if ( attributeMapping instanceof ToOneAttributeMapping ) { + ( (ToOneAttributeMapping) attributeMapping ).getForeignKeyDescriptor().createDomainResult( + navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), + entityTableGroup, + null, + creationState + ); + } + else { + attributeMapping.createDomainResult( + navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), + entityTableGroup, + null, + creationState + ); + } + } + ); } protected EntityDiscriminatorMapping getDiscriminatorMapping( @@ -127,4 +195,8 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent public DomainResult getVersionResult() { return versionResult; } + + public DomainResult getRowIdResult() { + return rowIdResult; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java index b1bd9f2248..e2509056dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java @@ -35,6 +35,7 @@ public class EntityJoinedFetchInitializer extends AbstractEntityInitializer { identifierResult, discriminatorResult, versionResult, + null, creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java index e0e62c1bd4..c0d472077d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java @@ -77,6 +77,7 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E getIdentifierResult(), getDiscriminatorResult(), getVersionResult(), + getRowIdResult(), creationState ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java index 57ea7eee39..6a641e2701 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java @@ -29,6 +29,7 @@ public class EntityResultInitializer extends AbstractEntityInitializer { DomainResult identifierResult, DomainResult discriminatorResult, DomainResult versionResult, + DomainResult rowIdResult, AssemblerCreationState creationState) { super( resultDescriptor, @@ -37,6 +38,7 @@ public class EntityResultInitializer extends AbstractEntityInitializer { identifierResult, discriminatorResult, versionResult, + rowIdResult, creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java index 266e643158..16fa61d17f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java @@ -40,6 +40,7 @@ public class EntityResultJoinedSubclassImpl extends EntityResultImpl { getIdentifierResult(), getDiscriminatorResult(), getVersionResult(), + getRowIdResult(), creationState ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/RootEntityResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/RootEntityResultImpl.java deleted file mode 100644 index a8d719062f..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/RootEntityResultImpl.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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.sql.results.graph.entity.internal; - -import java.util.ArrayList; - -import org.hibernate.LockMode; -import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; -import org.hibernate.metamodel.mapping.EntityIdentifierMapping; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.EntityValuedModelPart; -import org.hibernate.metamodel.mapping.EntityVersionMapping; -import org.hibernate.metamodel.mapping.ManagedMappingType; -import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; -import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; -import org.hibernate.query.NavigablePath; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.results.graph.AbstractFetchParent; -import org.hibernate.sql.results.graph.AssemblerCreationState; -import org.hibernate.sql.results.graph.DomainResult; -import org.hibernate.sql.results.graph.DomainResultAssembler; -import org.hibernate.sql.results.graph.DomainResultCreationState; -import org.hibernate.sql.results.graph.FetchableContainer; -import org.hibernate.sql.results.graph.entity.EntityInitializer; -import org.hibernate.sql.results.graph.entity.EntityResult; -import org.hibernate.sql.results.graph.entity.EntityResultGraphNode; -import org.hibernate.type.descriptor.java.JavaTypeDescriptor; - -/** - * @author Andrea Boriero - */ -public class RootEntityResultImpl extends AbstractFetchParent implements EntityResultGraphNode, EntityResult { - - private final String resultVariable; - - private final EntityValuedModelPart referencedModelPart; - private final DomainResult discriminatorResult; - private final DomainResult versionResult; - private final LockMode lockMode; - - public RootEntityResultImpl( - NavigablePath navigablePath, - EntityValuedModelPart entityValuedModelPart, - String resultVariable, - DomainResultCreationState creationState) { - this( navigablePath, entityValuedModelPart, resultVariable, null, creationState ); - } - - @SuppressWarnings("WeakerAccess") - public RootEntityResultImpl( - NavigablePath navigablePath, - EntityValuedModelPart entityValuedModelPart, - String resultVariable, - EntityMappingType targetType, - DomainResultCreationState creationState) { - super( entityValuedModelPart.getEntityMappingType(), navigablePath ); - this.resultVariable = resultVariable; - this.referencedModelPart = entityValuedModelPart; - this.lockMode = creationState.getSqlAstCreationState().determineLockMode( resultVariable ); - - final EntityMappingType entityDescriptor = referencedModelPart.getEntityMappingType(); - - final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup( navigablePath ); - - EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) { - identifierMapping.createDomainResult( - navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), - entityTableGroup, - null, - creationState - ); - } - else { - visitCompositeIdentifierMapping( navigablePath, creationState, identifierMapping, entityTableGroup ); - } - - final EntityDiscriminatorMapping discriminatorMapping = getDiscriminatorMapping( entityDescriptor, entityTableGroup ); - if ( discriminatorMapping != null ) { - discriminatorResult = discriminatorMapping.createDomainResult( - navigablePath.append( EntityDiscriminatorMapping.ROLE_NAME ), - entityTableGroup, - null, - creationState - ); - } - else { - discriminatorResult = null; - } - - final EntityVersionMapping versionDescriptor = entityDescriptor.getVersionMapping(); - if ( versionDescriptor == null ) { - versionResult = null; - } - else { - versionResult = versionDescriptor.createDomainResult( - navigablePath.append( versionDescriptor.getFetchableName() ), - entityTableGroup, - null, - creationState - ); - } - - afterInitialize( creationState ); - } - - private void visitCompositeIdentifierMapping( - NavigablePath navigablePath, - DomainResultCreationState creationState, - EntityIdentifierMapping identifierMapping, - TableGroup entityTableGroup) { - ManagedMappingType mappingType = (ManagedMappingType) identifierMapping.getPartMappingType(); - fetches = new ArrayList<>(); - mappingType.visitAttributeMappings( - attributeMapping -> { - if ( attributeMapping instanceof ToOneAttributeMapping ) { - ((ToOneAttributeMapping)attributeMapping).getForeignKeyDescriptor().createDomainResult( - navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), - entityTableGroup, - null, - creationState - ); - } - else { - attributeMapping.createDomainResult( - navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), - entityTableGroup, - null, - creationState - ); - } - } - ); - } - - protected EntityDiscriminatorMapping getDiscriminatorMapping( - EntityMappingType entityDescriptor, - TableGroup entityTableGroup) { - return entityDescriptor.getDiscriminatorMapping(); - } - - @Override - public EntityMappingType getReferencedMappingContainer() { - return getEntityValuedModelPart().getEntityMappingType(); - } - - @Override - public EntityValuedModelPart getEntityValuedModelPart() { - return referencedModelPart; - } - - @Override - public JavaTypeDescriptor getResultJavaTypeDescriptor() { - return getEntityValuedModelPart().getEntityMappingType().getMappedJavaTypeDescriptor(); - } - - public LockMode getLockMode() { - return lockMode; - } - - public DomainResult getDiscriminatorResult() { - return discriminatorResult; - } - - public DomainResult getVersionResult() { - return versionResult; - } - - @Override - public FetchableContainer getReferencedMappingType() { - return getReferencedMappingContainer(); - } - - @Override - public EntityValuedModelPart getReferencedModePart() { - return getEntityValuedModelPart(); - } - - @Override - public String getResultVariable() { - return resultVariable; - } - - @Override - public DomainResultAssembler createResultAssembler(AssemblerCreationState creationState) { - // todo (6.0) : seems like here is where we ought to determine the SQL selection mappings - - final EntityInitializer initializer = (EntityInitializer) creationState.resolveInitializer( - getNavigablePath(), - () -> new EntityResultInitializer( - this, - getNavigablePath(), - getLockMode(), - null, - getDiscriminatorResult(), - getVersionResult(), - creationState - ) - ); - - return new EntityAssembler( getResultJavaTypeDescriptor(), initializer ); - } - - @Override - public String toString() { - return "EntityResultImpl {" + getNavigablePath() + "}"; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java index 11e63cc949..3ea0282128 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java @@ -45,6 +45,7 @@ import org.hibernate.metadata.ClassMetadata; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; @@ -693,6 +694,11 @@ public class PersisterClassProviderTest { return null; } + @Override + public EntityRowIdMapping getRowIdMapping() { + return null; + } + @Override public NaturalIdMapping getNaturalIdMapping() { return null; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/RowIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/RowIdTest.java new file mode 100644 index 0000000000..68646002b2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/RowIdTest.java @@ -0,0 +1,122 @@ +/* + * 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.rowid; + +import java.util.List; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.RowId; +import org.hibernate.dialect.Oracle9iDialect; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; +import static org.junit.Assert.assertThat; + +/** + * @author Nathan Xu + */ +@DomainModel( annotatedClasses = RowIdTest.Product.class ) +@SessionFactory(statementInspectorClass = SQLStatementInspector.class) +@RequiresDialect( value = Oracle9iDialect.class ) +public class RowIdTest { + + @BeforeEach + void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + Product product = new Product(); + product.setId( 1L ); + product.setName( "Mobile phone" ); + product.setNumber( "123-456-7890" ); + session.persist( product ); + } ); + } + + @Test + void testRowId(SessionFactoryScope scope) { + final String updatedName = "Smart phone"; + scope.inTransaction( session -> { + SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + + Product product = session.find( Product.class, 1L ); + + List sqls = statementInspector.getSqlQueries(); + + assertThat( sqls, hasSize( 1 ) ); + assertThat( sqls.get(0).matches( "(?i).*\\bselect\\b.+\\.ROWID.*\\bfrom\\s+product\\b.*" ), is( true ) ); + + assertThat( product.getName(), not( is( updatedName ) ) ); + + product.setName( updatedName ); + + statementInspector.clear(); + session.flush(); + + sqls = statementInspector.getSqlQueries(); + + assertThat( sqls, hasSize( 1 ) ); + assertThat( sqls.get( 0 ).matches( "(?i).*\\bupdate\\s+product\\b.+?\\bwhere\\s+ROWID\\s*=.*" ), is( true ) ); + } ); + + scope.inTransaction( session -> { + Product product = session.createQuery( "from Product", Product.class ).uniqueResult(); + assertThat( product.getName(), is( updatedName ) ); + } ); + } + + @Entity(name = "Product") + @Table(name = "product") + @RowId("ROWID") + public static class Product { + + @Id + private Long id; + + @Column(name = "`name`") + private String name; + + @Column(name = "`number`") + private String number; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index 745a28952b..8d45d8d5cb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -45,6 +45,7 @@ import org.hibernate.metadata.CollectionMetadata; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; @@ -631,6 +632,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { return null; } + @Override + public EntityRowIdMapping getRowIdMapping() { + return null; + } + @Override public NaturalIdMapping getNaturalIdMapping() { return null; diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index f4ebe9b59c..71d3a0c179 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -49,6 +49,7 @@ import org.hibernate.metadata.ClassMetadata; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; @@ -742,6 +743,11 @@ public class CustomPersister implements EntityPersister { return null; } + @Override + public EntityRowIdMapping getRowIdMapping() { + return null; + } + @Override public NaturalIdMapping getNaturalIdMapping() { return null; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInspector.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInspector.java index 0586b1658e..8e4effcc85 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInspector.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInspector.java @@ -7,6 +7,7 @@ package org.hibernate.testing.jdbc; import java.util.LinkedList; +import java.util.List; import org.hibernate.resource.jdbc.spi.StatementInspector; @@ -19,7 +20,7 @@ import static org.junit.Assert.assertTrue; * @author Andrea Boriero */ public class SQLStatementInspector implements StatementInspector { - private final LinkedList sqlQueries = new LinkedList<>(); + private final List sqlQueries = new LinkedList<>(); public SQLStatementInspector() { } @@ -30,7 +31,7 @@ public class SQLStatementInspector implements StatementInspector { return sql; } - public LinkedList getSqlQueries() { + public List getSqlQueries() { return sqlQueries; } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInterceptor.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInterceptor.java index 43b114f014..8c76ec045c 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInterceptor.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInterceptor.java @@ -18,7 +18,9 @@ import static org.junit.Assert.assertTrue; /** * @author Vlad Mihalcea + * @deprecated use {@link SQLStatementInspector} instead */ +@Deprecated public class SQLStatementInterceptor { private final LinkedList sqlQueries = new LinkedList<>();