/*
* 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.engine.OptimisticLockStyle
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.spi.SessionFactoryImplementor
import org.hibernate.engine.spi.SharedSessionContractImplementor
import org.hibernate.metamodel.mapping.*
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.ast.ColumnValueBindingList
import org.hibernate.sql.model.ast.RestrictedTableMutation
import org.hibernate.sql.model.ast.builder.TableUpdateBuilder
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard
import org.hibernate.sql.model.internal.MutationGroupSingle
import org.hibernate.sql.model.internal.MutationOperationGroupFactory
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter
/**
* DeleteCoordinator for soft-deletes
*
* @author Steve Ebersole
*/
public class DeleteCoordinatorSoft extends AbstractDeleteCoordinator {
public DeleteCoordinatorSoft(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) {
super( entityPersister, factory )
}
@Override
protected MutationOperationGroup generateOperationGroup(
Object rowId,
Object[] loadedState,
boolean applyVersion,
SharedSessionContractImplementor session) {
final EntityTableMapping rootTableMapping = entityPersister().getIdentifierTableMapping()
final TableUpdateBuilderStandard<MutationOperation> tableUpdateBuilder = new TableUpdateBuilderStandard<>(
entityPersister(),
rootTableMapping,
factory()
)
applyKeyRestriction( rowId, entityPersister(), tableUpdateBuilder, rootTableMapping )
applySoftDelete( entityPersister().getSoftDeleteMapping(), tableUpdateBuilder )
applyPartitionKeyRestriction( tableUpdateBuilder )
applyOptimisticLocking( tableUpdateBuilder, loadedState, session )
final RestrictedTableMutation<MutationOperation> tableMutation = tableUpdateBuilder.buildMutation()
final MutationGroupSingle mutationGroup = new MutationGroupSingle(
MutationType.DELETE,
entityPersister(),
tableMutation
)
final MutationOperation mutationOperation = tableMutation.createMutationOperation( null, factory() )
return MutationOperationGroupFactory.singleOperation( mutationGroup, mutationOperation )
}
private void applyPartitionKeyRestriction(TableUpdateBuilder<?> tableUpdateBuilder) {
final AbstractEntityPersister persister = entityPersister()
if ( persister.hasPartitionedSelectionMapping() ) {
final AttributeMappingsList attributeMappings = persister.getAttributeMappings()
for ( int m = 0
final AttributeMapping attributeMapping = attributeMappings.get( m )
final int jdbcTypeCount = attributeMapping.getJdbcTypeCount()
for ( int i = 0
final SelectableMapping selectableMapping = attributeMapping.getSelectable( i )
if ( selectableMapping.isPartitioned() ) {
tableUpdateBuilder.addKeyRestrictionLeniently( selectableMapping )
}
}
}
}
}
private void applySoftDelete(
SoftDeleteMapping softDeleteMapping,
TableUpdateBuilderStandard<MutationOperation> tableUpdateBuilder) {
tableUpdateBuilder.addLiteralRestriction(
softDeleteMapping.getSelectionExpression(),
softDeleteMapping.getNonDeletedLiteralText(),
softDeleteMapping.getJdbcMapping()
)
tableUpdateBuilder.addValueColumn(
softDeleteMapping.getSelectionExpression(),
"?",
softDeleteMapping.getJdbcMapping()
)
}
protected void applyOptimisticLocking(
TableUpdateBuilderStandard<MutationOperation> tableUpdateBuilder,
Object[] loadedState,
SharedSessionContractImplementor session) {
final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle()
if ( optimisticLockStyle.isVersion() && entityPersister().getVersionMapping() != null ) {
applyVersionBasedOptLocking( tableUpdateBuilder )
}
else if ( loadedState != null && entityPersister().optimisticLockStyle().isAllOrDirty() ) {
applyNonVersionOptLocking(
optimisticLockStyle,
tableUpdateBuilder,
loadedState,
session
)
}
}
protected void applyVersionBasedOptLocking(TableUpdateBuilderStandard<MutationOperation> tableUpdateBuilder) {
assert entityPersister().optimisticLockStyle() == OptimisticLockStyle.VERSION
assert entityPersister().getVersionMapping() != null
tableUpdateBuilder.addOptimisticLockRestriction( entityPersister().getVersionMapping() )
}
protected void applyNonVersionOptLocking(
OptimisticLockStyle lockStyle,
TableUpdateBuilderStandard<MutationOperation> tableUpdateBuilder,
Object[] loadedState,
SharedSessionContractImplementor session) {
final AbstractEntityPersister persister = entityPersister()
assert loadedState != null
assert lockStyle.isAllOrDirty()
assert persister.optimisticLockStyle().isAllOrDirty()
assert session != null
final boolean[] versionability = persister.getPropertyVersionability()
for ( int attributeIndex = 0
final AttributeMapping attribute
// only makes sense to lock on singular attributes which are not excluded from optimistic locking
if ( versionability[attributeIndex]
&& !( attribute = persister.getAttributeMapping( attributeIndex ) ).isPluralAttributeMapping() ) {
breakDownJdbcValues( tableUpdateBuilder, session, attribute, loadedState[attributeIndex] )
}
}
}
private void breakDownJdbcValues(
TableUpdateBuilderStandard<MutationOperation> tableUpdateBuilder,
SharedSessionContractImplementor session,
AttributeMapping attribute,
Object loadedValue) {
if ( !tableUpdateBuilder.getMutatingTable()
.getTableName()
.equals( attribute.getContainingTableExpression() ) ) {
// it is not on the root table, skip it
return
}
final ColumnValueBindingList optimisticLockBindings = tableUpdateBuilder.getOptimisticLockBindings()
if ( optimisticLockBindings != null ) {
attribute.breakDownJdbcValues(
loadedValue,
(valueIndex, value, jdbcValueMapping) -> {
if ( !tableUpdateBuilder.getKeyRestrictionBindings()
.containsColumn(
jdbcValueMapping.getSelectableName(),
jdbcValueMapping.getJdbcMapping()
) ) {
optimisticLockBindings.consume( valueIndex, value, jdbcValueMapping )
}
}
,
session
)
}
}
@Override
protected void applyId(
Object id,
Object rowId,
MutationExecutor mutationExecutor,
MutationOperationGroup operationGroup,
SharedSessionContractImplementor session) {
final AbstractEntityPersister persister = entityPersister()
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings()
SoftDeleteMapping softDeleteMapping = persister.getSoftDeleteMapping()
JdbcMapping jdbcMapping = softDeleteMapping.getJdbcMapping()
BasicValueConverter valueConverter = jdbcMapping.getValueConverter()
jdbcValueBindings.bindValue(
valueConverter.toRelationalValue(true),
persister.physicalTableNameForMutation(softDeleteMapping),
softDeleteMapping.getSelectionExpression(),
ParameterUsage.SET
)
final EntityRowIdMapping rowIdMapping = persister.getRowIdMapping()
for ( int position = 0
final MutationOperation jdbcMutation = operationGroup.getOperation( position )
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcMutation.getTableDetails()
breakDownKeyJdbcValues( id, rowId, session, jdbcValueBindings, tableDetails )
final PreparedStatementDetails statementDetails = mutationExecutor.getPreparedStatementDetails( tableDetails.getTableName() )
if ( statementDetails != null ) {
// force creation of the PreparedStatement
//noinspection resource
statementDetails.resolveStatement()
}
}
}
}