JPA软删除的deleted值动态设置

42 阅读1分钟
/*
 * 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; m < attributeMappings.size(); m++ ) {
                final AttributeMapping attributeMapping = attributeMappings.get( m );
                final int jdbcTypeCount = attributeMapping.getJdbcTypeCount();
                for ( int i = 0; i < jdbcTypeCount; i++ ) {
                    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; attributeIndex < versionability.length; attributeIndex++ ) {
            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; position < operationGroup.getNumberOfOperations(); position++ ) {
            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();
            }
        }
    }
}