HHH-17406 Change EntityPersister mutation api to expose coordinators

This commit is contained in:
Marco Belladelli 2023-12-21 11:01:49 +01:00 committed by Christian Beikov
parent d72856fef0
commit 599be89308
22 changed files with 750 additions and 658 deletions

View File

@ -128,7 +128,7 @@ public class EntityDeleteAction extends EntityAction {
final Object ck = lockCacheItem();
if ( !isCascadeDeleteEnabled && !veto ) {
persister.delete( id, version, instance, session );
persister.getDeleteCoordinator().delete( instance, id, version, session );
}
if ( isInstanceLoaded() ) {

View File

@ -107,7 +107,12 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
if ( !veto ) {
final EntityPersister persister = getPersister();
final Object instance = getInstance();
final GeneratedValues generatedValues = persister.insertReturning( id, getState(), instance, session );
final GeneratedValues generatedValues = persister.getInsertCoordinator().insert(
instance,
id,
getState(),
session
);
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityEntry entry = persistenceContext.getEntry( instance );
if ( entry == null ) {

View File

@ -166,15 +166,15 @@ public class EntityUpdateAction extends EntityAction {
final Object instance = getInstance();
final Object previousVersion = getPreviousVersion();
final Object ck = lockCacheItem( previousVersion );
final GeneratedValues generatedValues = persister.updateReturning(
final GeneratedValues generatedValues = persister.getUpdateCoordinator().update(
instance,
id,
rowId,
state,
previousVersion,
previousState,
dirtyFields,
hasDirtyCollection,
previousState,
previousVersion,
instance,
rowId,
session
);
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( instance );

View File

@ -77,6 +77,7 @@ public class UniqueKeySelectingDelegate extends AbstractSelectingDelegate {
}
}
@Override
protected String getSelectSQL() {
return selectString;
}

View File

@ -50,6 +50,7 @@ import static org.hibernate.engine.internal.Versioning.incrementVersion;
import static org.hibernate.engine.internal.Versioning.seedVersion;
import static org.hibernate.engine.internal.Versioning.setVersion;
import static org.hibernate.generator.EventType.INSERT;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.pretty.MessageHelper.infoString;
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
@ -108,11 +109,11 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
persister.setValues( entity, state );
}
}
persister.insertReturning( id, state, entity, this );
persister.getInsertCoordinator().insert( entity, id, state, this );
}
else {
final GeneratedValues generatedValues = persister.insertReturning( state, entity, this );
id = generatedValues.getGeneratedValue( persister.getIdentifierMapping() );
final GeneratedValues generatedValues = persister.getInsertCoordinator().insert( entity, state, this );
id = castNonNull( generatedValues ).getGeneratedValue( persister.getIdentifierMapping() );
}
persister.setIdentifier( entity, id, this );
return id;
@ -133,7 +134,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
final EntityPersister persister = getEntityPersister( entityName, entity );
final Object id = persister.getIdentifier( entity, this );
final Object version = persister.getVersion( entity );
persister.delete( id, version, entity, this );
persister.getDeleteCoordinator().delete( entity, id, version, this );
}
@ -167,7 +168,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
else {
oldVersion = null;
}
persister.updateReturning( id, state, null, false, null, oldVersion, entity, null, this );
persister.getUpdateCoordinator().update( entity, id, null, state, oldVersion, null, null, false, this );
}
@Override
@ -203,7 +204,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
else {
oldVersion = null;
}
persister.merge( id, state, null, false, null, oldVersion, entity, null, this );
persister.getMergeCoordinator().update( entity, id, null, state, oldVersion, null, null, false, this );
// persister.setIdentifier( entity, id, this );
}

View File

@ -216,6 +216,7 @@ import org.hibernate.persister.entity.mutation.DeleteCoordinatorStandard;
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.persister.entity.mutation.EntityTableMapping;
import org.hibernate.persister.entity.mutation.InsertCoordinator;
import org.hibernate.persister.entity.mutation.InsertCoordinatorStandard;
import org.hibernate.persister.entity.mutation.MergeCoordinator;
import org.hibernate.persister.entity.mutation.UpdateCoordinator;
import org.hibernate.persister.entity.mutation.UpdateCoordinatorNoOp;
@ -1050,22 +1051,22 @@ public abstract class AbstractEntityPersister
return lazyLoadPlanByFetchGroup.get( fetchGroup );
}
@Internal
@Override
public InsertCoordinator getInsertCoordinator() {
return insertCoordinator;
}
@Internal
@Override
public UpdateCoordinator getUpdateCoordinator() {
return updateCoordinator;
}
@Internal
@Override
public DeleteCoordinator getDeleteCoordinator() {
return deleteCoordinator;
}
@Internal
@Override
public UpdateCoordinator getMergeCoordinator() {
return mergeCoordinator;
}
@ -2813,60 +2814,6 @@ public abstract class AbstractEntityPersister
return select.toStatementString();
}
/**
* Update an object
*/
@Override
public GeneratedValues updateReturning(
final Object id,
final Object[] values,
int[] dirtyAttributeIndexes,
final boolean hasDirtyCollection,
final Object[] oldValues,
final Object oldVersion,
final Object object,
final Object rowId,
final SharedSessionContractImplementor session) throws HibernateException {
return updateCoordinator.coordinateUpdate(
object,
id,
rowId,
values,
oldVersion,
oldValues,
dirtyAttributeIndexes,
hasDirtyCollection,
session
);
}
/**
* Merge an object
*/
@Override
public void merge(
final Object id,
final Object[] values,
int[] dirtyAttributeIndexes,
final boolean hasDirtyCollection,
final Object[] oldValues,
final Object oldVersion,
final Object object,
final Object rowId,
final SharedSessionContractImplementor session) throws HibernateException {
mergeCoordinator.coordinateUpdate(
object,
id,
rowId,
values,
oldVersion,
oldValues,
dirtyAttributeIndexes,
hasDirtyCollection,
session
);
}
@Internal
public boolean hasLazyDirtyFields(int[] dirtyFields) {
final boolean[] propertyLaziness = getPropertyLaziness();
@ -2888,16 +2835,6 @@ public abstract class AbstractEntityPersister
return updateDelegate;
}
@Override
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session) {
return insertCoordinator.coordinateInsert( null, fields, object, session );
}
@Override
public GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
return insertCoordinator.coordinateInsert( id, fields, object, session );
}
protected EntityTableMapping[] getTableMappings() {
return tableMappings;
}
@ -3001,7 +2938,7 @@ public abstract class AbstractEntityPersister
*/
@Override
public void delete(Object id, Object version, Object object, SharedSessionContractImplementor session) {
deleteCoordinator.coordinateDelete( object, id, version, session );
deleteCoordinator.delete( object, id, version, session );
}
/**
@ -3023,7 +2960,7 @@ public abstract class AbstractEntityPersister
}
{
final MutationOperationGroup staticInsertGroup = insertCoordinator.getStaticInsertGroup();
final MutationOperationGroup staticInsertGroup = insertCoordinator.getStaticMutationOperationGroup();
if ( staticInsertGroup != null ) {
for ( int i = 0; i < staticInsertGroup.getNumberOfOperations(); i++ ) {
final MutationOperation mutation = staticInsertGroup.getOperation( i );
@ -3035,7 +2972,7 @@ public abstract class AbstractEntityPersister
}
{
final MutationOperationGroup staticUpdateGroup = updateCoordinator.getStaticUpdateGroup();
final MutationOperationGroup staticUpdateGroup = updateCoordinator.getStaticMutationOperationGroup();
if ( staticUpdateGroup != null ) {
for ( int i = 0; i < staticUpdateGroup.getNumberOfOperations(); i++ ) {
final MutationOperation mutation = staticUpdateGroup.getOperation( i );
@ -3047,7 +2984,7 @@ public abstract class AbstractEntityPersister
}
{
final MutationOperationGroup staticDeleteGroup = deleteCoordinator.getStaticDeleteGroup();
final MutationOperationGroup staticDeleteGroup = deleteCoordinator.getStaticMutationOperationGroup();
if ( staticDeleteGroup != null ) {
for ( int i = 0; i < staticDeleteGroup.getNumberOfOperations(); i++ ) {
final MutationOperation mutation = staticDeleteGroup.getOperation( i );
@ -3672,7 +3609,7 @@ public abstract class AbstractEntityPersister
protected abstract boolean isIdentifierTable(String tableExpression);
protected InsertCoordinator buildInsertCoordinator() {
return new InsertCoordinator( this, factory );
return new InsertCoordinatorStandard( this, factory );
}
protected UpdateCoordinator buildUpdateCoordinator() {
@ -6409,7 +6346,7 @@ public abstract class AbstractEntityPersister
@Deprecated(forRemoval = true)
@Remove
public String[] getSQLDeleteStrings() {
return extractSqlStrings( deleteCoordinator.getStaticDeleteGroup() );
return extractSqlStrings( deleteCoordinator.getStaticMutationOperationGroup() );
}
private String[] extractSqlStrings(MutationOperationGroup operationGroup) {
@ -6431,7 +6368,7 @@ public abstract class AbstractEntityPersister
@Deprecated(forRemoval = true)
@Remove
public String[] getSQLUpdateStrings() {
return extractSqlStrings( updateCoordinator.getStaticUpdateGroup() );
return extractSqlStrings( updateCoordinator.getStaticMutationOperationGroup() );
}
/**

View File

@ -47,7 +47,10 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.persister.entity.mutation.DeleteCoordinator;
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.persister.entity.mutation.InsertCoordinator;
import org.hibernate.persister.entity.mutation.UpdateCoordinator;
import org.hibernate.persister.walking.spi.AttributeSource;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
@ -627,34 +630,45 @@ public interface EntityPersister extends EntityMappingType, EntityMutationTarget
/**
* Persist an instance
*
* @see #getInsertCoordinator()
* @deprecated Use {@link InsertCoordinator#insert(Object, Object, Object[], SharedSessionContractImplementor)} instead.
*/
@Deprecated( forRemoval = true, since = "6.5" )
default void insert(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
insertReturning( id, fields, object, session );
getInsertCoordinator().insert( object, id, fields, session );
}
/**
* Persist an instance
*
* @see #getInsertCoordinator()
* @deprecated Use {@link InsertCoordinator#insert(Object, Object[], SharedSessionContractImplementor)} instead.
*/
GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session);
/**
* Persist an instance
*/
@Deprecated( forRemoval = true, since = "6.5" )
default Object insert(Object[] fields, Object object, SharedSessionContractImplementor session) {
final GeneratedValues generatedValues = insertReturning( fields, object, session );
return generatedValues.getGeneratedValue( getIdentifierMapping() );
final GeneratedValues generatedValues = getInsertCoordinator().insert( object, fields, session );
return generatedValues == null ? null : generatedValues.getGeneratedValue( getIdentifierMapping() );
}
/**
* Persist an instance, using a natively generated identifier (optional operation)
*/
GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session);
/**
* Delete a persistent instance
*
* @see #getDeleteCoordinator()
* @deprecated Use {@link DeleteCoordinator#delete} instead.
*/
void delete(Object id, Object version, Object object, SharedSessionContractImplementor session);
@Deprecated( forRemoval = true, since = "6.5" )
default void delete(Object id, Object version, Object object, SharedSessionContractImplementor session) {
getDeleteCoordinator().delete( object, id, version, session );
}
/**
* Update a persistent instance
*
* @see #getUpdateCoordinator()
* @deprecated Use {@link UpdateCoordinator#update} instead.
*/
@Deprecated( forRemoval = true, since = "6.5" )
default void update(
Object id,
Object[] fields,
@ -665,26 +679,26 @@ public interface EntityPersister extends EntityMappingType, EntityMutationTarget
Object object,
Object rowId,
SharedSessionContractImplementor session) {
updateReturning( id, fields, dirtyFields, hasDirtyCollection, oldFields, oldVersion, object, rowId, session );
getUpdateCoordinator().update(
object,
id,
rowId,
fields,
oldVersion,
oldFields,
dirtyFields,
hasDirtyCollection,
session
);
}
/**
* Update a persistent instance
*/
GeneratedValues updateReturning(
Object id,
Object[] fields,
int[] dirtyFields,
boolean hasDirtyCollection,
Object[] oldFields,
Object oldVersion,
Object object,
Object rowId,
SharedSessionContractImplementor session);
/**
* Merge a persistent instance
*
* @see #getMergeCoordinator()
* @deprecated Use {@link UpdateCoordinator#update} instead.
*/
@Deprecated( forRemoval = true, since = "6.5" )
default void merge(
Object id,
Object[] fields,
@ -695,6 +709,46 @@ public interface EntityPersister extends EntityMappingType, EntityMutationTarget
Object object,
Object rowId,
SharedSessionContractImplementor session) {
getMergeCoordinator().update(
object,
id,
rowId,
fields,
oldVersion,
oldFields,
dirtyFields,
hasDirtyCollection,
session
);
}
/**
* Get the insert coordinator instance.
*
* @since 6.5
*/
InsertCoordinator getInsertCoordinator();
/**
* Get the update coordinator instance.
*
* @since 6.5
*/
UpdateCoordinator getUpdateCoordinator();
/**
* Get the delete coordinator instance.
*
* @since 6.5
*/
DeleteCoordinator getDeleteCoordinator();
/**
* Get the merge coordinator instance.
*
* @since 6.5
*/
default UpdateCoordinator getMergeCoordinator() {
throw new UnsupportedOperationException();
}
@ -811,7 +865,7 @@ public interface EntityPersister extends EntityMappingType, EntityMutationTarget
/**
* The batch size for batch loading.
*
*
* @see org.hibernate.engine.spi.LoadQueryInfluencers#effectiveBatchSize(EntityPersister)
*/
default int getBatchSize() {

View File

@ -52,7 +52,7 @@ public abstract class AbstractDeleteCoordinator
}
@Override
public MutationOperationGroup getStaticDeleteGroup() {
public MutationOperationGroup getStaticMutationOperationGroup() {
return staticOperationGroup;
}
@ -68,7 +68,7 @@ public abstract class AbstractDeleteCoordinator
SharedSessionContractImplementor session);
@Override
public void coordinateDelete(
public void delete(
Object entity,
Object id,
Object version,

View File

@ -7,28 +7,16 @@
package org.hibernate.persister.entity.mutation;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.sql.model.MutationOperationGroup;
/**
* Coordinates the deleting of an entity.
*
* @see #coordinateDelete
*
* @author Steve Ebersole
* @see #delete
*/
public interface DeleteCoordinator {
public interface DeleteCoordinator extends MutationCoordinator {
/**
* The operation group used to perform the deletion unless some form
* of dynamic delete is necessary
* Delete a persistent instance.
*/
MutationOperationGroup getStaticDeleteGroup();
/**
* Perform the deletions
*/
void coordinateDelete(
Object entity,
Object id,
Object version,
SharedSessionContractImplementor session);
void delete(Object entity, Object id, Object version, SharedSessionContractImplementor session);
}

View File

@ -1,457 +1,39 @@
/*
* 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.
* 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.persister.entity.mutation;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Internal;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey;
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.Generator;
import org.hibernate.generator.OnExecutionGenerator;
import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.MutationOperationGroup;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.ValuesAnalysis;
import org.hibernate.sql.model.ast.builder.MutationGroupBuilder;
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
import org.hibernate.tuple.entity.EntityMetamodel;
import static org.hibernate.generator.EventType.INSERT;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Coordinates the insertion of an entity.
* Coordinates the inserting of an entity.
*
* @see #coordinateInsert
*
* @author Steve Ebersole
* @author Marco Belladelli
* @see #insert(Object, Object[], SharedSessionContractImplementor)
* @see #insert(Object, Object, Object[], SharedSessionContractImplementor)
*/
@Internal
public class InsertCoordinator extends AbstractMutationCoordinator {
private final MutationOperationGroup staticInsertGroup;
private final BasicBatchKey batchKey;
public InsertCoordinator(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) {
super( entityPersister, factory );
if ( entityPersister.isIdentifierAssignedByInsert() || entityPersister.hasInsertGeneratedProperties() ) {
// disable batching in case of insert generated identifier or properties
batchKey = null;
}
else {
batchKey = new BasicBatchKey(
entityPersister.getEntityName() + "#INSERT",
null
);
}
if ( entityPersister.getEntityMetamodel().isDynamicInsert() ) {
// the entity specified dynamic-insert - skip generating the
// static inserts as we will create them every time
staticInsertGroup = null;
}
else {
staticInsertGroup = generateStaticOperationGroup();
}
}
public MutationOperationGroup getStaticInsertGroup() {
return staticInsertGroup;
}
@Override
protected BatchKey getBatchKey() {
return batchKey;
}
public interface InsertCoordinator extends MutationCoordinator {
/**
* Persist an entity instance with a generated identifier.
*
* @return The {@linkplain GeneratedValues generated values} if any, {@code null} otherwise.
*/
@Nullable GeneratedValues insert(Object entity, Object[] values, SharedSessionContractImplementor session);
/**
* Perform the insert(s).
* Persist an entity instance using the provided identifier.
*
* @param id This is the id as known in memory. For post-insert id generation (IDENTITY, etc)
* this will be null.
* @param values The extracted attribute values
* @param entity The entity instance being persisted
* @param session The originating context
*
* @return The id
* @return The {@linkplain GeneratedValues generated values} if any, {@code null} otherwise.
*/
public GeneratedValues coordinateInsert(
Object id,
Object[] values,
@Nullable GeneratedValues insert(
Object entity,
SharedSessionContractImplementor session) {
// apply any pre-insert in-memory value generation
final boolean needsDynamicInsert = preInsertInMemoryValueGeneration( values, entity, session );
final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel();
final boolean forceIdentifierBinding = entityPersister().getGenerator().generatedOnExecution() && id != null;
if ( entityMetamodel.isDynamicInsert() || needsDynamicInsert || forceIdentifierBinding ) {
return doDynamicInserts( id, values, entity, session, forceIdentifierBinding );
}
else {
return doStaticInserts( id, values, entity, session );
}
}
protected boolean preInsertInMemoryValueGeneration(Object[] values, Object entity, SharedSessionContractImplementor session) {
final AbstractEntityPersister persister = entityPersister();
final EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
boolean foundStateDependentGenerator = false;
if ( entityMetamodel.hasPreInsertGeneratedValues() ) {
final Generator[] generators = entityMetamodel.getGenerators();
for ( int i = 0; i < generators.length; i++ ) {
final Generator generator = generators[i];
if ( generator != null
&& !generator.generatedOnExecution( entity, session )
&& generator.generatesOnInsert() ) {
values[i] = ( (BeforeExecutionGenerator) generator ).generate( session, entity, values[i], INSERT );
persister.setPropertyValue( entity, i, values[i] );
foundStateDependentGenerator = foundStateDependentGenerator || generator.generatedOnExecution();
}
}
}
return foundStateDependentGenerator;
}
protected static class InsertValuesAnalysis implements ValuesAnalysis {
private final List<TableMapping> tablesWithNonNullValues = new ArrayList<>();
public InsertValuesAnalysis(EntityMutationTarget mutationTarget, Object[] values) {
mutationTarget.forEachMutableTable( (tableMapping) -> {
final int[] tableAttributeIndexes = tableMapping.getAttributeIndexes();
for ( int i = 0; i < tableAttributeIndexes.length; i++ ) {
if ( values[tableAttributeIndexes[i]] != null ) {
tablesWithNonNullValues.add( tableMapping );
break;
}
}
} );
}
public boolean hasNonNullBindings(TableMapping tableMapping) {
return tablesWithNonNullValues.contains( tableMapping );
}
}
protected GeneratedValues doStaticInserts(Object id, Object[] values, Object object, SharedSessionContractImplementor session) {
final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values );
final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis );
final MutationExecutor mutationExecutor = executor( session, staticInsertGroup, false );
decomposeForInsert(
mutationExecutor,
id,
values,
staticInsertGroup,
entityPersister().getPropertyInsertability(),
tableInclusionChecker,
session
);
try {
return mutationExecutor.execute(
object,
insertValuesAnalysis,
tableInclusionChecker,
(statementDetails, affectedRowCount, batchPosition) -> {
statementDetails.getExpectation().verifyOutcome(
affectedRowCount,
statementDetails.getStatement(),
batchPosition,
statementDetails.getSqlString()
);
return true;
},
session
);
}
finally {
mutationExecutor.release();
}
}
protected void decomposeForInsert(
MutationExecutor mutationExecutor,
Object id,
Object[] values,
MutationOperationGroup mutationGroup,
boolean[] propertyInclusions,
TableInclusionChecker tableInclusionChecker,
SharedSessionContractImplementor session) {
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings();
for ( int position = 0; position < mutationGroup.getNumberOfOperations(); position++ ) {
final MutationOperation operation = mutationGroup.getOperation( position );
final EntityTableMapping tableDetails = (EntityTableMapping) operation.getTableDetails();
if ( tableInclusionChecker.include( tableDetails ) ) {
final int[] attributeIndexes = tableDetails.getAttributeIndexes();
for ( int i = 0; i < attributeIndexes.length; i++ ) {
final int attributeIndex = attributeIndexes[ i ];
if ( propertyInclusions[attributeIndex] ) {
final AttributeMapping mapping = attributeMappings.get( attributeIndex );
decomposeAttribute( values[attributeIndex], session, jdbcValueBindings, mapping );
}
}
}
}
if ( id == null ) {
assert entityPersister().getInsertDelegate() != null;
}
else {
for ( int position = 0; position < mutationGroup.getNumberOfOperations(); position++ ) {
final MutationOperation jdbcOperation = mutationGroup.getOperation( position );
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcOperation.getTableDetails();
breakDownJdbcValue( id, session, jdbcValueBindings, tableDetails );
}
}
}
protected void breakDownJdbcValue(
Object id,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
EntityTableMapping tableDetails) {
final String tableName = tableDetails.getTableName();
tableDetails.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> {
jdbcValueBindings.bindValue(
jdbcValue,
tableName,
columnMapping.getColumnName(),
ParameterUsage.SET
);
},
session
);
}
protected void decomposeAttribute(
Object value,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
AttributeMapping mapping) {
if ( !(mapping instanceof PluralAttributeMapping) ) {
mapping.decompose(
value,
0,
jdbcValueBindings,
null,
(valueIndex, bindings, noop, jdbcValue, selectableMapping) -> {
if ( selectableMapping.isInsertable() ) {
bindings.bindValue(
jdbcValue,
entityPersister().physicalTableNameForMutation( selectableMapping ),
selectableMapping.getSelectionExpression(),
ParameterUsage.SET
);
}
},
session
);
}
}
protected GeneratedValues doDynamicInserts(
Object id,
Object[] values,
Object object,
SharedSessionContractImplementor session,
boolean forceIdentifierBinding) {
final boolean[] insertability = getPropertiesToInsert( values );
final MutationOperationGroup insertGroup = generateDynamicInsertSqlGroup( insertability, object, session, forceIdentifierBinding );
final MutationExecutor mutationExecutor = executor( session, insertGroup, true );
final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values );
final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis );
decomposeForInsert( mutationExecutor, id, values, insertGroup, insertability, tableInclusionChecker, session );
try {
return mutationExecutor.execute(
object,
insertValuesAnalysis,
tableInclusionChecker,
(statementDetails, affectedRowCount, batchPosition) -> {
statementDetails.getExpectation().verifyOutcome(
affectedRowCount,
statementDetails.getStatement(),
batchPosition,
statementDetails.getSqlString()
);
return true;
},
session
);
}
finally {
mutationExecutor.release();
}
}
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup group, boolean dynamicUpdate) {
return mutationExecutorService
.createExecutor( resolveBatchKeyAccess( dynamicUpdate, session ), group, session );
}
protected static TableInclusionChecker getTableInclusionChecker(InsertValuesAnalysis insertValuesAnalysis) {
return tableMapping -> !tableMapping.isOptional() || insertValuesAnalysis.hasNonNullBindings( tableMapping );
}
/**
* Transform the array of property indexes to an array of booleans,
* true when the property is insertable and non-null
*/
public boolean[] getPropertiesToInsert(Object[] fields) {
boolean[] notNull = new boolean[fields.length];
boolean[] insertable = entityPersister().getPropertyInsertability();
for ( int i = 0; i < fields.length; i++ ) {
notNull[i] = insertable[i] && fields[i] != null;
}
return notNull;
}
protected MutationOperationGroup generateDynamicInsertSqlGroup(
boolean[] insertable,
Object object,
SharedSessionContractImplementor session,
boolean forceIdentifierBinding) {
final MutationGroupBuilder insertGroupBuilder = new MutationGroupBuilder( MutationType.INSERT, entityPersister() );
entityPersister().forEachMutableTable(
(tableMapping) -> insertGroupBuilder.addTableDetailsBuilder( createTableInsertBuilder( tableMapping, forceIdentifierBinding ) )
);
applyTableInsertDetails( insertGroupBuilder, insertable, object, session, forceIdentifierBinding );
return createOperationGroup( null, insertGroupBuilder.buildMutationGroup() );
}
public MutationOperationGroup generateStaticOperationGroup() {
final MutationGroupBuilder insertGroupBuilder = new MutationGroupBuilder( MutationType.INSERT, entityPersister() );
entityPersister().forEachMutableTable(
(tableMapping) -> insertGroupBuilder.addTableDetailsBuilder( createTableInsertBuilder( tableMapping, false ) )
);
applyTableInsertDetails( insertGroupBuilder, entityPersister().getPropertyInsertability(), null, null, false );
return createOperationGroup( null, insertGroupBuilder.buildMutationGroup() );
}
private TableMutationBuilder<?> createTableInsertBuilder(EntityTableMapping tableMapping, boolean forceIdentifierBinding) {
final GeneratedValuesMutationDelegate delegate = entityPersister().getInsertDelegate();
if ( tableMapping.isIdentifierTable() && delegate != null && !forceIdentifierBinding ) {
return delegate.createTableMutationBuilder( tableMapping.getInsertExpectation(), factory() );
}
else {
return new TableInsertBuilderStandard( entityPersister(), tableMapping, factory() );
}
}
private void applyTableInsertDetails(
MutationGroupBuilder insertGroupBuilder,
boolean[] attributeInclusions,
Object object,
SharedSessionContractImplementor session,
boolean forceIdentifierBinding) {
final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings();
insertGroupBuilder.forEachTableMutationBuilder( (builder) -> {
final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping();
assert !tableMapping.isInverse();
// `attributeIndexes` represents the indexes (relative to `attributeMappings`) of
// the attributes mapped to the table
final int[] attributeIndexes = tableMapping.getAttributeIndexes();
for ( int i = 0; i < attributeIndexes.length; i++ ) {
final int attributeIndex = attributeIndexes[ i ];
final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex );
if ( attributeInclusions[attributeIndex] ) {
attributeMapping.forEachInsertable( insertGroupBuilder );
}
else {
final Generator generator = attributeMapping.getGenerator();
if ( isValueGenerated( generator ) ) {
if ( session != null && !generator.generatedOnExecution( object, session ) ) {
attributeInclusions[attributeIndex] = true;
attributeMapping.forEachInsertable( insertGroupBuilder );
}
else if ( isValueGenerationInSql( generator, factory().getJdbcServices().getDialect() ) ) {
handleValueGeneration( attributeMapping, insertGroupBuilder, (OnExecutionGenerator) generator );
}
}
}
}
} );
// add the discriminator
entityPersister().addDiscriminatorToInsertGroup( insertGroupBuilder );
entityPersister().addSoftDeleteToInsertGroup( insertGroupBuilder );
// add the keys
insertGroupBuilder.forEachTableMutationBuilder( (tableMutationBuilder) -> {
final TableInsertBuilder tableInsertBuilder = (TableInsertBuilder) tableMutationBuilder;
final EntityTableMapping tableMapping = (EntityTableMapping) tableInsertBuilder.getMutatingTable().getTableMapping();
if ( tableMapping.isIdentifierTable() && entityPersister().isIdentifierAssignedByInsert() && !forceIdentifierBinding ) {
assert entityPersister().getInsertDelegate() != null;
final OnExecutionGenerator generator = (OnExecutionGenerator) entityPersister().getGenerator();
if ( generator.referenceColumnsInSql( dialect() ) ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityPersister().getIdentifierMapping();
final String[] columnValues = generator.getReferencedColumnValues( dialect );
tableMapping.getKeyMapping().forEachKeyColumn( (i, column) -> tableInsertBuilder.addKeyColumn(
column.getColumnName(),
columnValues[i],
identifierMapping.getJdbcMapping()
) );
}
}
else {
tableMapping.getKeyMapping().forEachKeyColumn( tableInsertBuilder::addKeyColumn );
}
} );
}
private static boolean isValueGenerated(Generator generator) {
return generator != null
&& generator.generatesOnInsert()
&& generator.generatedOnExecution();
}
private static boolean isValueGenerationInSql(Generator generator, Dialect dialect) {
assert isValueGenerated( generator );
return ( (OnExecutionGenerator) generator ).referenceColumnsInSql(dialect);
}
/**
* @deprecated Use {@link #getBatchKey()}
*/
@Deprecated
public BasicBatchKey getInsertBatchKey() {
return batchKey;
}
SharedSessionContractImplementor session);
}

View File

@ -0,0 +1,474 @@
/*
* 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.persister.entity.mutation;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Internal;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey;
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.Generator;
import org.hibernate.generator.OnExecutionGenerator;
import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.MutationOperationGroup;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.ValuesAnalysis;
import org.hibernate.sql.model.ast.builder.MutationGroupBuilder;
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.generator.EventType.INSERT;
/**
* Coordinates the insertion of an entity.
*
* @see #coordinateInsert
*
* @author Steve Ebersole
*/
@Internal
public class InsertCoordinatorStandard extends AbstractMutationCoordinator implements InsertCoordinator {
private final MutationOperationGroup staticInsertGroup;
private final BasicBatchKey batchKey;
public InsertCoordinatorStandard(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) {
super( entityPersister, factory );
if ( entityPersister.isIdentifierAssignedByInsert() || entityPersister.hasInsertGeneratedProperties() ) {
// disable batching in case of insert generated identifier or properties
batchKey = null;
}
else {
batchKey = new BasicBatchKey(
entityPersister.getEntityName() + "#INSERT",
null
);
}
if ( entityPersister.getEntityMetamodel().isDynamicInsert() ) {
// the entity specified dynamic-insert - skip generating the
// static inserts as we will create them every time
staticInsertGroup = null;
}
else {
staticInsertGroup = generateStaticOperationGroup();
}
}
@Override
public MutationOperationGroup getStaticMutationOperationGroup() {
return staticInsertGroup;
}
@Override
protected BatchKey getBatchKey() {
return batchKey;
}
@Override
public @Nullable GeneratedValues insert(Object entity, Object[] values, SharedSessionContractImplementor session) {
return coordinateInsert( null, values, entity, session );
}
@Override
public @Nullable GeneratedValues insert(
Object entity,
Object id,
Object[] values,
SharedSessionContractImplementor session) {
return coordinateInsert( id, values, entity, session );
}
/**
* Perform the insert(s).
*
* @param id This is the id as known in memory. For post-insert id generation (IDENTITY, etc)
* this will be null.
* @param values The extracted attribute values
* @param entity The entity instance being persisted
* @param session The originating context
*
* @return The {@linkplain GeneratedValues generated values} if any, {@code null} otherwise.
*/
public GeneratedValues coordinateInsert(
Object id,
Object[] values,
Object entity,
SharedSessionContractImplementor session) {
// apply any pre-insert in-memory value generation
final boolean needsDynamicInsert = preInsertInMemoryValueGeneration( values, entity, session );
final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel();
final boolean forceIdentifierBinding = entityPersister().getGenerator().generatedOnExecution() && id != null;
if ( entityMetamodel.isDynamicInsert() || needsDynamicInsert || forceIdentifierBinding ) {
return doDynamicInserts( id, values, entity, session, forceIdentifierBinding );
}
else {
return doStaticInserts( id, values, entity, session );
}
}
protected boolean preInsertInMemoryValueGeneration(Object[] values, Object entity, SharedSessionContractImplementor session) {
final AbstractEntityPersister persister = entityPersister();
final EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
boolean foundStateDependentGenerator = false;
if ( entityMetamodel.hasPreInsertGeneratedValues() ) {
final Generator[] generators = entityMetamodel.getGenerators();
for ( int i = 0; i < generators.length; i++ ) {
final Generator generator = generators[i];
if ( generator != null
&& !generator.generatedOnExecution( entity, session )
&& generator.generatesOnInsert() ) {
values[i] = ( (BeforeExecutionGenerator) generator ).generate( session, entity, values[i], INSERT );
persister.setPropertyValue( entity, i, values[i] );
foundStateDependentGenerator = foundStateDependentGenerator || generator.generatedOnExecution();
}
}
}
return foundStateDependentGenerator;
}
protected static class InsertValuesAnalysis implements ValuesAnalysis {
private final List<TableMapping> tablesWithNonNullValues = new ArrayList<>();
public InsertValuesAnalysis(EntityMutationTarget mutationTarget, Object[] values) {
mutationTarget.forEachMutableTable( (tableMapping) -> {
final int[] tableAttributeIndexes = tableMapping.getAttributeIndexes();
for ( int i = 0; i < tableAttributeIndexes.length; i++ ) {
if ( values[tableAttributeIndexes[i]] != null ) {
tablesWithNonNullValues.add( tableMapping );
break;
}
}
} );
}
public boolean hasNonNullBindings(TableMapping tableMapping) {
return tablesWithNonNullValues.contains( tableMapping );
}
}
protected GeneratedValues doStaticInserts(Object id, Object[] values, Object object, SharedSessionContractImplementor session) {
final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values );
final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis );
final MutationExecutor mutationExecutor = executor( session, staticInsertGroup, false );
decomposeForInsert(
mutationExecutor,
id,
values,
staticInsertGroup,
entityPersister().getPropertyInsertability(),
tableInclusionChecker,
session
);
try {
return mutationExecutor.execute(
object,
insertValuesAnalysis,
tableInclusionChecker,
(statementDetails, affectedRowCount, batchPosition) -> {
statementDetails.getExpectation().verifyOutcome(
affectedRowCount,
statementDetails.getStatement(),
batchPosition,
statementDetails.getSqlString()
);
return true;
},
session
);
}
finally {
mutationExecutor.release();
}
}
protected void decomposeForInsert(
MutationExecutor mutationExecutor,
Object id,
Object[] values,
MutationOperationGroup mutationGroup,
boolean[] propertyInclusions,
TableInclusionChecker tableInclusionChecker,
SharedSessionContractImplementor session) {
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings();
for ( int position = 0; position < mutationGroup.getNumberOfOperations(); position++ ) {
final MutationOperation operation = mutationGroup.getOperation( position );
final EntityTableMapping tableDetails = (EntityTableMapping) operation.getTableDetails();
if ( tableInclusionChecker.include( tableDetails ) ) {
final int[] attributeIndexes = tableDetails.getAttributeIndexes();
for ( int i = 0; i < attributeIndexes.length; i++ ) {
final int attributeIndex = attributeIndexes[ i ];
if ( propertyInclusions[attributeIndex] ) {
final AttributeMapping mapping = attributeMappings.get( attributeIndex );
decomposeAttribute( values[attributeIndex], session, jdbcValueBindings, mapping );
}
}
}
}
if ( id == null ) {
assert entityPersister().getInsertDelegate() != null;
}
else {
for ( int position = 0; position < mutationGroup.getNumberOfOperations(); position++ ) {
final MutationOperation jdbcOperation = mutationGroup.getOperation( position );
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcOperation.getTableDetails();
breakDownJdbcValue( id, session, jdbcValueBindings, tableDetails );
}
}
}
protected void breakDownJdbcValue(
Object id,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
EntityTableMapping tableDetails) {
final String tableName = tableDetails.getTableName();
tableDetails.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> {
jdbcValueBindings.bindValue(
jdbcValue,
tableName,
columnMapping.getColumnName(),
ParameterUsage.SET
);
},
session
);
}
protected void decomposeAttribute(
Object value,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
AttributeMapping mapping) {
if ( !(mapping instanceof PluralAttributeMapping) ) {
mapping.decompose(
value,
0,
jdbcValueBindings,
null,
(valueIndex, bindings, noop, jdbcValue, selectableMapping) -> {
if ( selectableMapping.isInsertable() ) {
bindings.bindValue(
jdbcValue,
entityPersister().physicalTableNameForMutation( selectableMapping ),
selectableMapping.getSelectionExpression(),
ParameterUsage.SET
);
}
},
session
);
}
}
protected GeneratedValues doDynamicInserts(
Object id,
Object[] values,
Object object,
SharedSessionContractImplementor session,
boolean forceIdentifierBinding) {
final boolean[] insertability = getPropertiesToInsert( values );
final MutationOperationGroup insertGroup = generateDynamicInsertSqlGroup( insertability, object, session, forceIdentifierBinding );
final MutationExecutor mutationExecutor = executor( session, insertGroup, true );
final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values );
final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis );
decomposeForInsert( mutationExecutor, id, values, insertGroup, insertability, tableInclusionChecker, session );
try {
return mutationExecutor.execute(
object,
insertValuesAnalysis,
tableInclusionChecker,
(statementDetails, affectedRowCount, batchPosition) -> {
statementDetails.getExpectation().verifyOutcome(
affectedRowCount,
statementDetails.getStatement(),
batchPosition,
statementDetails.getSqlString()
);
return true;
},
session
);
}
finally {
mutationExecutor.release();
}
}
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup group, boolean dynamicUpdate) {
return mutationExecutorService
.createExecutor( resolveBatchKeyAccess( dynamicUpdate, session ), group, session );
}
protected static TableInclusionChecker getTableInclusionChecker(InsertValuesAnalysis insertValuesAnalysis) {
return tableMapping -> !tableMapping.isOptional() || insertValuesAnalysis.hasNonNullBindings( tableMapping );
}
/**
* Transform the array of property indexes to an array of booleans,
* true when the property is insertable and non-null
*/
public boolean[] getPropertiesToInsert(Object[] fields) {
boolean[] notNull = new boolean[fields.length];
boolean[] insertable = entityPersister().getPropertyInsertability();
for ( int i = 0; i < fields.length; i++ ) {
notNull[i] = insertable[i] && fields[i] != null;
}
return notNull;
}
protected MutationOperationGroup generateDynamicInsertSqlGroup(
boolean[] insertable,
Object object,
SharedSessionContractImplementor session,
boolean forceIdentifierBinding) {
final MutationGroupBuilder insertGroupBuilder = new MutationGroupBuilder( MutationType.INSERT, entityPersister() );
entityPersister().forEachMutableTable(
(tableMapping) -> insertGroupBuilder.addTableDetailsBuilder( createTableInsertBuilder( tableMapping, forceIdentifierBinding ) )
);
applyTableInsertDetails( insertGroupBuilder, insertable, object, session, forceIdentifierBinding );
return createOperationGroup( null, insertGroupBuilder.buildMutationGroup() );
}
public MutationOperationGroup generateStaticOperationGroup() {
final MutationGroupBuilder insertGroupBuilder = new MutationGroupBuilder( MutationType.INSERT, entityPersister() );
entityPersister().forEachMutableTable(
(tableMapping) -> insertGroupBuilder.addTableDetailsBuilder( createTableInsertBuilder( tableMapping, false ) )
);
applyTableInsertDetails( insertGroupBuilder, entityPersister().getPropertyInsertability(), null, null, false );
return createOperationGroup( null, insertGroupBuilder.buildMutationGroup() );
}
private TableMutationBuilder<?> createTableInsertBuilder(EntityTableMapping tableMapping, boolean forceIdentifierBinding) {
final GeneratedValuesMutationDelegate delegate = entityPersister().getInsertDelegate();
if ( tableMapping.isIdentifierTable() && delegate != null && !forceIdentifierBinding ) {
return delegate.createTableMutationBuilder( tableMapping.getInsertExpectation(), factory() );
}
else {
return new TableInsertBuilderStandard( entityPersister(), tableMapping, factory() );
}
}
private void applyTableInsertDetails(
MutationGroupBuilder insertGroupBuilder,
boolean[] attributeInclusions,
Object object,
SharedSessionContractImplementor session,
boolean forceIdentifierBinding) {
final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings();
insertGroupBuilder.forEachTableMutationBuilder( (builder) -> {
final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping();
assert !tableMapping.isInverse();
// `attributeIndexes` represents the indexes (relative to `attributeMappings`) of
// the attributes mapped to the table
final int[] attributeIndexes = tableMapping.getAttributeIndexes();
for ( int i = 0; i < attributeIndexes.length; i++ ) {
final int attributeIndex = attributeIndexes[ i ];
final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex );
if ( attributeInclusions[attributeIndex] ) {
attributeMapping.forEachInsertable( insertGroupBuilder );
}
else {
final Generator generator = attributeMapping.getGenerator();
if ( isValueGenerated( generator ) ) {
if ( session != null && !generator.generatedOnExecution( object, session ) ) {
attributeInclusions[attributeIndex] = true;
attributeMapping.forEachInsertable( insertGroupBuilder );
}
else if ( isValueGenerationInSql( generator, factory().getJdbcServices().getDialect() ) ) {
handleValueGeneration( attributeMapping, insertGroupBuilder, (OnExecutionGenerator) generator );
}
}
}
}
} );
// add the discriminator
entityPersister().addDiscriminatorToInsertGroup( insertGroupBuilder );
entityPersister().addSoftDeleteToInsertGroup( insertGroupBuilder );
// add the keys
insertGroupBuilder.forEachTableMutationBuilder( (tableMutationBuilder) -> {
final TableInsertBuilder tableInsertBuilder = (TableInsertBuilder) tableMutationBuilder;
final EntityTableMapping tableMapping = (EntityTableMapping) tableInsertBuilder.getMutatingTable().getTableMapping();
if ( tableMapping.isIdentifierTable() && entityPersister().isIdentifierAssignedByInsert() && !forceIdentifierBinding ) {
assert entityPersister().getInsertDelegate() != null;
final OnExecutionGenerator generator = (OnExecutionGenerator) entityPersister().getGenerator();
if ( generator.referenceColumnsInSql( dialect() ) ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityPersister().getIdentifierMapping();
final String[] columnValues = generator.getReferencedColumnValues( dialect );
tableMapping.getKeyMapping().forEachKeyColumn( (i, column) -> tableInsertBuilder.addKeyColumn(
column.getColumnName(),
columnValues[i],
identifierMapping.getJdbcMapping()
) );
}
}
else {
tableMapping.getKeyMapping().forEachKeyColumn( tableInsertBuilder::addKeyColumn );
}
} );
}
private static boolean isValueGenerated(Generator generator) {
return generator != null
&& generator.generatesOnInsert()
&& generator.generatedOnExecution();
}
private static boolean isValueGenerationInSql(Generator generator, Dialect dialect) {
assert isValueGenerated( generator );
return ( (OnExecutionGenerator) generator ).referenceColumnsInSql(dialect);
}
/**
* @deprecated Use {@link #getBatchKey()}
*/
@Deprecated
public BasicBatchKey getInsertBatchKey() {
return batchKey;
}
}

View File

@ -11,7 +11,6 @@ import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.ast.builder.AbstractTableUpdateBuilder;
import org.hibernate.sql.model.ast.builder.TableMergeBuilder;
import org.hibernate.sql.model.ast.builder.TableUpdateBuilder;
/**
* Specialized {@link UpdateCoordinator} for {@code merge into}.

View File

@ -0,0 +1,27 @@
/*
* 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.persister.entity.mutation;
import org.hibernate.sql.model.MutationOperationGroup;
/**
* Coordinates the mutation operations of an entity.
*
* @see InsertCoordinator
* @see DeleteCoordinator
* @see UpdateCoordinator
* @see MergeCoordinator
*
* @author Marco Belladelli
*/
public interface MutationCoordinator {
/**
* The operation group used to perform the mutation unless some form
* of dynamic mutation is necessary.
*/
MutationOperationGroup getStaticMutationOperationGroup();
}

View File

@ -6,23 +6,24 @@
*/
package org.hibernate.persister.entity.mutation;
import org.hibernate.Internal;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.sql.model.MutationOperationGroup;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Coordinates the updating of an entity.
*
* @see #coordinateUpdate
*
* @author Steve Ebersole
* @see #update
*/
@Internal
public interface UpdateCoordinator {
MutationOperationGroup getStaticUpdateGroup();
GeneratedValues coordinateUpdate(
public interface UpdateCoordinator extends MutationCoordinator {
/**
* Update a persistent instance.
*
* @return The {@linkplain GeneratedValues generated values} if any, {@code null} otherwise.
*/
@Nullable GeneratedValues update(
Object entity,
Object id,
Object rowId,
@ -44,7 +45,7 @@ public interface UpdateCoordinator {
Object currentVersion,
Object nextVersion,
boolean batching,
SharedSessionContractImplementor session){
SharedSessionContractImplementor session) {
forceVersionIncrement( id, currentVersion, nextVersion, session );
}
}

View File

@ -24,12 +24,12 @@ public class UpdateCoordinatorNoOp implements UpdateCoordinator {
}
@Override
public MutationOperationGroup getStaticUpdateGroup() {
public MutationOperationGroup getStaticMutationOperationGroup() {
return operationGroup;
}
@Override
public GeneratedValues coordinateUpdate(Object entity, Object id, Object rowId, Object[] values, Object oldVersion, Object[] incomingOldValues, int[] dirtyAttributeIndexes, boolean hasDirtyCollection, SharedSessionContractImplementor session) {
public GeneratedValues update(Object entity, Object id, Object rowId, Object[] values, Object oldVersion, Object[] incomingOldValues, int[] dirtyAttributeIndexes, boolean hasDirtyCollection, SharedSessionContractImplementor session) {
// nothing to do
return null;
}

View File

@ -68,7 +68,7 @@ import static org.hibernate.internal.util.collections.ArrayHelper.trim;
/**
* Coordinates the updating of an entity.
*
* @see #coordinateUpdate
* @see #update
*
* @author Steve Ebersole
*/
@ -125,7 +125,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
}
@Override
public MutationOperationGroup getStaticUpdateGroup() {
public MutationOperationGroup getStaticMutationOperationGroup() {
return staticUpdateGroup;
}
@ -167,7 +167,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
}
@Override
public GeneratedValues coordinateUpdate(
public GeneratedValues update(
Object entity,
Object id,
Object rowId,

View File

@ -408,7 +408,7 @@ public class DefaultCatalogAndSchemaTest {
// because ID generators table/sequence names are prefixed with the owning entity name.
{
final MutationOperationGroup staticSqlInsertGroup = persister.getInsertCoordinator().getStaticInsertGroup();
final MutationOperationGroup staticSqlInsertGroup = persister.getInsertCoordinator().getStaticMutationOperationGroup();
final String[] insertSqls = new String[staticSqlInsertGroup.getNumberOfOperations()];
for ( int tablePosition = 0;
tablePosition < staticSqlInsertGroup.getNumberOfOperations();
@ -427,7 +427,7 @@ public class DefaultCatalogAndSchemaTest {
}
{
final MutationOperationGroup staticSqlUpdateGroup = persister.getUpdateCoordinator().getStaticUpdateGroup();
final MutationOperationGroup staticSqlUpdateGroup = persister.getUpdateCoordinator().getStaticMutationOperationGroup();
final String[] sqlUpdateStrings = new String[staticSqlUpdateGroup.getNumberOfOperations()];
for ( int tablePosition = 0;
tablePosition < staticSqlUpdateGroup.getNumberOfOperations();
@ -442,7 +442,7 @@ public class DefaultCatalogAndSchemaTest {
{
final MutationOperationGroup staticDeleteGroup = persister.getDeleteCoordinator().getStaticDeleteGroup();
final MutationOperationGroup staticDeleteGroup = persister.getDeleteCoordinator().getStaticMutationOperationGroup();
final String[] sqlDeleteStrings = new String[staticDeleteGroup.getNumberOfOperations()];
for ( int tablePosition = 0; tablePosition < staticDeleteGroup.getNumberOfOperations(); tablePosition++ ) {
final MutationOperation operation = staticDeleteGroup.getOperation( tablePosition );

View File

@ -64,7 +64,10 @@ import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UniqueKeyEntry;
import org.hibernate.persister.entity.mutation.DeleteCoordinator;
import org.hibernate.persister.entity.mutation.EntityTableMapping;
import org.hibernate.persister.entity.mutation.InsertCoordinator;
import org.hibernate.persister.entity.mutation.UpdateCoordinator;
import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
@ -403,30 +406,17 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
}
@Override
public GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
public InsertCoordinator getInsertCoordinator() {
return null;
}
@Override
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session) {
public UpdateCoordinator getUpdateCoordinator() {
return null;
}
@Override
public void delete(Object id, Object version, Object object, SharedSessionContractImplementor session) {
}
@Override
public GeneratedValues updateReturning(
Object id,
Object[] fields,
int[] dirtyFields,
boolean hasDirtyCollection,
Object[] oldFields,
Object oldVersion,
Object object,
Object rowId,
SharedSessionContractImplementor session) {
public DeleteCoordinator getDeleteCoordinator() {
return null;
}

View File

@ -61,7 +61,10 @@ import org.hibernate.orm.test.jpa.SettingsGenerator;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UniqueKeyEntry;
import org.hibernate.persister.entity.mutation.DeleteCoordinator;
import org.hibernate.persister.entity.mutation.EntityTableMapping;
import org.hibernate.persister.entity.mutation.InsertCoordinator;
import org.hibernate.persister.entity.mutation.UpdateCoordinator;
import org.hibernate.persister.internal.PersisterClassResolverInitiator;
import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.persister.spi.PersisterCreationContext;
@ -445,21 +448,17 @@ public class PersisterClassProviderTest {
}
@Override
public GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
public InsertCoordinator getInsertCoordinator() {
return null;
}
@Override
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session) {
public UpdateCoordinator getUpdateCoordinator() {
return null;
}
@Override
public void delete(Object id, Object version, Object object, SharedSessionContractImplementor session) {
}
@Override
public GeneratedValues updateReturning(Object id, Object[] fields, int[] dirtyFields, boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object object, Object rowId, SharedSessionContractImplementor session) {
public DeleteCoordinator getDeleteCoordinator() {
return null;
}

View File

@ -60,13 +60,17 @@ import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UniqueKeyEntry;
import org.hibernate.persister.entity.mutation.DeleteCoordinator;
import org.hibernate.persister.entity.mutation.EntityTableMapping;
import org.hibernate.persister.entity.mutation.InsertCoordinator;
import org.hibernate.persister.entity.mutation.UpdateCoordinator;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.model.MutationOperationGroup;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.tuple.entity.EntityMetamodel;
@ -77,6 +81,8 @@ import org.hibernate.type.descriptor.java.StringJavaType;
import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
import org.hibernate.type.internal.BasicTypeImpl;
import org.checkerframework.checker.nullness.qual.Nullable;
public class CustomPersister implements EntityPersister {
private static final Hashtable<Object,Object> INSTANCES = new Hashtable<>();
@ -503,52 +509,80 @@ public class CustomPersister implements EntityPersister {
throw new UnsupportedOperationException();
}
public GeneratedValues insertReturning(
Object id,
Object[] fields,
Object object,
SharedSessionContractImplementor session
) throws HibernateException {
@Override
public InsertCoordinator getInsertCoordinator() {
return new InsertCoordinator() {
@Override
public @Nullable GeneratedValues insert(
Object entity,
Object[] values,
SharedSessionContractImplementor session) {
throw new UnsupportedOperationException();
}
INSTANCES.put(id, ( (Custom) object ).clone() );
@Override
public @Nullable GeneratedValues insert(
Object entity,
Object id,
Object[] values,
SharedSessionContractImplementor session) {
INSTANCES.put( id, ( (Custom) entity ).clone() );
return null;
}
return null;
@Override
public MutationOperationGroup getStaticMutationOperationGroup() {
return null;
}
};
}
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session)
throws HibernateException {
@Override
public UpdateCoordinator getUpdateCoordinator() {
return new UpdateCoordinator() {
@Override
public @Nullable GeneratedValues update(
Object entity,
Object id,
Object rowId,
Object[] values,
Object oldVersion,
Object[] incomingOldValues,
int[] dirtyAttributeIndexes,
boolean hasDirtyCollection,
SharedSessionContractImplementor session) {
INSTANCES.put( id, ( (Custom) entity ).clone() );
return null;
}
throw new UnsupportedOperationException();
@Override
public void forceVersionIncrement(
Object id,
Object currentVersion,
Object nextVersion,
SharedSessionContractImplementor session) {
}
@Override
public MutationOperationGroup getStaticMutationOperationGroup() {
return null;
}
};
}
public void delete(
Object id,
Object version,
Object object,
SharedSessionContractImplementor session
) throws HibernateException {
@Override
public DeleteCoordinator getDeleteCoordinator() {
return new DeleteCoordinator() {
@Override
public void delete(Object entity, Object id, Object version, SharedSessionContractImplementor session) {
INSTANCES.remove( id );
}
INSTANCES.remove(id);
}
/**
* @see EntityPersister
*/
public GeneratedValues updateReturning(
Object id,
Object[] fields,
int[] dirtyFields,
boolean hasDirtyCollection,
Object[] oldFields,
Object oldVersion,
Object object,
Object rowId,
SharedSessionContractImplementor session
) throws HibernateException {
INSTANCES.put( id, ( (Custom) object ).clone() );
return null;
@Override
public MutationOperationGroup getStaticMutationOperationGroup() {
return null;
}
};
}
private static final BasicType<String> STRING_TYPE = new BasicTypeImpl<>(

View File

@ -50,9 +50,9 @@ public class CustomSqlSchemaResolvingIdentityTest {
String className = CustomEntity.class.getName();
final AbstractEntityPersister persister = (AbstractEntityPersister) scope.getSessionFactory().getMappingMetamodel().getEntityDescriptor(className);
String insertQuery = ( (JdbcMutationOperation) persister.getInsertCoordinator().getStaticInsertGroup().getSingleOperation() ).getSqlString();
String updateQuery = ( (JdbcMutationOperation) persister.getUpdateCoordinator().getStaticUpdateGroup().getSingleOperation() ).getSqlString();
String deleteQuery = ( (JdbcMutationOperation) persister.getDeleteCoordinator().getStaticDeleteGroup().getSingleOperation() ).getSqlString();
String insertQuery = ( (JdbcMutationOperation) persister.getInsertCoordinator().getStaticMutationOperationGroup().getSingleOperation() ).getSqlString();
String updateQuery = ( (JdbcMutationOperation) persister.getUpdateCoordinator().getStaticMutationOperationGroup().getSingleOperation() ).getSqlString();
String deleteQuery = ( (JdbcMutationOperation) persister.getDeleteCoordinator().getStaticMutationOperationGroup().getSingleOperation() ).getSqlString();
assertEquals( "Incorrect custom SQL for insert in Entity: " + className,
"INSERT INTO FOO (name) VALUES (?)", insertQuery );

View File

@ -45,9 +45,9 @@ public class CustomSqlSchemaResolvingTest {
String className = CustomEntity.class.getName();
final AbstractEntityPersister persister = (AbstractEntityPersister) scope.getSessionFactory().getMappingMetamodel().getEntityDescriptor(className);
String insertQuery = ( (JdbcMutationOperation) persister.getInsertCoordinator().getStaticInsertGroup().getSingleOperation() ).getSqlString();
String updateQuery = ( (JdbcMutationOperation) persister.getUpdateCoordinator().getStaticUpdateGroup().getSingleOperation() ).getSqlString();
String deleteQuery = ( (JdbcMutationOperation) persister.getDeleteCoordinator().getStaticDeleteGroup().getSingleOperation() ).getSqlString();
String insertQuery = ( (JdbcMutationOperation) persister.getInsertCoordinator().getStaticMutationOperationGroup().getSingleOperation() ).getSqlString();
String updateQuery = ( (JdbcMutationOperation) persister.getUpdateCoordinator().getStaticMutationOperationGroup().getSingleOperation() ).getSqlString();
String deleteQuery = ( (JdbcMutationOperation) persister.getDeleteCoordinator().getStaticMutationOperationGroup().getSingleOperation() ).getSqlString();
assertEquals( "Incorrect custom SQL for insert in Entity: " + className,
"INSERT INTO FOO (name, id) VALUES (?, ?)", insertQuery );