method renamings and extract method refactorings

- couple of last-minute name changes in Generator stuff for consistency
- make logic in Coordinators easier to understand by extracting lots of
  little methods (there were some very long methods here)
- extract a couple of inner classes that didn't need to be
This commit is contained in:
Gavin 2022-12-22 12:34:02 +01:00 committed by Gavin King
parent a49beafca4
commit c754dfacdf
25 changed files with 1102 additions and 1014 deletions

View File

@ -274,7 +274,7 @@ public class TemporaryTable implements Exportable, Contributable {
.getEntityBinding( entityDescriptor.getEntityName() );
final Generator identifierGenerator = entityDescriptor.getEntityPersister().getGenerator();
final boolean identityColumn = identifierGenerator.generatedOnExecute();
final boolean identityColumn = identifierGenerator.generatedOnExecution();
final boolean hasOptimizer;
if ( identityColumn ) {
hasOptimizer = false;

View File

@ -116,7 +116,7 @@ public abstract class AbstractSaveEventListener<C>
final EntityPersister persister = source.getEntityPersister( entityName, entity );
Generator generator = persister.getGenerator();
if ( !generator.generatedOnExecute() ) {
if ( !generator.generatedOnExecution() ) {
final Object generatedId = ( (BeforeExecutionGenerator) generator ).generate( source, entity, null, INSERT );
if ( generatedId == null ) {
throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() );

View File

@ -47,7 +47,7 @@ public interface BeforeExecutionGenerator extends Generator {
Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType);
@Override
default boolean generatedOnExecute() {
default boolean generatedOnExecution() {
return false;
}
}

View File

@ -74,7 +74,8 @@ import static org.hibernate.generator.EventType.UPDATE;
*/
public interface Generator extends Serializable {
/**
* Determines if the property value is generated in Java code, or by the database.
* Determines if the property value is generated when a row is written to the database,
* or in Java code that executes before the row is written.
* <ul>
* <li>Generators which only implement {@link BeforeExecutionGenerator} must result
* {@code false}.
@ -84,10 +85,11 @@ public interface Generator extends Serializable {
* to return.
* </ul>
*
* @return {@code true} if the value is generated by the database, or false if it is
* generated in Java code.
* @return {@code true} if the value is generated by the database as a side effect of
* the execution of an {@code insert} or {@code update} statement, or false if
* it is generated in Java code before the statement is executed via JDBC.
*/
boolean generatedOnExecute();
boolean generatedOnExecution();
/**
* The {@linkplain EventType event types} for which this generator should be called

View File

@ -142,7 +142,7 @@ public interface OnExecutionGenerator extends Generator {
}
@Override
default boolean generatedOnExecute() {
default boolean generatedOnExecution() {
return true;
}
}

View File

@ -80,7 +80,7 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
}
@Override
public boolean generatedOnExecute() {
public boolean generatedOnExecution() {
return generator == null;
}

View File

@ -101,7 +101,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
final Object id;
final Object[] state = persister.getValues( entity );
final Generator generator = persister.getGenerator();
if ( !generator.generatedOnExecute() ) {
if ( !generator.generatedOnExecution() ) {
id = ( (BeforeExecutionGenerator) generator).generate( this, entity, null, INSERT );
if ( persister.isVersioned() ) {
if ( seedVersion( entity, state, persister, this ) ) {

View File

@ -654,7 +654,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
@Override
public void execute(SharedSessionContractImplementor session, Object incomingObject, Object injectionContext) {
if ( !subgenerator.generatedOnExecute() ) {
if ( !subgenerator.generatedOnExecution() ) {
Object generatedId = ( (BeforeExecutionGenerator) subgenerator).generate( session, incomingObject, null, INSERT );
injector.set( injectionContext, generatedId );
}

View File

@ -91,7 +91,7 @@ public class GeneratedValuesProcessor {
entityDescriptor.forEachAttributeMapping( mapping -> {
final Generator generator = generators[ mapping.getStateArrayPosition() ];
if ( generator != null
&& generator.generatedOnExecute()
&& generator.generatedOnExecution()
&& generator.getEventTypes().contains(timing) ) {
generatedValuesToSelect.add( mapping );
}

View File

@ -628,7 +628,7 @@ public abstract class AbstractCollectionPersister
factory.getJdbcServices().getDialect(),
null
);
if ( generator.generatedOnExecute() ) {
if ( generator.generatedOnExecution() ) {
throw new MappingException("must be an BeforeExecutionGenerator"); //TODO fix message
}
if ( generator instanceof IdentifierGenerator ) {

View File

@ -150,7 +150,6 @@ import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.mapping.Association;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata;
@ -280,6 +279,7 @@ import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirti
import static org.hibernate.engine.internal.Versioning.isVersionIncrementRequired;
import static org.hibernate.generator.EventType.INSERT;
import static org.hibernate.generator.EventType.UPDATE;
import static org.hibernate.metamodel.RepresentationMode.POJO;
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
/**
@ -870,12 +870,9 @@ public abstract class AbstractEntityPersister
}
public String getDiscriminatorColumnReaderTemplate() {
if ( getSubclassEntityNames().size() == 1 ) {
return getDiscriminatorSQLValue();
}
else {
return Template.TEMPLATE + "." + DISCRIMINATOR_ALIAS;
}
return getSubclassEntityNames().size() == 1
? getDiscriminatorSQLValue()
: Template.TEMPLATE + "." + DISCRIMINATOR_ALIAS;
}
public String getDiscriminatorAlias() {
@ -1017,11 +1014,7 @@ public abstract class AbstractEntityPersister
}
private boolean determineCanWriteToCache(PersistentClass persistentClass, EntityDataAccess cacheAccessStrategy) {
if ( cacheAccessStrategy == null ) {
return false;
}
return persistentClass.isCached();
return cacheAccessStrategy != null && persistentClass.isCached();
}
private boolean determineCanReadFromCache(PersistentClass persistentClass, EntityDataAccess cacheAccessStrategy) {
@ -1576,8 +1569,8 @@ public abstract class AbstractEntityPersister
final Serializable cachedValue = disassembledValues[lazyPropertyNumbers[j]];
final Type lazyPropertyType = lazyPropertyTypes[j];
final String propertyName = lazyPropertyNames[j];
if (cachedValue == LazyPropertyInitializer.UNFETCHED_PROPERTY) {
if (fieldName.equals(propertyName)) {
if ( cachedValue == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
if ( fieldName.equals(propertyName) ) {
result = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
// don't try to initialize the unfetched property
@ -1945,7 +1938,7 @@ public abstract class AbstractEntityPersister
throw new AssertionFailure( "cannot force version increment on non-versioned entity" );
}
if ( entityMetamodel.isVersionGeneratedOnExecute() ) {
if ( isVersionGeneratedOnExecution() ) {
// the difficulty here is exactly what we update in order to
// force the version to be incremented in the db...
throw new HibernateException( "LockMode.FORCE is currently not supported for generated version properties" );
@ -2739,7 +2732,7 @@ public abstract class AbstractEntityPersister
private static final boolean[] SINGLE_TRUE = new boolean[] { true };
public final boolean checkVersion(final boolean[] includeProperty) {
return includeProperty[getVersionProperty()] || entityMetamodel.isVersionGeneratedOnExecute();
return includeProperty[getVersionProperty()] || isVersionGeneratedOnExecution();
}
@Override
@ -3947,8 +3940,7 @@ public abstract class AbstractEntityPersister
@Override
public boolean isVersionPropertyGenerated() {
return isVersioned()
&& ( getEntityMetamodel().isVersionGeneratedOnExecute()
|| getEntityMetamodel().isVersionGeneratedBeforeExecute() );
&& ( isVersionGeneratedOnExecution() || isVersionGeneratedBeforeExecution() );
}
@Override
@ -3956,9 +3948,19 @@ public abstract class AbstractEntityPersister
return isVersioned() && getPropertyInsertability()[getVersionProperty()];
}
public boolean isVersionGeneratedOnExecution() {
final Generator strategy = getEntityMetamodel().getGenerators()[ getVersionProperty() ];
return strategy != null && strategy.generatesSometimes() && strategy.generatedOnExecution();
}
public boolean isVersionGeneratedBeforeExecution() {
final Generator strategy = getEntityMetamodel().getGenerators()[ getVersionProperty() ];
return strategy != null && strategy.generatesSometimes() && !strategy.generatedOnExecution();
}
@Override
public void afterInitialize(Object entity, SharedSessionContractImplementor session) {
if ( isPersistentAttributeInterceptable( entity ) && getRepresentationStrategy().getMode() == RepresentationMode.POJO ) {
if ( isPersistentAttributeInterceptable( entity ) && getRepresentationStrategy().getMode() == POJO ) {
final BytecodeLazyAttributeInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata()
.extractLazyInterceptor( entity );
assert interceptor != null;

View File

@ -29,6 +29,8 @@ import org.hibernate.sql.model.internal.MutationOperationGroupSingle;
import org.hibernate.sql.model.internal.MutationOperationGroupStandard;
import org.hibernate.generator.OnExecutionGenerator;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
/**
* Base support for coordinating mutations against an entity
*
@ -58,43 +60,35 @@ public abstract class AbstractMutationCoordinator {
return factory().getJdbcServices().getDialect();
}
protected MutationOperationGroup createOperationGroup(ValuesAnalysis valuesAnalysis, MutationGroup mutationGroup) {
if ( mutationGroup.getNumberOfTableMutations() == 0 ) {
return new MutationOperationGroupNone( mutationGroup.getMutationType(), mutationGroup.getMutationTarget() );
final int numberOfTableMutations = mutationGroup.getNumberOfTableMutations();
switch ( numberOfTableMutations ) {
case 0:
return new MutationOperationGroupNone( mutationGroup );
case 1: {
final MutationOperation operation = mutationGroup.getSingleTableMutation()
.createMutationOperation( valuesAnalysis, factory() );
return operation == null
? new MutationOperationGroupNone( mutationGroup )
: new MutationOperationGroupSingle( mutationGroup, operation );
}
default: {
final List<MutationOperation> operations = arrayList( numberOfTableMutations );
mutationGroup.forEachTableMutation( (integer, tableMutation) -> {
final MutationOperation operation = tableMutation.createMutationOperation( valuesAnalysis, factory );
if ( operation != null ) {
operations.add( operation );
}
else {
ModelMutationLogging.MODEL_MUTATION_LOGGER.debugf(
"Skipping table update - %s",
tableMutation.getTableName()
);
}
} );
return new MutationOperationGroupStandard( mutationGroup.getMutationType(), entityPersister, operations );
}
}
if ( mutationGroup.getNumberOfTableMutations() == 1 ) {
final MutationOperation operation = mutationGroup.getSingleTableMutation().createMutationOperation( valuesAnalysis, factory() );
if ( operation == null ) {
return new MutationOperationGroupNone( mutationGroup.getMutationType(), mutationGroup.getMutationTarget() );
}
return new MutationOperationGroupSingle(
mutationGroup.getMutationType(),
mutationGroup.getMutationTarget(),
operation
);
}
final List<MutationOperation> operations = CollectionHelper.arrayList( mutationGroup.getNumberOfTableMutations() );
mutationGroup.forEachTableMutation( (integer, tableMutation) -> {
final MutationOperation operation = tableMutation.createMutationOperation( valuesAnalysis, factory );
if ( operation != null ) {
operations.add( operation );
}
else {
ModelMutationLogging.MODEL_MUTATION_LOGGER.debugf(
"Skipping table update - %s",
tableMutation.getTableName()
);
}
} );
return new MutationOperationGroupStandard(
mutationGroup.getMutationType(),
entityPersister,
operations
);
}
void handleValueGeneration(

View File

@ -12,12 +12,12 @@ 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.group.PreparedStatementDetails;
import org.hibernate.engine.jdbc.mutation.internal.ModelMutationHelper;
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister;
@ -29,6 +29,8 @@ import org.hibernate.sql.model.ast.builder.TableDeleteBuilder;
import org.hibernate.sql.model.ast.builder.TableDeleteBuilderSkipped;
import org.hibernate.sql.model.ast.builder.TableDeleteBuilderStandard;
import static org.hibernate.engine.jdbc.mutation.internal.ModelMutationHelper.identifiedResultsCheck;
/**
* Coordinates the deleting of an entity.
*
@ -91,16 +93,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
SharedSessionContractImplementor session) {
final MutationOperationGroup operationGroup = generateOperationGroup( loadedState, true, session );
final MutationExecutorService mutationExecutorService = session
.getFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class );
final MutationExecutor mutationExecutor = mutationExecutorService.createExecutor(
() -> batchKey,
operationGroup,
session
);
final MutationExecutor mutationExecutor = executor( session, operationGroup );
operationGroup.forEachOperation( (position, mutation) -> {
if ( mutation != null ) {
@ -118,7 +111,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
entity,
null,
null,
(statementDetails, affectedRowCount, batchPosition) -> ModelMutationHelper.identifiedResultsCheck(
(statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck(
statementDetails,
affectedRowCount,
batchPosition,
@ -134,21 +127,70 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
}
}
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup group) {
return session.getFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class )
.createExecutor( () -> batchKey, group, session );
}
protected void applyLocking(
Object version,
Object[] loadedState,
MutationExecutor mutationExecutor,
SharedSessionContractImplementor session) {
final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle();
if ( optimisticLockStyle == OptimisticLockStyle.NONE ) {
return;
}
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle();
switch ( optimisticLockStyle ) {
case VERSION:
applyVersionLocking( version, session, jdbcValueBindings );
break;
case ALL:
case DIRTY:
applyAllOrDirtyLocking( loadedState, session, jdbcValueBindings );
break;
}
}
if ( version != null
&& optimisticLockStyle.isVersion()
&& entityPersister().getVersionMapping() != null ) {
private void applyAllOrDirtyLocking(
Object[] loadedState,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings) {
if ( loadedState != null ) {
final boolean[] versionability = entityPersister().getPropertyVersionability();
entityPersister().forEachAttributeMapping( (attributeIndex, attribute) -> {
if ( versionability[attributeIndex] && attribute instanceof SingularAttributeMapping ) {
final Object loadedValue = loadedState[attributeIndex];
if (loadedValue != null) {
attribute.breakDownJdbcValues(
loadedValue,
(jdbcValue, jdbcValueMapping) -> {
if ( jdbcValue == null ) {
// presumably the SQL was generated with `is null`
return;
}
jdbcValueBindings.bindValue(
jdbcValue,
entityPersister().getAttributeMutationTableName( attributeIndex ),
jdbcValueMapping.getSelectionExpression(),
ParameterUsage.RESTRICT,
session
);
},
session
);
}
}
} );
}
}
private void applyVersionLocking(
Object version,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings) {
if ( version != null && entityPersister().getVersionMapping() != null ) {
jdbcValueBindings.bindValue(
version,
entityPersister().getIdentifierTableMapping().getTableName(),
@ -156,52 +198,6 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
ParameterUsage.RESTRICT,
session
);
return;
}
if ( loadedState == null ) {
return;
}
if ( optimisticLockStyle.isAllOrDirty() ) {
final boolean[] versionability = entityPersister().getPropertyVersionability();
entityPersister().forEachAttributeMapping( (attributeIndex, attribute) -> {
if ( ! versionability[attributeIndex] ) {
return;
}
if ( !( attribute instanceof SingularAttributeMapping ) ) {
return;
}
final Object loadedValue = loadedState[ attributeIndex ];
if ( loadedValue == null ) {
return;
}
attribute.breakDownJdbcValues(
loadedValue,
(jdbcValue, jdbcValueMapping) -> {
if ( jdbcValue == null ) {
// presumably the SQL was generated with `is null`
return;
}
final String physicalTableName = entityPersister().getAttributeMutationTableName( attributeIndex );
jdbcValueBindings.bindValue(
jdbcValue,
physicalTableName,
jdbcValueMapping.getSelectionExpression(),
ParameterUsage.RESTRICT,
session
);
},
session
);
} );
}
}
@ -216,31 +212,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
operationGroup.forEachOperation( (position, jdbcMutation) -> {
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcMutation.getTableDetails();
if ( rowId != null
&& rowIdMapping != null
&& tableDetails.isIdentifierTable() ) {
jdbcValueBindings.bindValue(
rowId,
tableDetails.getTableName(),
rowIdMapping.getRowIdName(),
ParameterUsage.RESTRICT,
session
);
}
else {
tableDetails.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> jdbcValueBindings.bindValue(
jdbcValue,
tableDetails.getTableName(),
columnMapping.getColumnName(),
ParameterUsage.RESTRICT,
session
),
session
);
}
breakDownIdJdbcValues( id, rowId, session, jdbcValueBindings, rowIdMapping, tableDetails );
final PreparedStatementDetails statementDetails = mutationExecutor.getPreparedStatementDetails( tableDetails.getTableName() );
if ( statementDetails != null ) {
// force creation of the PreparedStatement
@ -250,16 +222,43 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
} );
}
private static void breakDownIdJdbcValues(
Object id,
Object rowId,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
EntityRowIdMapping rowIdMapping,
EntityTableMapping tableDetails) {
if ( rowId != null && rowIdMapping != null && tableDetails.isIdentifierTable() ) {
jdbcValueBindings.bindValue(
rowId,
tableDetails.getTableName(),
rowIdMapping.getRowIdName(),
ParameterUsage.RESTRICT,
session
);
}
else {
tableDetails.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> jdbcValueBindings.bindValue(
jdbcValue,
tableDetails.getTableName(),
columnMapping.getColumnName(),
ParameterUsage.RESTRICT,
session
),
session
);
}
}
protected void doStaticDelete(
Object entity,
Object id,
Object[] loadedState,
Object version,
SharedSessionContractImplementor session) {
final MutationExecutorService mutationExecutorService = session
.getFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class );
final boolean applyVersion;
final MutationOperationGroup operationGroupToUse;
@ -272,16 +271,11 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
operationGroupToUse = staticOperationGroup;
}
final MutationExecutor mutationExecutor = mutationExecutorService.createExecutor(
() -> batchKey,
operationGroupToUse,
session
);
final MutationExecutor mutationExecutor = executor( session, operationGroupToUse );
staticOperationGroup.forEachOperation( (position, mutation) -> {
if ( mutation != null ) {
final String tableName = mutation.getTableDetails().getTableName();
mutationExecutor.getPreparedStatementDetails( tableName );
mutationExecutor.getPreparedStatementDetails( mutation.getTableDetails().getTableName() );
}
} );
@ -298,7 +292,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
entity,
null,
null,
(statementDetails, affectedRowCount, batchPosition) -> ModelMutationHelper.identifiedResultsCheck(
(statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck(
statementDetails,
affectedRowCount,
batchPosition,
@ -327,19 +321,9 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
final MutationGroupBuilder deleteGroupBuilder = new MutationGroupBuilder( MutationType.DELETE, entityPersister() );
entityPersister().forEachMutableTableReverse( (tableMapping) -> {
final TableDeleteBuilder tableDeleteBuilder;
if ( tableMapping.isCascadeDeleteEnabled() ) {
tableDeleteBuilder = new TableDeleteBuilderSkipped( tableMapping );
}
else {
tableDeleteBuilder = new TableDeleteBuilderStandard(
entityPersister(),
tableMapping,
factory()
);
}
final TableDeleteBuilder tableDeleteBuilder = tableMapping.isCascadeDeleteEnabled()
? new TableDeleteBuilderSkipped( tableMapping )
: new TableDeleteBuilderStandard( entityPersister(), tableMapping, factory() );
deleteGroupBuilder.addTableDetailsBuilder( tableDeleteBuilder );
} );
@ -354,16 +338,10 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
boolean applyVersion,
SharedSessionContractImplementor session) {
// first, the table key column(s)
deleteGroupBuilder.forEachTableMutationBuilder( (tableMutationBuilder) -> {
final TableDeleteBuilder tableDeleteBuilder = (TableDeleteBuilder) tableMutationBuilder;
final EntityTableMapping tableMapping = (EntityTableMapping) tableDeleteBuilder.getMutatingTable().getTableMapping();
final EntityTableMapping.KeyMapping keyMapping = tableMapping.getKeyMapping();
keyMapping.forEachKeyColumn( (columnMapping) -> tableDeleteBuilder.addKeyRestriction(
columnMapping.getColumnName(),
columnMapping.getWriteExpression(),
columnMapping.getJdbcMapping()
) );
deleteGroupBuilder.forEachTableMutationBuilder( (builder) -> {
final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping();
final TableDeleteBuilder tableDeleteBuilder = (TableDeleteBuilder) builder;
applyKeyDetails( tableDeleteBuilder, tableMapping );
} );
if ( applyVersion ) {
@ -386,6 +364,16 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
// todo (6.2) : apply where + where-fragments
}
private static void applyKeyDetails(TableDeleteBuilder tableDeleteBuilder, EntityTableMapping tableMapping) {
tableMapping.getKeyMapping().forEachKeyColumn(
(columnMapping) -> tableDeleteBuilder.addKeyRestriction(
columnMapping.getColumnName(),
columnMapping.getWriteExpression(),
columnMapping.getJdbcMapping()
)
);
}
protected void applyOptimisticLocking(
MutationGroupBuilder mutationGroupBuilder,
Object[] loadedState,
@ -424,38 +412,37 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
assert session != null;
final boolean[] versionability = entityPersister().getPropertyVersionability();
entityPersister().forEachAttributeMapping( (attributeIndex, attribute) -> {
if ( ! versionability[attributeIndex] ) {
// the attribute is excluded from optimistic locking
return;
// only makes sense to lock on singular attributes which are not excluded from optimistic locking
if ( versionability[attributeIndex] && attribute instanceof SingularAttributeMapping ) {
breakDownJdbcValues( mutationGroupBuilder, session, attribute, loadedState[attributeIndex] );
}
} );
}
if ( !( attribute instanceof SingularAttributeMapping ) ) {
// only makes sense to lock on singular attributes
return;
}
final Object loadedValue = loadedState[ attributeIndex ];
attribute.breakDownJdbcValues(
loadedValue,
(jdbcValue, columnMapping) -> {
final String physicalTableName = entityPersister().physicalTableNameForMutation( columnMapping );
final RestrictedTableMutationBuilder<?,?> tableMutationBuilder = mutationGroupBuilder.findTableDetailsBuilder( physicalTableName );
if ( tableMutationBuilder == null ) {
// there is no actual delete statement for that table. this
// generally indicates we have an on-delete=cascade situation
return;
}
if ( jdbcValue == null ) {
private void breakDownJdbcValues(
MutationGroupBuilder mutationGroupBuilder,
SharedSessionContractImplementor session,
AttributeMapping attribute,
Object loadedValue) {
attribute.breakDownJdbcValues(
loadedValue,
(jdbcValue, columnMapping) -> {
final String physicalTableName = entityPersister().physicalTableNameForMutation( columnMapping );
final RestrictedTableMutationBuilder<?, ?> tableMutationBuilder =
mutationGroupBuilder.findTableDetailsBuilder( physicalTableName );
if ( tableMutationBuilder != null ) {
if (jdbcValue == null) {
tableMutationBuilder.addNullOptimisticLockRestriction( columnMapping );
}
else {
tableMutationBuilder.addOptimisticLockRestriction( columnMapping );
}
},
session
);
} );
}
// else there is no actual delete statement for that table,
// generally indicates we have an on-delete=cascade situation
},
session
);
}
}

View File

@ -19,7 +19,6 @@ import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
@ -38,6 +37,8 @@ import org.hibernate.generator.OnExecutionGenerator;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.tuple.entity.EntityMetamodel;
import static org.hibernate.generator.EventType.INSERT;
/**
* Coordinates the insertion of an entity.
*
@ -111,9 +112,9 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
for ( int i = 0; i < generators.length; i++ ) {
final Generator generator = generators[i];
if ( generator != null
&& !generator.generatedOnExecute()
&& !generator.generatedOnExecution()
&& generator.generatesOnInsert() ) {
values[i] = ( (BeforeExecutionGenerator) generator ).generate( session, entity, values[i], EventType.INSERT );
values[i] = ( (BeforeExecutionGenerator) generator ).generate( session, entity, values[i], INSERT );
entityPersister().setPropertyValue( entity, i, values[i] );
}
}
@ -145,15 +146,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis );
final MutationExecutorService mutationExecutorService = session.getSessionFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class );
final MutationExecutor mutationExecutor = mutationExecutorService.createExecutor(
() -> insertBatchKey,
staticInsertGroup,
session
);
final MutationExecutor mutationExecutor = executor( session, staticInsertGroup );
decomposeForInsert(
mutationExecutor,
@ -197,85 +190,79 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
SharedSessionContractImplementor session) {
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings();
mutationGroup.forEachOperation( (position, operation) -> {
final EntityTableMapping tableDetails = (EntityTableMapping) operation.getTableDetails();
if ( !tableInclusionChecker.include( tableDetails ) ) {
return;
}
final int[] attributeIndexes = tableDetails.getAttributeIndexes();
for ( int i = 0; i < attributeIndexes.length; i++ ) {
final int attributeIndex = attributeIndexes[ i ];
if ( !propertyInclusions[attributeIndex] ) {
continue;
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 = entityPersister().getAttributeMappings().get( attributeIndex );
decomposeAttribute( values[attributeIndex], session, jdbcValueBindings, mapping );
}
}
final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex );
if ( attributeMapping instanceof PluralAttributeMapping ) {
continue;
}
attributeMapping.decompose(
values[ attributeIndex ],
(jdbcValue, selectableMapping) -> {
if ( !selectableMapping.isInsertable() ) {
return;
}
final String tableName = entityPersister().physicalTableNameForMutation( selectableMapping );
jdbcValueBindings.bindValue(
jdbcValue,
tableName,
selectableMapping.getSelectionExpression(),
ParameterUsage.SET,
session
);
},
session
);
}
} );
mutationGroup.forEachOperation( (position, jdbcOperation) -> {
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcOperation.getTableDetails();
final String tableName = tableDetails.getTableName();
if ( id == null ) {
assert entityPersister().getIdentityInsertDelegate() != null;
return;
}
else {
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcOperation.getTableDetails();
breakDownJdbcValue( id, session, jdbcValueBindings, tableDetails );
}
} );
}
tableDetails.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> jdbcValueBindings.bindValue(
jdbcValue,
tableName,
columnMapping.getColumnName(),
ParameterUsage.SET,
session
),
private static 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
),
session
);
}
private void decomposeAttribute(
Object value,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
AttributeMapping mapping) {
if ( !(mapping instanceof PluralAttributeMapping) ) {
mapping.decompose(
value,
(jdbcValue, selectableMapping) -> {
if ( selectableMapping.isInsertable() ) {
jdbcValueBindings.bindValue(
jdbcValue,
entityPersister().physicalTableNameForMutation( selectableMapping ),
selectableMapping.getSelectionExpression(),
ParameterUsage.SET,
session
);
}
},
session
);
} );
}
}
protected Object doDynamicInserts(Object id, Object[] values, Object object, SharedSessionContractImplementor session) {
final boolean[] insertability = getPropertiesToInsert( values );
final MutationOperationGroup insertGroup = generateDynamicInsertSqlGroup( insertability );
final MutationExecutorService mutationExecutorService = session
.getFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class );
final MutationExecutor mutationExecutor = mutationExecutorService.createExecutor(
() -> insertBatchKey,
insertGroup,
session
);
final MutationExecutor mutationExecutor = executor( session, insertGroup );
final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values );
@ -305,6 +292,13 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
}
}
private MutationExecutor executor(SharedSessionContractImplementor session, MutationOperationGroup insertGroup) {
return session.getFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class )
.createExecutor( () -> insertBatchKey, insertGroup, session );
}
protected static TableInclusionChecker getTableInclusionChecker(InsertValuesAnalysis insertValuesAnalysis) {
return tableMapping -> !tableMapping.isOptional() || insertValuesAnalysis.hasNonNullBindings( tableMapping );
}
@ -325,63 +319,35 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
protected MutationOperationGroup generateDynamicInsertSqlGroup(boolean[] insertable) {
assert entityPersister().getEntityMetamodel().isDynamicInsert();
final MutationGroupBuilder insertGroupBuilder = new MutationGroupBuilder( MutationType.INSERT, entityPersister() );
entityPersister().forEachMutableTable( (tableMapping) -> {
final TableInsertBuilder tableInsertBuilder;
final InsertGeneratedIdentifierDelegate identityDelegate = entityPersister().getIdentityInsertDelegate();
if ( tableMapping.isIdentifierTable() && identityDelegate != null ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityPersister().getIdentifierMapping();
tableInsertBuilder = identityDelegate.createTableInsertBuilder(
identifierMapping,
tableMapping.getInsertExpectation(),
factory()
);
}
else {
tableInsertBuilder = new TableInsertBuilderStandard(
entityPersister(),
tableMapping,
factory()
);
}
insertGroupBuilder.addTableDetailsBuilder( tableInsertBuilder );
} );
entityPersister().forEachMutableTable(
(tableMapping) -> insertGroupBuilder.addTableDetailsBuilder( createTableInsertBuilder( tableMapping ) )
);
applyTableInsertDetails( insertGroupBuilder, insertable );
return createOperationGroup( null, insertGroupBuilder.buildMutationGroup() );
}
public MutationOperationGroup generateStaticOperationGroup() {
final MutationGroupBuilder insertGroupBuilder = new MutationGroupBuilder( MutationType.INSERT, entityPersister() );
entityPersister().forEachMutableTable( (tableMapping) -> {
final TableInsertBuilder tableInsertBuilder;
final InsertGeneratedIdentifierDelegate identityDelegate = entityPersister().getIdentityInsertDelegate();
if ( tableMapping.isIdentifierTable() && identityDelegate != null ) {
tableInsertBuilder = identityDelegate.createTableInsertBuilder(
(BasicEntityIdentifierMapping) entityPersister().getIdentifierMapping(),
tableMapping.getInsertExpectation(),
factory()
);
}
else {
tableInsertBuilder = new TableInsertBuilderStandard(
entityPersister(),
tableMapping,
factory()
);
}
insertGroupBuilder.addTableDetailsBuilder( tableInsertBuilder );
} );
entityPersister().forEachMutableTable(
(tableMapping) -> insertGroupBuilder.addTableDetailsBuilder( createTableInsertBuilder( tableMapping ) )
);
applyTableInsertDetails( insertGroupBuilder, entityPersister().getPropertyInsertability() );
return createOperationGroup( null, insertGroupBuilder.buildMutationGroup() );
}
private TableInsertBuilder createTableInsertBuilder(EntityTableMapping tableMapping) {
final InsertGeneratedIdentifierDelegate identityDelegate = entityPersister().getIdentityInsertDelegate();
if ( tableMapping.isIdentifierTable() && identityDelegate != null ) {
final BasicEntityIdentifierMapping mapping =
(BasicEntityIdentifierMapping) entityPersister().getIdentifierMapping();
return identityDelegate.createTableInsertBuilder( mapping, tableMapping.getInsertExpectation(), factory() );
}
else {
return new TableInsertBuilderStandard( entityPersister(), tableMapping, factory() );
}
}
private void applyTableInsertDetails(
MutationGroupBuilder insertGroupBuilder,
boolean[] attributeInclusions) {
@ -397,16 +363,15 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
for ( int i = 0; i < attributeIndexes.length; i++ ) {
final int attributeIndex = attributeIndexes[ i ];
final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex );
if ( !attributeInclusions[ attributeIndex ] ) {
if ( attributeInclusions[attributeIndex] ) {
attributeMapping.forEachInsertable( insertGroupBuilder );
}
else {
final Generator generator = attributeMapping.getGenerator();
if ( isValueGenerationInSql( generator, factory().getJdbcServices().getDialect()) ) {
if ( isValueGenerationInSql( generator, factory().getJdbcServices().getDialect() ) ) {
handleValueGeneration( attributeMapping, insertGroupBuilder, (OnExecutionGenerator) generator );
}
continue;
}
attributeMapping.forEachInsertable( insertGroupBuilder );
}
} );
@ -431,7 +396,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
private static boolean isValueGenerationInSql(Generator generator, Dialect dialect) {
return generator != null
&& generator.generatesOnInsert()
&& generator.generatedOnExecute()
&& generator.generatedOnExecution()
&& ( (OnExecutionGenerator) generator ).referenceColumnsInSql(dialect);
}
}

View File

@ -272,7 +272,7 @@ public class CteInsertHandler implements InsertHandler {
rowNumberColumn
);
}
if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecute() ) {
if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) {
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
1,
@ -336,7 +336,7 @@ public class CteInsertHandler implements InsertHandler {
processingStateStack.push( oldState );
sqmConverter.pruneTableGroupJoins();
if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecute() ) {
if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) {
// Add the row number to the assignments
final CteColumn rowNumberColumn = cteTable.getCteColumns()
.get( cteTable.getCteColumns().size() - 1 );
@ -580,7 +580,7 @@ public class CteInsertHandler implements InsertHandler {
statement.addCteStatement( entityCte );
}
}
else if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecute() ) {
else if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) {
final String baseTableName = "base_" + entityCteTable.getTableExpression();
final CteStatement baseEntityCte = new CteStatement(
entityCteTable.withName( baseTableName ),
@ -777,7 +777,7 @@ public class CteInsertHandler implements InsertHandler {
final Generator identifierGenerator = entityDescriptor.getEntityPersister().getGenerator();
final List<Map.Entry<List<CteColumn>, Assignment>> tableAssignments = assignmentsByTable.get( rootTableReference );
if ( ( tableAssignments == null || tableAssignments.isEmpty() )
&& !identifierGenerator.generatedOnExecute() ) {
&& !identifierGenerator.generatedOnExecution() ) {
throw new IllegalStateException( "There must be at least a single root table assignment" );
}
@ -805,7 +805,7 @@ public class CteInsertHandler implements InsertHandler {
final QuerySpec insertSelectSpec = new QuerySpec( true );
CteStatement finalCteStatement = null;
final CteTable dmlResultCte;
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecute() ) {
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
// Special handling for identity generation
final String cteTableName = getCteTableName( tableExpression, "base_" );
if ( statement.getCteStatement( cteTableName ) != null ) {
@ -1074,7 +1074,7 @@ public class CteInsertHandler implements InsertHandler {
if ( finalCteStatement != null ) {
statement.addCteStatement( finalCteStatement );
}
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecute() ) {
if ( i == 0 && !assignsId && identifierGenerator.generatedOnExecution() ) {
// Special handling for identity generation
statement.addCteStatement( queryCte );
}

View File

@ -33,6 +33,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.persister.entity.AbstractEntityPersister;
@ -79,6 +80,8 @@ import org.hibernate.generator.OnExecutionGenerator;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.type.descriptor.ValueBinder;
import static org.hibernate.generator.EventType.INSERT;
/**
* @author Christian Beikov
* @author Steve Ebersole
@ -178,7 +181,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
if ( assignmentTableReference != null && assignmentTableReference != tableReference ) {
throw new SemanticException( "Assignment referred to columns from multiple tables: " + i );
}
assignmentTableReference = tableReference;
}
@ -308,7 +310,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
final Generator generator = entityPersister.getGenerator();
final List<Assignment> assignments = assignmentsByTable.get( updatingTableReference );
if ( ( assignments == null || assignments.isEmpty() )
&& !generator.generatedOnExecute()
&& !generator.generatedOnExecution()
&& ( !( generator instanceof BulkInsertionCapableIdentifierGenerator )
|| ( (BulkInsertionCapableIdentifierGenerator) generator ).supportsBulkInsertionIdentifierGeneration() ) ) {
throw new IllegalStateException( "There must be at least a single root table assignment" );
@ -335,8 +337,9 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
insertStatement.setSourceSelectStatement( querySpec );
if ( assignments != null ) {
for ( Assignment assignment : assignments ) {
insertStatement.addTargetColumnReferences( assignment.getAssignable().getColumnReferences() );
for ( ColumnReference columnReference : assignment.getAssignable().getColumnReferences() ) {
final Assignable assignable = assignment.getAssignable();
insertStatement.addTargetColumnReferences( assignable.getColumnReferences() );
for ( ColumnReference columnReference : assignable.getColumnReferences() ) {
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
1,
@ -356,8 +359,10 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
}
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final Map<Object, Object> entityTableToRootIdentity;
if ( generator.generatedOnExecute() ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final SharedSessionContractImplementor session = executionContext.getSession();
if ( generator.generatedOnExecution() ) {
final BasicEntityIdentifierMapping identifierMapping =
(BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final QuerySpec idSelectQuerySpec = new QuerySpec( true );
idSelectQuerySpec.getFromClause().addRoot( temporaryTableGroup );
final ColumnReference columnReference = new ColumnReference(
@ -368,7 +373,8 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
null,
identifierMapping.getJdbcMapping()
);
idSelectQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, columnReference ) );
idSelectQuerySpec.getSelectClause()
.addSqlSelection( new SqlSelectionImpl( 1, 0, columnReference ) );
idSelectQuerySpec.addSortSpecification(
new SortSpecification(
columnReference,
@ -420,21 +426,13 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
if ( needsIdentifierGeneration( generator )
&& insertStatement.getTargetColumns().stream()
.noneMatch( c -> keyColumns[0].equals( c.getColumnExpression() ) ) ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final BasicEntityIdentifierMapping identifierMapping =
(BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final JdbcParameter rowNumber = new JdbcParameterImpl( identifierMapping.getJdbcMapping() );
final JdbcParameter rootIdentity = new JdbcParameterImpl( identifierMapping.getJdbcMapping() );
final List<Assignment> temporaryTableAssignments = new ArrayList<>( 1 );
final ColumnReference idColumnReference = new ColumnReference(
(String) null,
identifierMapping
);
temporaryTableAssignments.add(
new Assignment(
idColumnReference,
rootIdentity
)
);
final ColumnReference idColumnReference = new ColumnReference( (String) null, identifierMapping );
temporaryTableAssignments.add( new Assignment( idColumnReference, rootIdentity ) );
final TemporaryTableColumn rowNumberColumn;
final TemporaryTableColumn sessionUidColumn;
final Predicate sessionUidPredicate;
@ -493,11 +491,12 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
sessionUidParameter,
new JdbcParameterBindingImpl(
sessionUidColumn.getJdbcMapping(),
UUID.fromString( sessionUidAccess.apply( executionContext.getSession() ) )
UUID.fromString( sessionUidAccess.apply(session) )
)
);
}
final BeforeExecutionGenerator beforeExecutionGenerator = (BeforeExecutionGenerator) generator;
for ( int i = 0; i < rows; i++ ) {
updateBindings.addBinding(
rowNumber,
@ -510,18 +509,17 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
rootIdentity,
new JdbcParameterBindingImpl(
identifierMapping.getJdbcMapping(),
( (BeforeExecutionGenerator) generator ).generate( executionContext.getSession(), null, null, EventType.INSERT )
beforeExecutionGenerator.generate( session, null, null, INSERT )
)
);
jdbcServices.getJdbcMutationExecutor().execute(
jdbcUpdate,
updateBindings,
sql -> executionContext.getSession()
sql -> session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {
},
(integer, preparedStatement) -> {},
executionContext
);
}
@ -558,23 +556,23 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
.buildInsertTranslator( sessionFactory, insertStatement )
.translate( null, executionContext.getQueryOptions() );
if ( generator.generatedOnExecute() ) {
if ( generator.generatedOnExecution() ) {
final OnExecutionGenerator databaseGenerator = (OnExecutionGenerator) generator;
final InsertGeneratedIdentifierDelegate identifierDelegate =
databaseGenerator.getGeneratedIdentifierDelegate( (PostInsertIdentityPersister) entityPersister );
final String finalSql = identifierDelegate.prepareIdentifierGeneratingInsert( jdbcInsert.getSqlString() );
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final BasicEntityIdentifierMapping identifierMapping =
(BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final ValueBinder jdbcValueBinder = identifierMapping.getJdbcMapping().getJdbcValueBinder();
for ( Map.Entry<Object, Object> entry : entityTableToRootIdentity.entrySet() ) {
final Object rootIdentity = identifierDelegate.performInsert(
finalSql,
executionContext.getSession(),
session,
new Binder() {
@Override
public void bindValues(PreparedStatement ps) throws SQLException {
jdbcValueBinder.bind( ps, entry.getKey(), 1, executionContext.getSession() );
jdbcValueBinder.bind( ps, entry.getKey(), 1, session );
}
@Override
public Object getEntity() {
return null;
@ -589,10 +587,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
final List<Assignment> temporaryTableAssignments = new ArrayList<>( 1 );
temporaryTableAssignments.add(
new Assignment(
new ColumnReference(
(String) null,
identifierMapping
),
new ColumnReference( (String) null, identifierMapping ),
rootIdentity
)
);
@ -620,12 +615,13 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
final JdbcParameterBindings updateBindings = new JdbcParameterBindingsImpl( 2 );
for ( Map.Entry<Object, Object> entry : entityTableToRootIdentity.entrySet() ) {
updateBindings.addBinding( entityIdentity, new JdbcParameterBindingImpl( identifierMapping.getJdbcMapping(), entry.getKey() ) );
updateBindings.addBinding( rootIdentity, new JdbcParameterBindingImpl( identifierMapping.getJdbcMapping(), entry.getValue() ) );
JdbcMapping jdbcMapping = identifierMapping.getJdbcMapping();
updateBindings.addBinding( entityIdentity, new JdbcParameterBindingImpl( jdbcMapping, entry.getKey() ) );
updateBindings.addBinding( rootIdentity, new JdbcParameterBindingImpl( jdbcMapping, entry.getValue() ) );
jdbcServices.getJdbcMutationExecutor().execute(
jdbcUpdate,
updateBindings,
sql -> executionContext.getSession()
sql -> session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
@ -639,7 +635,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
jdbcServices.getJdbcMutationExecutor().execute(
jdbcInsert,
JdbcParameterBindings.NO_BINDINGS,
sql -> executionContext.getSession()
sql -> session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
@ -651,14 +647,19 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
}
private boolean needsIdentifierGeneration(Generator identifierGenerator) {
if ( !( identifierGenerator instanceof OptimizableGenerator ) ) {
if (identifierGenerator instanceof OptimizableGenerator) {
// If the generator uses an optimizer or is not bulk insertion capable,
// we have to generate identifiers for the new rows, as that couldn't
// have been done via a SQL expression
final Optimizer optimizer = ( (OptimizableGenerator) identifierGenerator ).getOptimizer();
return optimizer != null && optimizer.getIncrementSize() > 1
|| identifierGenerator instanceof BulkInsertionCapableIdentifierGenerator
&& !( (BulkInsertionCapableIdentifierGenerator) identifierGenerator )
.supportsBulkInsertionIdentifierGeneration();
}
else {
return false;
}
// If the generator uses an optimizer or is not bulk insertion capable, we have to generate identifiers for the new rows,
// as that couldn't have been done through a SQL expression
final Optimizer optimizer = ( (OptimizableGenerator) identifierGenerator ).getOptimizer();
return optimizer != null && optimizer.getIncrementSize() > 1 || identifierGenerator instanceof BulkInsertionCapableIdentifierGenerator
&& !( (BulkInsertionCapableIdentifierGenerator) identifierGenerator ).supportsBulkInsertionIdentifierGeneration();
}
private void insertTable(
@ -719,7 +720,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
final Generator identifierGenerator = entityPersister.getGenerator();
final boolean needsKeyInsert;
if ( identifierGenerator.generatedOnExecute() ) {
if ( identifierGenerator.generatedOnExecution() ) {
needsKeyInsert = true;
}
else if ( identifierGenerator instanceof OptimizableGenerator ) {
@ -733,7 +734,8 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
if ( needsKeyInsert && insertStatement.getTargetColumns()
.stream()
.noneMatch( c -> targetKeyColumnName.equals( c.getColumnExpression() ) ) ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final BasicEntityIdentifierMapping identifierMapping =
(BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
insertStatement.addTargetColumnReferences(
new ColumnReference(
dmlTargetTableReference.getIdentificationVariable(),

View File

@ -1355,7 +1355,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
// This uses identity generation, so we don't need to list the column
if ( identifierGenerator != null && identifierGenerator.generatedOnExecute()
if ( identifierGenerator != null && identifierGenerator.generatedOnExecution()
|| identifierGenerator instanceof CompositeNestedGeneratedValueGenerator ) {
identifierGenerator = null;
}
@ -1440,7 +1440,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( discriminatorExpression != null ) {
expressions.add( discriminatorExpression );
}
if ( identifierGenerator != null && !identifierGenerator.generatedOnExecute() ) {
if ( identifierGenerator != null && !identifierGenerator.generatedOnExecution() ) {
if ( identifierGeneratorParameter == null ) {
identifierGeneratorParameter =
new IdGeneratorParameter( identifierMapping, (BeforeExecutionGenerator) identifierGenerator );

View File

@ -14,6 +14,7 @@ import java.util.function.BiFunction;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.ast.MutationGroup;
/**
* Specialized MutationOperationGroup for case of no operations
@ -24,6 +25,9 @@ public class MutationOperationGroupNone extends AbstractMutationOperationGroup {
public MutationOperationGroupNone(MutationType mutationType, MutationTarget<?> mutationTarget) {
super( mutationType, mutationTarget );
}
public MutationOperationGroupNone(MutationGroup mutationGroup) {
this( mutationGroup.getMutationType(), mutationGroup.getMutationTarget() );
}
@Override
public int getNumberOfOperations() {

View File

@ -14,6 +14,7 @@ import java.util.function.BiFunction;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.ast.MutationGroup;
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
@ -28,6 +29,10 @@ public class MutationOperationGroupSingle extends AbstractMutationOperationGroup
this.operation = operation;
}
public MutationOperationGroupSingle(MutationGroup mutationGroup, MutationOperation operation) {
this( mutationGroup.getMutationType(), mutationGroup.getMutationTarget(), operation );
}
@Override
public int getNumberOfOperations() {
return 1;

View File

@ -42,7 +42,7 @@ public class IdentifierProperty extends AbstractAttribute implements IdentifierA
this.embedded = embedded;
this.hasIdentifierMapper = false;
this.identifierGenerator = identifierGenerator;
this.identifierAssignedByInsert = identifierGenerator.generatedOnExecute();
this.identifierAssignedByInsert = identifierGenerator.generatedOnExecution();
}
/**
@ -63,7 +63,7 @@ public class IdentifierProperty extends AbstractAttribute implements IdentifierA
this.embedded = embedded;
this.hasIdentifierMapper = hasIdentifierMapper;
this.identifierGenerator = identifierGenerator;
this.identifierAssignedByInsert = identifierGenerator.generatedOnExecute();
this.identifierAssignedByInsert = identifierGenerator.generatedOnExecution();
}
@Override

View File

@ -83,7 +83,7 @@ public interface ValueGeneration extends BeforeExecutionGenerator, OnExecutionGe
/**
* A SQL expression indicating how to calculate the generated value when the property value
* is {@linkplain #generatedOnExecute() generated in the database} and the mapped column is
* is {@linkplain #generatedOnExecution() generated in the database} and the mapped column is
* {@linkplain #referenceColumnInSql() included in the SQL statement}. The SQL expression
* might be:
* <ul>
@ -100,7 +100,7 @@ public interface ValueGeneration extends BeforeExecutionGenerator, OnExecutionGe
/**
* A SQL expression indicating how to calculate the generated value when the property value
* is {@linkplain #generatedOnExecute() generated in the database} and the mapped column is
* is {@linkplain #generatedOnExecution() generated in the database} and the mapped column is
* {@linkplain #referenceColumnInSql() included in the SQL statement}. The SQL expression
* might be:
* <ul>
@ -140,7 +140,7 @@ public interface ValueGeneration extends BeforeExecutionGenerator, OnExecutionGe
* generated in Java using a {@link ValueGenerator}.
*/
@Override
default boolean generatedOnExecute() {
default boolean generatedOnExecution() {
return getValueGenerator() == null;
}
@ -157,7 +157,7 @@ public interface ValueGeneration extends BeforeExecutionGenerator, OnExecutionGe
*/
@Override
default boolean writePropertyValue() {
return !this.generatedOnExecute() // value generated in memory and then written as normal
return !this.generatedOnExecution() // value generated in memory and then written as normal
// current value of property of entity instance written completely as normal
|| referenceColumnInSql() && getDatabaseGeneratedReferencedColumnValue()==null;
}

View File

@ -0,0 +1,161 @@
/*
* 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.tuple.entity;
import org.hibernate.dialect.Dialect;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.EventType;
import org.hibernate.generator.Generator;
import org.hibernate.generator.OnExecutionGenerator;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import static java.lang.System.arraycopy;
import static org.hibernate.generator.EventTypeSets.NONE;
/**
* Handles value generation for composite properties.
*/
class CompositeGeneratorBuilder {
private final Property mappingProperty;
private final Dialect dialect;
private boolean hadBeforeExecutionGeneration;
private boolean hadOnExecutionGeneration;
private List<OnExecutionGenerator> onExecutionGenerators;
public CompositeGeneratorBuilder(Property mappingProperty, Dialect dialect) {
this.mappingProperty = mappingProperty;
this.dialect = dialect;
}
public void add(Generator generator) {
if ( generator != null ) {
if ( generator.generatedOnExecution() ) {
if ( generator instanceof OnExecutionGenerator ) {
add( (OnExecutionGenerator) generator );
}
}
else {
if ( generator instanceof BeforeExecutionGenerator ) {
add( (BeforeExecutionGenerator) generator );
}
}
}
}
private void add(BeforeExecutionGenerator beforeExecutionGenerator) {
if ( beforeExecutionGenerator.generatesSometimes() ) {
hadBeforeExecutionGeneration = true;
}
}
private void add(OnExecutionGenerator onExecutionGenerator) {
if ( onExecutionGenerators == null ) {
onExecutionGenerators = new ArrayList<>();
}
onExecutionGenerators.add( onExecutionGenerator );
if ( onExecutionGenerator.generatesSometimes() ) {
hadOnExecutionGeneration = true;
}
}
public Generator build() {
if ( hadBeforeExecutionGeneration && hadOnExecutionGeneration) {
throw new CompositeValueGenerationException(
"Composite attribute [" + mappingProperty.getName() + "] contained both in-memory"
+ " and in-database value generation"
);
}
else if ( hadBeforeExecutionGeneration ) {
throw new UnsupportedOperationException("Composite in-memory value generation not supported");
}
else if ( hadOnExecutionGeneration ) {
final Component composite = (Component) mappingProperty.getValue();
// we need the numbers to match up so that we can properly handle 'referenced sql column values'
if ( onExecutionGenerators.size() != composite.getPropertySpan() ) {
throw new CompositeValueGenerationException(
"Internal error : mismatch between number of collected in-db generation strategies" +
" and number of attributes for composite attribute : " + mappingProperty.getName()
);
}
// the base-line values for the aggregated OnExecutionGenerator we will build here.
final EnumSet<EventType> eventTypes = EnumSet.noneOf(EventType.class);
boolean referenceColumns = false;
final String[] columnValues = new String[composite.getColumnSpan()];
// start building the aggregate values
int propertyIndex = -1;
int columnIndex = 0;
for ( Property property : composite.getProperties() ) {
propertyIndex++;
final OnExecutionGenerator generator = onExecutionGenerators.get( propertyIndex );
eventTypes.addAll( generator.getEventTypes() );
if ( generator.referenceColumnsInSql( dialect ) ) {
// override base-line value
referenceColumns = true;
final String[] referencedColumnValues = generator.getReferencedColumnValues( dialect );
if ( referencedColumnValues != null ) {
final int span = property.getColumnSpan();
if ( referencedColumnValues.length != span ) {
throw new CompositeValueGenerationException(
"Mismatch between number of collected generated column values and number of columns for composite attribute: "
+ mappingProperty.getName() + '.' + property.getName()
);
}
arraycopy( referencedColumnValues, 0, columnValues, columnIndex, span );
}
}
}
final boolean referenceColumnsInSql = referenceColumns;
// then use the aggregated values to build an OnExecutionGenerator
return new OnExecutionGenerator() {
@Override
public EnumSet<EventType> getEventTypes() {
return eventTypes;
}
@Override
public boolean referenceColumnsInSql(Dialect dialect) {
return referenceColumnsInSql;
}
@Override
public String[] getReferencedColumnValues(Dialect dialect) {
return columnValues;
}
@Override
public boolean writePropertyValue() {
return false;
}
};
}
else {
return new Generator() {
@Override
public EnumSet<EventType> getEventTypes() {
return NONE;
}
@Override
public boolean generatedOnExecution() {
return false;
}
};
}
}
}

View File

@ -0,0 +1,17 @@
/*
* 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.tuple.entity;
import org.hibernate.HibernateException;
import org.hibernate.Incubating;
@Incubating
public class CompositeValueGenerationException extends HibernateException {
public CompositeValueGenerationException(String message) {
super(message);
}
}

View File

@ -10,7 +10,6 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -31,7 +30,6 @@ import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadeStyles;
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.generator.Generator;
import org.hibernate.generator.OnExecutionGenerator;
import org.hibernate.generator.BeforeExecutionGenerator;
@ -59,7 +57,6 @@ import org.hibernate.type.EntityType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.Type;
import static org.hibernate.generator.EventTypeSets.NONE;
import static org.hibernate.internal.CoreLogging.messageLogger;
/**
@ -311,7 +308,7 @@ public class EntityMetamodel implements Serializable {
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final Generator generator = buildGenerator( property, creationContext );
if ( generator != null ) {
if ( i == tempVersionProperty && !generator.generatedOnExecute() ) {
if ( i == tempVersionProperty && !generator.generatedOnExecution() ) {
// when we have an in-memory generator for the version, we
// want to plug it in to the older infrastructure specific
// to version generation, instead of treating it like a
@ -325,7 +322,7 @@ public class EntityMetamodel implements Serializable {
propertyUpdateability[i] = false;
}
if ( generator.generatesOnInsert() ) {
if ( generator.generatedOnExecute() ) {
if ( generator.generatedOnExecution() ) {
foundPostInsertGeneratedValues = true;
}
else {
@ -333,7 +330,7 @@ public class EntityMetamodel implements Serializable {
}
}
if ( generator.generatesOnUpdate() ) {
if ( generator.generatedOnExecute() ) {
if ( generator.generatedOnExecution() ) {
foundPostUpdateGeneratedValues = true;
}
else {
@ -463,7 +460,7 @@ public class EntityMetamodel implements Serializable {
}
private static boolean generatedWithNoParameter(Generator generator) {
return generator.generatedOnExecute()
return generator.generatedOnExecution()
&& !((OnExecutionGenerator) generator).writePropertyValue();
}
@ -482,7 +479,7 @@ public class EntityMetamodel implements Serializable {
final CompositeGeneratorBuilder builder = new CompositeGeneratorBuilder( mappingProperty, dialect );
final Component component = (Component) mappingProperty.getValue();
for ( Property property : component.getProperties() ) {
builder.addPair( property.createGenerator( context ) );
builder.add( property.createGenerator( context ) );
}
return builder.build();
}
@ -497,175 +494,13 @@ public class EntityMetamodel implements Serializable {
return versionGenerator;
}
public static class ValueGenerationStrategyException extends HibernateException {
public ValueGenerationStrategyException(String message) {
super( message );
}
}
private static class CompositeGeneratorBuilder {
private final Property mappingProperty;
private final Dialect dialect;
private boolean hadInMemoryGeneration;
private boolean hadInDatabaseGeneration;
private List<OnExecutionGenerator> inDatabaseStrategies;
public CompositeGeneratorBuilder(Property mappingProperty, Dialect dialect) {
this.mappingProperty = mappingProperty;
this.dialect = dialect;
}
public void addPair(Generator generator) {
if ( generator != null ) {
if ( generator.generatedOnExecute() ) {
if ( generator instanceof OnExecutionGenerator ) {
add( (OnExecutionGenerator) generator );
}
}
else {
if ( generator instanceof BeforeExecutionGenerator ) {
add( (BeforeExecutionGenerator) generator );
}
}
}
}
private void add(BeforeExecutionGenerator inMemoryStrategy) {
if ( inMemoryStrategy.generatesSometimes() ) {
hadInMemoryGeneration = true;
}
}
private void add(OnExecutionGenerator inDatabaseStrategy) {
if ( inDatabaseStrategies == null ) {
inDatabaseStrategies = new ArrayList<>();
}
inDatabaseStrategies.add( inDatabaseStrategy );
if ( inDatabaseStrategy.generatesSometimes() ) {
hadInDatabaseGeneration = true;
}
}
public Generator build() {
if ( hadInMemoryGeneration && hadInDatabaseGeneration ) {
throw new ValueGenerationStrategyException(
"Composite attribute [" + mappingProperty.getName() + "] contained both in-memory"
+ " and in-database value generation"
);
}
else if ( hadInMemoryGeneration ) {
throw new UnsupportedOperationException( "Composite in-memory value generation not supported" );
}
else if ( hadInDatabaseGeneration ) {
final Component composite = (Component) mappingProperty.getValue();
// we need the numbers to match up so that we can properly handle 'referenced sql column values'
if ( inDatabaseStrategies.size() != composite.getPropertySpan() ) {
throw new ValueGenerationStrategyException(
"Internal error : mismatch between number of collected in-db generation strategies" +
" and number of attributes for composite attribute : " + mappingProperty.getName()
);
}
// the base-line values for the aggregated InDatabaseValueGenerationStrategy we will build here.
EnumSet<EventType> eventTypes = EnumSet.noneOf(EventType.class);
boolean referenceColumns = false;
String[] columnValues = new String[ composite.getColumnSpan() ];
// start building the aggregate values
int propertyIndex = -1;
int columnIndex = 0;
for ( Property property : composite.getProperties() ) {
propertyIndex++;
final OnExecutionGenerator generator = inDatabaseStrategies.get( propertyIndex );
eventTypes.addAll( generator.getEventTypes() );
if ( generator.referenceColumnsInSql(dialect) ) {
// override base-line value
referenceColumns = true;
}
if ( generator.getReferencedColumnValues(dialect) != null ) {
if ( generator.getReferencedColumnValues(dialect).length != property.getColumnSpan() ) {
throw new ValueGenerationStrategyException(
"Internal error : mismatch between number of collected 'referenced column values'" +
" and number of columns for composite attribute : " + mappingProperty.getName() +
'.' + property.getName()
);
}
System.arraycopy(
generator.getReferencedColumnValues(dialect),
0,
columnValues,
columnIndex,
property.getColumnSpan()
);
}
}
// then use the aggregated values to build the InDatabaseValueGenerationStrategy
return new OnExecutionGeneratorImpl( eventTypes, referenceColumns, columnValues );
}
else {
return new Generator() {
@Override
public EnumSet<EventType> getEventTypes() {
return NONE;
}
@Override
public boolean generatedOnExecute() {
return false;
}
};
}
}
}
private static class OnExecutionGeneratorImpl implements OnExecutionGenerator {
private final EnumSet<EventType> eventTypes;
private final boolean referenceColumnInSql;
private final String[] referencedColumnValues;
private OnExecutionGeneratorImpl(
EnumSet<EventType> eventTypes,
boolean referenceColumnInSql,
String[] referencedColumnValues) {
this.eventTypes = eventTypes;
this.referenceColumnInSql = referenceColumnInSql;
this.referencedColumnValues = referencedColumnValues;
}
@Override
public EnumSet<EventType> getEventTypes() {
return eventTypes;
}
@Override
public boolean referenceColumnsInSql(Dialect dialect) {
return referenceColumnInSql;
}
@Override
public String[] getReferencedColumnValues(Dialect dialect) {
return referencedColumnValues;
}
@Override
public boolean writePropertyValue() {
return false;
}
}
private void mapPropertyToIndex(Property prop, int i) {
propertyIndexes.put( prop.getName(), i );
if ( prop.getValue() instanceof Component ) {
Component composite = (Component) prop.getValue();
for ( Property subprop : composite.getProperties() ) {
private void mapPropertyToIndex(Property property, int i) {
propertyIndexes.put( property.getName(), i );
if ( property.getValue() instanceof Component ) {
Component composite = (Component) property.getValue();
for ( Property subproperty : composite.getProperties() ) {
propertyIndexes.put(
prop.getName() + '.' + subprop.getName(),
property.getName() + '.' + subproperty.getName(),
i
);
}
@ -682,23 +517,13 @@ public class EntityMetamodel implements Serializable {
}
for ( int i = 0; i < naturalIdPropertyNumbers.length; i++ ) {
final Generator strategy = generators[ naturalIdPropertyNumbers[i] ];
if ( strategy != null && strategy.generatesOnInsert() && strategy.generatedOnExecute() ) {
if ( strategy != null && strategy.generatesOnInsert() && strategy.generatedOnExecution() ) {
return true;
}
}
return false;
}
public boolean isVersionGeneratedOnExecute() {
final Generator strategy = generators[ versionPropertyIndex ];
return strategy != null && strategy.generatesSometimes() && strategy.generatedOnExecute();
}
public boolean isVersionGeneratedBeforeExecute() {
final Generator strategy = generators[ versionPropertyIndex ];
return strategy != null && strategy.generatesSometimes() && !strategy.generatedOnExecute();
}
public int[] getNaturalIdentifierProperties() {
return naturalIdPropertyNumbers;
}