HHH-13715 - working support for "multi-table" HQL/Criteria UPDATE and DELETE queries
complete support for "local temp table"-based strategy
This commit is contained in:
parent
0dae701c93
commit
b04599cbe5
|
@ -87,15 +87,7 @@ public interface JdbcServices extends Service {
|
||||||
return JdbcSelectExecutorStandardImpl.INSTANCE;
|
return JdbcSelectExecutorStandardImpl.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
default JdbcMutationExecutor getJdbcDeleteExecutor() {
|
default JdbcMutationExecutor getJdbcMutationExecutor() {
|
||||||
return StandardJdbcMutationExecutor.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
default JdbcMutationExecutor getJdbcUpdateExecutor() {
|
|
||||||
return StandardJdbcMutationExecutor.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
default JdbcMutationExecutor getJdbcInsertExecutor() {
|
|
||||||
return StandardJdbcMutationExecutor.INSTANCE;
|
return StandardJdbcMutationExecutor.INSTANCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,13 @@ public interface EntityMappingType extends ManagedMappingType, Loadable {
|
||||||
TableReferenceCollector collector,
|
TableReferenceCollector collector,
|
||||||
SqlExpressionResolver sqlExpressionResolver,
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
SqlAstCreationContext creationContext) {
|
SqlAstCreationContext creationContext) {
|
||||||
getEntityPersister().applyTableReferences( sqlAliasBase, baseJoinType, collector, sqlExpressionResolver, creationContext );
|
getEntityPersister().applyTableReferences(
|
||||||
|
sqlAliasBase,
|
||||||
|
baseJoinType,
|
||||||
|
collector,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
creationContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -85,7 +85,6 @@ import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
||||||
import org.hibernate.persister.walking.spi.EntityDefinition;
|
import org.hibernate.persister.walking.spi.EntityDefinition;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|
||||||
import org.hibernate.sql.Alias;
|
import org.hibernate.sql.Alias;
|
||||||
import org.hibernate.sql.SelectFragment;
|
import org.hibernate.sql.SelectFragment;
|
||||||
import org.hibernate.sql.SimpleSelect;
|
import org.hibernate.sql.SimpleSelect;
|
||||||
|
@ -93,6 +92,7 @@ import org.hibernate.sql.Template;
|
||||||
import org.hibernate.sql.ast.JoinType;
|
import org.hibernate.sql.ast.JoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReferenceCollector;
|
import org.hibernate.sql.ast.tree.from.TableReferenceCollector;
|
||||||
import org.hibernate.type.AnyType;
|
import org.hibernate.type.AnyType;
|
||||||
|
@ -2397,7 +2397,12 @@ public abstract class AbstractCollectionPersister
|
||||||
else {
|
else {
|
||||||
// we do have a "collection table" - apply it first
|
// we do have a "collection table" - apply it first
|
||||||
collector.applySecondaryTableReferences(
|
collector.applySecondaryTableReferences(
|
||||||
new TableReference( qualifiedTableName, sqlAliasBase.generateNewAlias(), false, getFactory() ),
|
new TableReference(
|
||||||
|
qualifiedTableName,
|
||||||
|
sqlAliasBase.generateNewAlias(),
|
||||||
|
false,
|
||||||
|
getFactory()
|
||||||
|
),
|
||||||
baseJoinType,
|
baseJoinType,
|
||||||
(lhs, rhs, joinType) -> {
|
(lhs, rhs, joinType) -> {
|
||||||
// create the join-predicate between the owner table and the collection table
|
// create the join-predicate between the owner table and the collection table
|
||||||
|
|
|
@ -28,10 +28,10 @@ import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.walking.spi.CollectionDefinition;
|
import org.hibernate.persister.walking.spi.CollectionDefinition;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|
||||||
import org.hibernate.sql.ast.JoinType;
|
import org.hibernate.sql.ast.JoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReferenceCollector;
|
import org.hibernate.sql.ast.tree.from.TableReferenceCollector;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReferenceContributor;
|
import org.hibernate.sql.ast.tree.from.TableReferenceContributor;
|
||||||
import org.hibernate.type.CollectionType;
|
import org.hibernate.type.CollectionType;
|
||||||
|
|
|
@ -1227,13 +1227,7 @@ public abstract class AbstractEntityPersister
|
||||||
SqlExpressionResolver sqlExpressionResolver,
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||||
SqlAstCreationContext creationContext) {
|
SqlAstCreationContext creationContext) {
|
||||||
final SqlAliasBase sqlAliasBase;
|
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
||||||
if ( aliasBaseGenerator == null ) {
|
|
||||||
sqlAliasBase = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
|
||||||
}
|
|
||||||
|
|
||||||
final TableGroupBuilder builder = TableGroupBuilder.builder(
|
final TableGroupBuilder builder = TableGroupBuilder.builder(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
|
|
|
@ -43,7 +43,6 @@ import org.hibernate.mapping.Value;
|
||||||
import org.hibernate.persister.spi.PersisterCreationContext;
|
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||||
import org.hibernate.query.ComparisonOperator;
|
import org.hibernate.query.ComparisonOperator;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|
||||||
import org.hibernate.sql.InFragment;
|
import org.hibernate.sql.InFragment;
|
||||||
import org.hibernate.sql.Insert;
|
import org.hibernate.sql.Insert;
|
||||||
import org.hibernate.sql.SelectFragment;
|
import org.hibernate.sql.SelectFragment;
|
||||||
|
@ -51,6 +50,7 @@ import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.JoinType;
|
import org.hibernate.sql.ast.JoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
|
||||||
executionContext.getSession()
|
executionContext.getSession()
|
||||||
);
|
);
|
||||||
|
|
||||||
return jdbcServices.getJdbcDeleteExecutor().execute(
|
return jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
jdbcDelete,
|
jdbcDelete,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
sql -> executionContext.getSession()
|
sql -> executionContext.getSession()
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class SimpleUpdateQueryPlan implements NonSelectQueryPlan {
|
||||||
executionContext.getSession()
|
executionContext.getSession()
|
||||||
);
|
);
|
||||||
|
|
||||||
return jdbcServices.getJdbcUpdateExecutor().execute(
|
return jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
jdbcUpdate,
|
jdbcUpdate,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
sql -> executionContext.getSession()
|
sql -> executionContext.getSession()
|
||||||
|
|
|
@ -102,6 +102,20 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
|
||||||
private static final Logger LOGGER = QueryLogger.subLogger( "sqm.ast" );
|
private static final Logger LOGGER = QueryLogger.subLogger( "sqm.ast" );
|
||||||
private static final boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
|
private static final boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
|
||||||
|
|
||||||
|
public static void logTree(SqmQuerySpec sqmQuerySpec, String header) {
|
||||||
|
if ( ! DEBUG_ENABLED ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SqmTreePrinter treePrinter = new SqmTreePrinter();
|
||||||
|
|
||||||
|
treePrinter.visitQuerySpec( sqmQuerySpec );
|
||||||
|
|
||||||
|
final String title = header != null ? header : "SqmQuerySpec Tree";
|
||||||
|
|
||||||
|
LOGGER.debugf( "%s :\n%s", title, treePrinter.buffer.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
public static void logTree(SqmStatement sqmStatement) {
|
public static void logTree(SqmStatement sqmStatement) {
|
||||||
if ( ! DEBUG_ENABLED ) {
|
if ( ! DEBUG_ENABLED ) {
|
||||||
return;
|
return;
|
||||||
|
@ -122,7 +136,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
|
||||||
printer.visitInsertSelectStatement( (SqmInsertSelectStatement) sqmStatement );
|
printer.visitInsertSelectStatement( (SqmInsertSelectStatement) sqmStatement );
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.debugf( "Semantic Query (SQM) Tree :\n%s", printer.buffer.toString() );
|
LOGGER.debugf( "SqmStatement Tree :\n%s", printer.buffer.toString() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private final StringBuffer buffer = new StringBuffer();
|
private final StringBuffer buffer = new StringBuffer();
|
||||||
|
|
|
@ -6,28 +6,22 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.mutation.internal;
|
package org.hibernate.query.sqm.mutation.internal;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.internal.util.collections.StandardStack;
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationOptions;
|
import org.hibernate.query.hql.spi.SqmCreationOptions;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
import org.hibernate.query.sqm.SqmQuerySource;
|
|
||||||
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
|
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
|
||||||
import org.hibernate.query.sqm.mutation.internal.idtable.IdTableSessionUidColumn;
|
|
||||||
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
||||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
|
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFromClause;
|
import org.hibernate.query.sqm.tree.from.SqmFromClause;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
|
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
||||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
|
||||||
import org.hibernate.type.StringType;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ -41,41 +35,30 @@ import org.jboss.logging.Logger;
|
||||||
public class SqmIdSelectGenerator {
|
public class SqmIdSelectGenerator {
|
||||||
private static final Logger log = Logger.getLogger( SqmIdSelectGenerator.class );
|
private static final Logger log = Logger.getLogger( SqmIdSelectGenerator.class );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @asciidoc
|
||||||
|
*
|
||||||
|
* Generates a query-spec for selecting all ids matching the restriction defined as part
|
||||||
|
* of the user's update/delete query. This query-spec is generally used:
|
||||||
|
*
|
||||||
|
* * to select all the matching ids via JDBC - see {@link SqmMutationStrategyHelper#selectMatchingIds}
|
||||||
|
* * as a sub-query restriction to insert rows into an "id table"
|
||||||
|
*/
|
||||||
public static SqmQuerySpec generateSqmEntityIdSelect(
|
public static SqmQuerySpec generateSqmEntityIdSelect(
|
||||||
SqmDeleteOrUpdateStatement sqmStatement,
|
SqmDeleteOrUpdateStatement sqmStatement,
|
||||||
ExecutionContext executionContext,
|
SqmCreationContext sqmCreationContext) {
|
||||||
SessionFactoryImplementor sessionFactory) {
|
final EntityDomainType entityDomainType = sqmStatement.getTarget().getModel();
|
||||||
final SqmIdSelectGenerator generator = new SqmIdSelectGenerator( sqmStatement, executionContext, sessionFactory );
|
|
||||||
return generator.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final SqmDeleteOrUpdateStatement sourceSqmStatement;
|
log.tracef( "Starting generation of entity-id SQM selection - %s", entityDomainType.getHibernateEntityName() );
|
||||||
private final ExecutionContext executionContext;
|
|
||||||
private final SqmCreationContext creationContext;
|
|
||||||
private final EntityDomainType entityType;
|
|
||||||
|
|
||||||
public SqmIdSelectGenerator(
|
|
||||||
SqmDeleteOrUpdateStatement sourceSqmStatement,
|
|
||||||
ExecutionContext executionContext,
|
|
||||||
SqmCreationContext creationContext) {
|
|
||||||
this.sourceSqmStatement = sourceSqmStatement;
|
|
||||||
this.executionContext = executionContext;
|
|
||||||
this.creationContext = creationContext;
|
|
||||||
|
|
||||||
final String targetEntityName = sourceSqmStatement.getTarget().getEntityName();
|
|
||||||
this.entityType = creationContext.getJpaMetamodel().entity( targetEntityName );
|
|
||||||
}
|
|
||||||
|
|
||||||
private SqmQuerySpec process() {
|
|
||||||
final SqmQuerySpec sqmQuerySpec = new SqmQuerySpec( creationContext.getNodeBuilder() );
|
|
||||||
|
|
||||||
|
final SqmQuerySpec sqmQuerySpec = new SqmQuerySpec( sqmCreationContext.getNodeBuilder() );
|
||||||
|
|
||||||
final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
|
final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
|
||||||
|
|
||||||
final SqmCreationState creationState = new SqmCreationState() {
|
final SqmCreationState creationState = new SqmCreationState() {
|
||||||
@Override
|
@Override
|
||||||
public SqmCreationContext getCreationContext() {
|
public SqmCreationContext getCreationContext() {
|
||||||
return creationContext;
|
return sqmCreationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,8 +72,9 @@ public class SqmIdSelectGenerator {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// temporary - used just for creating processingState
|
// temporary - used just for creating processingState
|
||||||
final SqmSelectStatement sqmSelectStatement = new SqmSelectStatement( creationContext.getNodeBuilder() );
|
final SqmSelectStatement sqmSelectStatement = new SqmSelectStatement( sqmCreationContext.getNodeBuilder() );
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
sqmSelectStatement.setQuerySpec( sqmQuerySpec );
|
sqmSelectStatement.setQuerySpec( sqmQuerySpec );
|
||||||
|
|
||||||
|
@ -105,27 +89,31 @@ public class SqmIdSelectGenerator {
|
||||||
final SqmFromClause sqmFromClause = new SqmFromClause();
|
final SqmFromClause sqmFromClause = new SqmFromClause();
|
||||||
sqmQuerySpec.setFromClause( sqmFromClause );
|
sqmQuerySpec.setFromClause( sqmFromClause );
|
||||||
|
|
||||||
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
final SqmRoot<?> sqmRoot = new SqmRoot( entityType, null, sourceSqmStatement.nodeBuilder() );
|
// final SqmRoot<?> sqmRoot = new SqmRoot( entityDomainType, null, sqmCreationContext.getNodeBuilder() );
|
||||||
|
final SqmRoot<?> sqmRoot = sqmStatement.getTarget();
|
||||||
|
|
||||||
|
log.debugf( "Using SqmRoot [%s] as root for entity id-select", sqmRoot );
|
||||||
sqmFromClause.addRoot( sqmRoot );
|
sqmFromClause.addRoot( sqmRoot );
|
||||||
|
|
||||||
final SqmSelectClause sqmSelectClause = new SqmSelectClause( true, creationContext.getNodeBuilder() );
|
final SqmSelectClause sqmSelectClause = new SqmSelectClause( true, sqmCreationContext.getNodeBuilder() );
|
||||||
sqmQuerySpec.setSelectClause( sqmSelectClause );
|
sqmQuerySpec.setSelectClause( sqmSelectClause );
|
||||||
applySelections( sqmQuerySpec, sqmRoot, processingState );
|
applySelections( sqmQuerySpec, sqmRoot, processingState );
|
||||||
|
|
||||||
if ( sourceSqmStatement.getWhereClause() != null ) {
|
if ( sqmStatement.getWhereClause() != null ) {
|
||||||
sqmQuerySpec.applyPredicate( sourceSqmStatement.getWhereClause().getPredicate() );
|
sqmQuerySpec.applyPredicate( sqmStatement.getWhereClause().getPredicate() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return sqmQuerySpec;
|
return sqmQuerySpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySelections(
|
private static void applySelections(
|
||||||
SqmQuerySpec sqmQuerySpec,
|
SqmQuerySpec sqmQuerySpec,
|
||||||
SqmRoot<?> sqmRoot,
|
SqmRoot<?> sqmRoot,
|
||||||
SqmCreationProcessingState processingState) {
|
SqmCreationProcessingState processingState) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
final SqmPath idPath = entityType.getIdentifierDescriptor().createSqmPath( sqmRoot, processingState.getCreationState() );
|
final SqmPath idPath = sqmRoot.getModel().getIdentifierDescriptor().createSqmPath( sqmRoot, processingState.getCreationState() );
|
||||||
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
sqmQuerySpec.getSelectClause().add(
|
sqmQuerySpec.getSelectClause().add(
|
||||||
|
|
|
@ -245,7 +245,6 @@ public class SqmMutationStrategyHelper {
|
||||||
|
|
||||||
final SqmQuerySpec sqmIdSelectQuerySpec = SqmIdSelectGenerator.generateSqmEntityIdSelect(
|
final SqmQuerySpec sqmIdSelectQuerySpec = SqmIdSelectGenerator.generateSqmEntityIdSelect(
|
||||||
sqmDeleteStatement,
|
sqmDeleteStatement,
|
||||||
executionContext,
|
|
||||||
factory
|
factory
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,12 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int execute(ExecutionContext executionContext) {
|
public int execute(ExecutionContext executionContext) {
|
||||||
final List<Object> ids = selectMatchingIds( executionContext );
|
final List<Object> ids = SqmMutationStrategyHelper.selectMatchingIds(
|
||||||
|
getSqmDeleteOrUpdateStatement(),
|
||||||
|
getDomainParameterXref(),
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
if ( ids == null || ids.isEmpty() ) {
|
if ( ids == null || ids.isEmpty() ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -133,14 +138,6 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
||||||
return ids.size();
|
return ids.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Object> selectMatchingIds(ExecutionContext executionContext) {
|
|
||||||
return SqmMutationStrategyHelper.selectMatchingIds(
|
|
||||||
getSqmDeleteOrUpdateStatement(),
|
|
||||||
getDomainParameterXref(),
|
|
||||||
executionContext
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void executeDelete(
|
protected void executeDelete(
|
||||||
QuerySpec cteDefinition,
|
QuerySpec cteDefinition,
|
||||||
String targetTable,
|
String targetTable,
|
||||||
|
@ -158,7 +155,7 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
||||||
executionContext
|
executionContext
|
||||||
);
|
);
|
||||||
|
|
||||||
final SessionFactoryImplementor sessionFactory = getCreationContext().getSessionFactory();
|
final SessionFactoryImplementor sessionFactory = getSessionFactory();
|
||||||
|
|
||||||
final JdbcDelete jdbcDelete = sqlAstTranslatorFactory.buildDeleteTranslator( sessionFactory )
|
final JdbcDelete jdbcDelete = sqlAstTranslatorFactory.buildDeleteTranslator( sessionFactory )
|
||||||
.translate( cteStatement );
|
.translate( cteStatement );
|
||||||
|
@ -168,7 +165,7 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
||||||
.getJdbcCoordinator()
|
.getJdbcCoordinator()
|
||||||
.getLogicalConnection();
|
.getLogicalConnection();
|
||||||
|
|
||||||
sessionFactory.getJdbcServices().getJdbcDeleteExecutor().execute(
|
sessionFactory.getJdbcServices().getJdbcMutationExecutor().execute(
|
||||||
jdbcDelete,
|
jdbcDelete,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
sql -> {
|
sql -> {
|
||||||
|
|
|
@ -16,15 +16,13 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.query.ComparisonOperator;
|
import org.hibernate.query.ComparisonOperator;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.query.spi.QueryOptions;
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
import org.hibernate.query.sqm.internal.SqmTreePrinter;
|
||||||
import org.hibernate.query.sqm.mutation.internal.SqmIdSelectGenerator;
|
import org.hibernate.query.sqm.mutation.internal.SqmIdSelectGenerator;
|
||||||
import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler;
|
|
||||||
import org.hibernate.query.sqm.mutation.spi.Handler;
|
|
||||||
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
|
||||||
import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation;
|
import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation;
|
||||||
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
|
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
|
||||||
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
|
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
|
||||||
|
@ -32,6 +30,7 @@ import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
|
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
||||||
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstInsertSelectTranslator;
|
import org.hibernate.sql.ast.SqlAstInsertSelectTranslator;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
|
@ -42,6 +41,7 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
||||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
import org.hibernate.sql.exec.spi.JdbcInsert;
|
import org.hibernate.sql.exec.spi.JdbcInsert;
|
||||||
|
@ -52,162 +52,39 @@ import org.hibernate.type.UUIDCharType;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for {@link Handler} implementations
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractTableBasedHandler extends AbstractMutationHandler {
|
@SuppressWarnings("WeakerAccess")
|
||||||
private static final Logger log = Logger.getLogger( AbstractTableBasedHandler.class );
|
public final class ExecuteWithIdTableHelper {
|
||||||
|
private static final Logger log = Logger.getLogger( ExecuteWithIdTableHelper.class );
|
||||||
|
public static final boolean debugging = log.isDebugEnabled();
|
||||||
|
|
||||||
private final IdTable idTable;
|
private ExecuteWithIdTableHelper() {
|
||||||
private final TempTableDdlTransactionHandling ddlTransactionHandling;
|
}
|
||||||
private final BeforeUseAction beforeUseAction;
|
|
||||||
private final AfterUseAction afterUseAction;
|
|
||||||
|
|
||||||
private final DomainParameterXref domainParameterXref;
|
public static int saveMatchingIdsIntoIdTable(
|
||||||
|
SqmUpdateStatement sqmMutation,
|
||||||
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
|
MultiTableSqmMutationConverter sqmConverter,
|
||||||
|
TableGroup mutatingTableGroup,
|
||||||
private final Supplier<IdTableExporter> exporterSupplier;
|
Predicate suppliedPredicate,
|
||||||
|
|
||||||
|
|
||||||
public AbstractTableBasedHandler(
|
|
||||||
SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement,
|
|
||||||
IdTable idTable,
|
IdTable idTable,
|
||||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
BeforeUseAction beforeUseAction,
|
JdbcParameterBindings jdbcParameterBindings,
|
||||||
AfterUseAction afterUseAction,
|
ExecutionContext executionContext) {
|
||||||
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
|
||||||
Supplier<IdTableExporter> exporterSupplier,
|
|
||||||
HandlerCreationContext creationContext) {
|
|
||||||
super( sqmDeleteOrUpdateStatement, creationContext );
|
|
||||||
this.idTable = idTable;
|
|
||||||
this.ddlTransactionHandling = ddlTransactionHandling;
|
|
||||||
this.beforeUseAction = beforeUseAction;
|
|
||||||
this.afterUseAction = afterUseAction;
|
|
||||||
|
|
||||||
this.domainParameterXref = domainParameterXref;
|
|
||||||
|
|
||||||
this.sessionUidAccess = sessionUidAccess;
|
|
||||||
this.exporterSupplier = exporterSupplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IdTable getIdTable() {
|
|
||||||
return idTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DomainParameterXref getDomainParameterXref() {
|
|
||||||
return domainParameterXref;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BeforeUseAction getBeforeUseAction() {
|
|
||||||
return beforeUseAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AfterUseAction getAfterUseAction() {
|
|
||||||
return afterUseAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Function<SharedSessionContractImplementor, String> getSessionUidAccess() {
|
|
||||||
return sessionUidAccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Supplier<IdTableExporter> getExporterSupplier() {
|
|
||||||
return exporterSupplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int execute(ExecutionContext executionContext) {
|
|
||||||
|
|
||||||
// In general:
|
|
||||||
// 1) prepare for use - this is completely a subclass hook
|
|
||||||
// 2) perform execution
|
|
||||||
// 3) release after use - again, completely a subclass hook
|
|
||||||
|
|
||||||
beforeExecution( executionContext );
|
|
||||||
|
|
||||||
try {
|
|
||||||
return performExecution( executionContext );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
afterExecution( executionContext );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow subclasses a chance to perform any preliminary work they need
|
|
||||||
* to perform prior to execution
|
|
||||||
*/
|
|
||||||
protected void beforeExecution(ExecutionContext executionContext) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow subclasses a chance to perform any clean-up work they need
|
|
||||||
* to perform after execution
|
|
||||||
*/
|
|
||||||
protected void afterExecution(ExecutionContext executionContext) {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int performExecution(ExecutionContext executionContext) {
|
|
||||||
performBeforeUseActions( executionContext );
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1) save the matching ids into the id table
|
|
||||||
final int affectedRowCount = saveMatchingIdsIntoIdTable( executionContext );
|
|
||||||
log.debugf( "insert for matching ids resulted in %s rows", affectedRowCount );
|
|
||||||
|
|
||||||
// 2) perform the actual individual update or deletes, using
|
|
||||||
// inclusion in the id-table as restriction
|
|
||||||
performMutations( executionContext );
|
|
||||||
|
|
||||||
return affectedRowCount;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
performAfterUseActions( executionContext );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performBeforeUseActions(ExecutionContext executionContext) {
|
|
||||||
if ( getBeforeUseAction() == BeforeUseAction.CREATE ) {
|
|
||||||
IdTableHelper.createIdTable( idTable, getExporterSupplier().get(), ddlTransactionHandling, executionContext.getSession() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performAfterUseActions(ExecutionContext executionContext) {
|
|
||||||
if ( getAfterUseAction() == AfterUseAction.CLEAN ) {
|
|
||||||
IdTableHelper.cleanIdTableRows(
|
|
||||||
idTable,
|
|
||||||
getExporterSupplier().get(),
|
|
||||||
sessionUidAccess,
|
|
||||||
executionContext.getSession()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ( getAfterUseAction() == AfterUseAction.DROP ) {
|
|
||||||
IdTableHelper.dropIdTable(
|
|
||||||
idTable,
|
|
||||||
getExporterSupplier().get(),
|
|
||||||
ddlTransactionHandling,
|
|
||||||
executionContext.getSession()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int saveMatchingIdsIntoIdTable(ExecutionContext executionContext) {
|
|
||||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||||
|
|
||||||
final SqmQuerySpec sqmIdSelect = SqmIdSelectGenerator.generateSqmEntityIdSelect(
|
final SqmQuerySpec sqmIdSelect = SqmIdSelectGenerator.generateSqmEntityIdSelect(
|
||||||
getSqmDeleteOrUpdateStatement(),
|
sqmMutation,
|
||||||
executionContext,
|
|
||||||
factory
|
factory
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( getIdTable().getSessionUidColumn() != null ) {
|
if ( idTable.getSessionUidColumn() != null ) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
sqmIdSelect.getSelectClause().add(
|
sqmIdSelect.getSelectClause().add(
|
||||||
new SqmSelection(
|
new SqmSelection(
|
||||||
new SqmLiteral(
|
new SqmLiteral(
|
||||||
executionContext.getSession().getSessionIdentifier().toString(),
|
sessionUidAccess.apply( executionContext.getSession() ),
|
||||||
UUIDCharType.INSTANCE,
|
UUIDCharType.INSTANCE,
|
||||||
executionContext.getSession().getFactory().getNodeBuilder()
|
executionContext.getSession().getFactory().getNodeBuilder()
|
||||||
),
|
),
|
||||||
|
@ -217,23 +94,20 @@ public abstract class AbstractTableBasedHandler extends AbstractMutationHandler
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final SqmTranslatorFactory sqmTranslatorFactory = factory.getQueryEngine().getSqmTranslatorFactory();
|
SqmTreePrinter.logTree( sqmIdSelect, "Entity-identifier Selection SqmQuerySpec" );
|
||||||
final SqmSelectTranslator sqmTranslator = sqmTranslatorFactory.createSelectTranslator(
|
|
||||||
QueryOptions.NONE,
|
|
||||||
domainParameterXref,
|
|
||||||
executionContext.getQueryParameterBindings(),
|
|
||||||
executionContext.getSession().getLoadQueryInfluencers(),
|
|
||||||
factory
|
|
||||||
);
|
|
||||||
|
|
||||||
final SqmQuerySpecTranslation sqmIdSelectTranslation = sqmTranslator.translate( sqmIdSelect );
|
|
||||||
|
|
||||||
final InsertSelectStatement insertSelectStatement = new InsertSelectStatement();
|
final InsertSelectStatement insertSelectStatement = new InsertSelectStatement();
|
||||||
|
|
||||||
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
|
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
|
||||||
|
|
||||||
insertSelectStatement.setTargetTable( idTableReference );
|
insertSelectStatement.setTargetTable( idTableReference );
|
||||||
insertSelectStatement.setSourceSelectStatement( sqmIdSelectTranslation.getSqlAst() );
|
|
||||||
|
final QuerySpec matchingIdRestrictionQuerySpec = generateTempTableInsertValuesQuerySpec(
|
||||||
|
sqmConverter,
|
||||||
|
mutatingTableGroup,
|
||||||
|
suppliedPredicate,
|
||||||
|
sqmIdSelect
|
||||||
|
);
|
||||||
|
insertSelectStatement.setSourceSelectStatement( matchingIdRestrictionQuerySpec );
|
||||||
|
|
||||||
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
|
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
|
||||||
final IdTableColumn column = idTable.getIdTableColumns().get( i );
|
final IdTableColumn column = idTable.getIdTableColumns().get( i );
|
||||||
|
@ -248,34 +122,117 @@ public abstract class AbstractTableBasedHandler extends AbstractMutationHandler
|
||||||
final SqlAstInsertSelectTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildInsertTranslator( factory );
|
final SqlAstInsertSelectTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildInsertTranslator( factory );
|
||||||
final JdbcInsert jdbcInsert = sqlAstTranslator.translate( insertSelectStatement );
|
final JdbcInsert jdbcInsert = sqlAstTranslator.translate( insertSelectStatement );
|
||||||
|
|
||||||
|
return jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
|
||||||
executionContext.getQueryParameterBindings(),
|
|
||||||
domainParameterXref,
|
|
||||||
SqmUtil.generateJdbcParamsXref(
|
|
||||||
domainParameterXref,
|
|
||||||
sqmIdSelectTranslation::getJdbcParamsBySqmParam
|
|
||||||
),
|
|
||||||
factory.getDomainModel(),
|
|
||||||
sqmTranslator.getFromClauseAccess()::findTableGroup,
|
|
||||||
executionContext.getSession()
|
|
||||||
);
|
|
||||||
|
|
||||||
return jdbcServices.getJdbcInsertExecutor().execute(
|
|
||||||
jdbcInsert,
|
jdbcInsert,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
sql -> executionContext.getSession()
|
sql -> executionContext.getSession()
|
||||||
.getJdbcCoordinator()
|
.getJdbcCoordinator()
|
||||||
.getStatementPreparer()
|
.getStatementPreparer()
|
||||||
.prepareStatement( sql ),
|
.prepareStatement( sql ),
|
||||||
(integer, preparedStatement) -> {
|
(integer, preparedStatement) -> {},
|
||||||
},
|
|
||||||
executionContext
|
executionContext
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static QuerySpec generateTempTableInsertValuesQuerySpec(
|
||||||
|
MultiTableSqmMutationConverter sqmConverter,
|
||||||
|
TableGroup mutatingTableGroup, Predicate suppliedPredicate, SqmQuerySpec sqmIdSelect) {
|
||||||
|
final QuerySpec matchingIdRestrictionQuerySpec = new QuerySpec( false, 1 );
|
||||||
|
sqmConverter.visitSelectClause(
|
||||||
|
sqmIdSelect.getSelectClause(),
|
||||||
|
matchingIdRestrictionQuerySpec,
|
||||||
|
columnReference -> {},
|
||||||
|
(sqmParameter, jdbcParameters) -> {}
|
||||||
|
);
|
||||||
|
matchingIdRestrictionQuerySpec.getFromClause().addRoot( mutatingTableGroup );
|
||||||
|
matchingIdRestrictionQuerySpec.applyPredicate( suppliedPredicate );
|
||||||
|
return matchingIdRestrictionQuerySpec;
|
||||||
|
}
|
||||||
|
|
||||||
public QuerySpec createIdTableSubQuery(ExecutionContext executionContext) {
|
public static int saveMatchingIdsIntoIdTable(
|
||||||
|
SqmDeleteOrUpdateStatement sqmMutation,
|
||||||
|
Predicate predicate,
|
||||||
|
IdTable idTable,
|
||||||
|
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
||||||
|
DomainParameterXref domainParameterXref,
|
||||||
|
JdbcParameterBindings jdbcParameterBindings,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||||
|
|
||||||
|
final SqmQuerySpec sqmIdSelect = SqmIdSelectGenerator.generateSqmEntityIdSelect(
|
||||||
|
sqmMutation,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( idTable.getSessionUidColumn() != null ) {
|
||||||
|
//noinspection unchecked
|
||||||
|
sqmIdSelect.getSelectClause().add(
|
||||||
|
new SqmSelection(
|
||||||
|
new SqmLiteral(
|
||||||
|
sessionUidAccess.apply( executionContext.getSession() ),
|
||||||
|
UUIDCharType.INSTANCE,
|
||||||
|
executionContext.getSession().getFactory().getNodeBuilder()
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
executionContext.getSession().getFactory().getNodeBuilder()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SqmTreePrinter.logTree( sqmIdSelect, "Entity-identifier Selection SqmQuerySpec" );
|
||||||
|
|
||||||
|
final SqmTranslatorFactory sqmTranslatorFactory = factory.getQueryEngine().getSqmTranslatorFactory();
|
||||||
|
final SqmSelectTranslator sqmTranslator = sqmTranslatorFactory.createSelectTranslator(
|
||||||
|
QueryOptions.NONE,
|
||||||
|
domainParameterXref,
|
||||||
|
executionContext.getQueryParameterBindings(),
|
||||||
|
executionContext.getSession().getLoadQueryInfluencers(),
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
|
||||||
|
final SqmQuerySpecTranslation sqmIdSelectTranslation = sqmTranslator.translate( sqmIdSelect );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
final InsertSelectStatement insertSelectStatement = new InsertSelectStatement();
|
||||||
|
|
||||||
|
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
|
||||||
|
insertSelectStatement.setTargetTable( idTableReference );
|
||||||
|
|
||||||
|
final QuerySpec matchingIdRestrictionQuerySpec = sqmIdSelectTranslation.getSqlAst();
|
||||||
|
insertSelectStatement.setSourceSelectStatement( matchingIdRestrictionQuerySpec );
|
||||||
|
matchingIdRestrictionQuerySpec.applyPredicate( predicate );
|
||||||
|
|
||||||
|
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
|
||||||
|
final IdTableColumn column = idTable.getIdTableColumns().get( i );
|
||||||
|
insertSelectStatement.addTargetColumnReferences(
|
||||||
|
new ColumnReference( idTableReference, column.getColumnName(), column.getJdbcMapping(), factory )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final JdbcServices jdbcServices = factory.getJdbcServices();
|
||||||
|
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||||
|
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||||
|
final SqlAstInsertSelectTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildInsertTranslator( factory );
|
||||||
|
final JdbcInsert jdbcInsert = sqlAstTranslator.translate( insertSelectStatement );
|
||||||
|
|
||||||
|
return jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
|
jdbcInsert,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
sql -> executionContext.getSession()
|
||||||
|
.getJdbcCoordinator()
|
||||||
|
.getStatementPreparer()
|
||||||
|
.prepareStatement( sql ),
|
||||||
|
(integer, preparedStatement) -> {},
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QuerySpec createIdTableSelectQuerySpec(
|
||||||
|
IdTable idTable,
|
||||||
|
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
||||||
|
EntityMappingType entityDescriptor,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
final QuerySpec querySpec = new QuerySpec( false );
|
final QuerySpec querySpec = new QuerySpec( false );
|
||||||
|
|
||||||
final TableReference idTableReference = new TableReference(
|
final TableReference idTableReference = new TableReference(
|
||||||
|
@ -286,7 +243,7 @@ public abstract class AbstractTableBasedHandler extends AbstractMutationHandler
|
||||||
);
|
);
|
||||||
final TableGroup idTableGroup = new StandardTableGroup(
|
final TableGroup idTableGroup = new StandardTableGroup(
|
||||||
new NavigablePath( idTableReference.getTableExpression() ),
|
new NavigablePath( idTableReference.getTableExpression() ),
|
||||||
getEntityDescriptor(),
|
entityDescriptor,
|
||||||
LockMode.NONE,
|
LockMode.NONE,
|
||||||
idTableReference,
|
idTableReference,
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
|
@ -296,15 +253,16 @@ public abstract class AbstractTableBasedHandler extends AbstractMutationHandler
|
||||||
|
|
||||||
querySpec.getFromClause().addRoot( idTableGroup );
|
querySpec.getFromClause().addRoot( idTableGroup );
|
||||||
|
|
||||||
applySelections( querySpec, idTableReference, executionContext );
|
applyIdTableSelections( querySpec, idTableReference, idTable, executionContext );
|
||||||
applyRestrictions( querySpec, idTableReference, executionContext );
|
applyIdTableRestrictions( querySpec, idTableReference, idTable, sessionUidAccess, executionContext );
|
||||||
|
|
||||||
return querySpec;
|
return querySpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySelections(
|
private static void applyIdTableSelections(
|
||||||
QuerySpec querySpec,
|
QuerySpec querySpec,
|
||||||
TableReference tableReference,
|
TableReference tableReference,
|
||||||
|
IdTable idTable,
|
||||||
ExecutionContext executionContext) {
|
ExecutionContext executionContext) {
|
||||||
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
|
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
|
||||||
final IdTableColumn idTableColumn = idTable.getIdTableColumns().get( i );
|
final IdTableColumn idTableColumn = idTable.getIdTableColumns().get( i );
|
||||||
|
@ -326,9 +284,11 @@ public abstract class AbstractTableBasedHandler extends AbstractMutationHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyRestrictions(
|
private static void applyIdTableRestrictions(
|
||||||
QuerySpec querySpec,
|
QuerySpec querySpec,
|
||||||
TableReference idTableReference,
|
TableReference idTableReference,
|
||||||
|
IdTable idTable,
|
||||||
|
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
||||||
ExecutionContext executionContext) {
|
ExecutionContext executionContext) {
|
||||||
if ( idTable.getSessionUidColumn() != null ) {
|
if ( idTable.getSessionUidColumn() != null ) {
|
||||||
querySpec.applyPredicate(
|
querySpec.applyPredicate(
|
||||||
|
@ -350,5 +310,44 @@ public abstract class AbstractTableBasedHandler extends AbstractMutationHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void performMutations(ExecutionContext executionContext);
|
public static void performBeforeIdTableUseActions(
|
||||||
|
BeforeUseAction beforeUseAction,
|
||||||
|
IdTable idTable,
|
||||||
|
Supplier<IdTableExporter> idTableExporterAccess,
|
||||||
|
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
if ( beforeUseAction == BeforeUseAction.CREATE ) {
|
||||||
|
IdTableHelper.createIdTable(
|
||||||
|
idTable,
|
||||||
|
idTableExporterAccess.get(),
|
||||||
|
ddlTransactionHandling,
|
||||||
|
executionContext.getSession()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void performAfterIdTableUseActions(
|
||||||
|
AfterUseAction afterUseAction,
|
||||||
|
IdTable idTable,
|
||||||
|
Supplier<IdTableExporter> idTableExporterAccess,
|
||||||
|
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||||
|
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
if ( afterUseAction == AfterUseAction.CLEAN ) {
|
||||||
|
IdTableHelper.cleanIdTableRows(
|
||||||
|
idTable,
|
||||||
|
idTableExporterAccess.get(),
|
||||||
|
sessionUidAccess,
|
||||||
|
executionContext.getSession()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ( afterUseAction == AfterUseAction.DROP ) {
|
||||||
|
IdTableHelper.dropIdTable(
|
||||||
|
idTable,
|
||||||
|
idTableExporterAccess.get(),
|
||||||
|
ddlTransactionHandling,
|
||||||
|
executionContext.getSession()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public final class ExecuteWithoutIdTableHelper {
|
||||||
|
private ExecuteWithoutIdTableHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QuerySpec createIdMatchingSubQuerySpec(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
TableReference rootTableReference,
|
||||||
|
Predicate predicate,
|
||||||
|
EntityPersister rootEntityPersister,
|
||||||
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
/*
|
||||||
|
* `select root_id from root_table where {predicate}
|
||||||
|
*/
|
||||||
|
final QuerySpec matchingIdSelect = new QuerySpec( false, 1 );
|
||||||
|
|
||||||
|
final StandardTableGroup matchingIdSelectTableGroup = new StandardTableGroup(
|
||||||
|
navigablePath,
|
||||||
|
rootEntityPersister,
|
||||||
|
LockMode.PESSIMISTIC_WRITE,
|
||||||
|
rootTableReference,
|
||||||
|
Collections.emptyList(),
|
||||||
|
null,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
|
||||||
|
matchingIdSelect.getFromClause().addRoot( matchingIdSelectTableGroup );
|
||||||
|
|
||||||
|
rootEntityPersister.getIdentifierMapping().visitColumns(
|
||||||
|
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||||
|
final ColumnReference columnReference = (ColumnReference) sqlExpressionResolver.resolveSqlExpression(
|
||||||
|
SqlExpressionResolver.createColumnReferenceKey( rootTableReference, columnExpression ),
|
||||||
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
|
rootTableReference,
|
||||||
|
columnExpression,
|
||||||
|
jdbcMapping,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final SqlSelection sqlSelection = new SqlSelectionImpl(
|
||||||
|
// irrelevant
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
columnReference,
|
||||||
|
jdbcMapping
|
||||||
|
);
|
||||||
|
matchingIdSelect.getSelectClause().addSqlSelection( sqlSelection );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
matchingIdSelect.applyPredicate( predicate );
|
||||||
|
|
||||||
|
return matchingIdSelect;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
|
||||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
||||||
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
||||||
|
@ -61,7 +61,16 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg
|
||||||
SqmUpdateStatement sqmUpdateStatement,
|
SqmUpdateStatement sqmUpdateStatement,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
HandlerCreationContext creationContext) {
|
HandlerCreationContext creationContext) {
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
return new TableBasedUpdateHandler(
|
||||||
|
sqmUpdateStatement,
|
||||||
|
domainParameterXref, idTable,
|
||||||
|
SharedSessionContractImplementor::getTenantIdentifier,
|
||||||
|
idTableExporterAccess,
|
||||||
|
BeforeUseAction.CREATE,
|
||||||
|
afterUseAction,
|
||||||
|
ddlTransactionHandling,
|
||||||
|
creationContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,29 +78,15 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg
|
||||||
SqmDeleteStatement sqmDeleteStatement,
|
SqmDeleteStatement sqmDeleteStatement,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
HandlerCreationContext creationContext) {
|
HandlerCreationContext creationContext) {
|
||||||
if ( sqmDeleteStatement.getWhereClause() == null
|
|
||||||
|| sqmDeleteStatement.getWhereClause().getPredicate() == null ) {
|
|
||||||
// optimization - special handler not needing the temp table
|
|
||||||
return new UnrestrictedTableBasedDeleteHandler(
|
|
||||||
sqmDeleteStatement,
|
|
||||||
idTable,
|
|
||||||
ddlTransactionHandling,
|
|
||||||
domainParameterXref,
|
|
||||||
BeforeUseAction.CREATE,
|
|
||||||
afterUseAction,
|
|
||||||
sessionContractImplementor -> null,
|
|
||||||
creationContext
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TableBasedDeleteHandler(
|
return new TableBasedDeleteHandler(
|
||||||
sqmDeleteStatement,
|
sqmDeleteStatement,
|
||||||
|
domainParameterXref,
|
||||||
idTable,
|
idTable,
|
||||||
|
SharedSessionContractImplementor::getTenantIdentifier,
|
||||||
idTableExporterAccess,
|
idTableExporterAccess,
|
||||||
BeforeUseAction.CREATE,
|
BeforeUseAction.CREATE,
|
||||||
afterUseAction,
|
afterUseAction,
|
||||||
ddlTransactionHandling,
|
ddlTransactionHandling,
|
||||||
domainParameterXref,
|
|
||||||
creationContext
|
creationContext
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* 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.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
|
import org.hibernate.query.spi.QueryParameterBindings;
|
||||||
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
|
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.SqlAstQuerySpecProcessingStateImpl;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
|
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
|
||||||
|
import org.hibernate.query.sqm.tree.update.SqmAssignment;
|
||||||
|
import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
||||||
|
import org.hibernate.sql.ast.JoinType;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstProcessingState;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignable;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
|
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized BaseSqmToSqlAstConverter implementation used during conversion
|
||||||
|
* of an SQM mutation query tree representing into the various SQL AST trees
|
||||||
|
* needed to perform that operation.
|
||||||
|
*
|
||||||
|
* @see #visitSetClause(SqmSetClause, Consumer, BiConsumer)
|
||||||
|
* @see #visitWhereClause(SqmWhereClause, Consumer, BiConsumer)
|
||||||
|
* @see #visitSelectClause(SqmSelectClause, QuerySpec, Consumer, BiConsumer)
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter implements DomainResultCreationState {
|
||||||
|
private final EntityMappingType mutatingEntityDescriptor;
|
||||||
|
private final TableGroup mutatingTableGroup;
|
||||||
|
|
||||||
|
private BiConsumer<SqmParameter, List<JdbcParameter>> parameterResolutionConsumer;
|
||||||
|
|
||||||
|
public MultiTableSqmMutationConverter(
|
||||||
|
EntityMappingType mutatingEntityDescriptor,
|
||||||
|
DomainParameterXref domainParameterXref,
|
||||||
|
QueryOptions queryOptions,
|
||||||
|
QueryParameterBindings domainParameterBindings,
|
||||||
|
SqlAstCreationContext creationContext) {
|
||||||
|
super( creationContext, queryOptions, domainParameterXref, domainParameterBindings );
|
||||||
|
this.mutatingEntityDescriptor = mutatingEntityDescriptor;
|
||||||
|
|
||||||
|
final SqlAstProcessingStateImpl rootProcessingState = new SqlAstProcessingStateImpl(
|
||||||
|
null,
|
||||||
|
this,
|
||||||
|
getCurrentClauseStack()::getCurrent
|
||||||
|
);
|
||||||
|
|
||||||
|
getProcessingStateStack().push( rootProcessingState );
|
||||||
|
|
||||||
|
final NavigablePath navigablePath = new NavigablePath( mutatingEntityDescriptor.getEntityName() );
|
||||||
|
this.mutatingTableGroup = mutatingEntityDescriptor.createRootTableGroup(
|
||||||
|
navigablePath,
|
||||||
|
null,
|
||||||
|
JoinType.LEFT,
|
||||||
|
LockMode.PESSIMISTIC_WRITE,
|
||||||
|
getSqlAliasBaseGenerator(),
|
||||||
|
getSqlExpressionResolver(),
|
||||||
|
() -> predicate -> {
|
||||||
|
},
|
||||||
|
creationContext.getSessionFactory()
|
||||||
|
);
|
||||||
|
|
||||||
|
// because this is a multi-table update, here we expect multiple TableReferences
|
||||||
|
assert !mutatingTableGroup.getTableReferenceJoins().isEmpty();
|
||||||
|
|
||||||
|
getFromClauseAccess().registerTableGroup( navigablePath, mutatingTableGroup );
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public EntityMappingType getMutatingEntityDescriptor() {
|
||||||
|
return mutatingEntityDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableGroup getMutatingTableGroup() {
|
||||||
|
return mutatingTableGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stack<SqlAstProcessingState> getProcessingStateStack() {
|
||||||
|
return super.getProcessingStateStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized hook to visit the assignments defined by the update SQM allow
|
||||||
|
* "listening" for each SQL assignment.
|
||||||
|
*/
|
||||||
|
public void visitSetClause(
|
||||||
|
SqmSetClause setClause,
|
||||||
|
Consumer<Assignment> assignmentConsumer,
|
||||||
|
BiConsumer<SqmParameter, List<JdbcParameter>> parameterResolutionConsumer) {
|
||||||
|
this.parameterResolutionConsumer = parameterResolutionConsumer;
|
||||||
|
|
||||||
|
for ( SqmAssignment assignment : setClause.getAssignments() ) {
|
||||||
|
visitAssignment( assignment, assignmentConsumer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Assignment> visitSetClause(SqmSetClause setClause) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
private void visitAssignment(
|
||||||
|
SqmAssignment sqmAssignment,
|
||||||
|
Consumer<Assignment> assignmentConsumer) {
|
||||||
|
final Assignable assignable = (Assignable) sqmAssignment.getTargetPath().accept( this );
|
||||||
|
|
||||||
|
final Expression value = (Expression) sqmAssignment.getValue().accept( this );
|
||||||
|
|
||||||
|
assignmentConsumer.accept( new Assignment( assignable, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Assignment visitAssignment(SqmAssignment sqmAssignment) {
|
||||||
|
return new Assignment(
|
||||||
|
(Assignable) sqmAssignment.getTargetPath().accept( this ),
|
||||||
|
(Expression) sqmAssignment.getValue().accept( this )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Predicate visitWhereClause(
|
||||||
|
SqmWhereClause sqmWhereClause,
|
||||||
|
Consumer<ColumnReference> restrictionColumnReferenceConsumer,
|
||||||
|
BiConsumer<SqmParameter, List<JdbcParameter>> parameterResolutionConsumer) {
|
||||||
|
this.parameterResolutionConsumer = parameterResolutionConsumer;
|
||||||
|
|
||||||
|
if ( sqmWhereClause == null || sqmWhereClause.getPredicate() == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SqlAstProcessingState rootProcessingState = getProcessingStateStack().getCurrent();
|
||||||
|
final SqlAstProcessingStateImpl restrictionProcessingState = new SqlAstProcessingStateImpl(
|
||||||
|
rootProcessingState,
|
||||||
|
this,
|
||||||
|
getCurrentClauseStack()::getCurrent
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public SqlExpressionResolver getSqlExpressionResolver() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression resolveSqlExpression(
|
||||||
|
String key, Function<SqlAstProcessingState, Expression> creator) {
|
||||||
|
final Expression expression = rootProcessingState.getSqlExpressionResolver().resolveSqlExpression(
|
||||||
|
key,
|
||||||
|
creator
|
||||||
|
);
|
||||||
|
if ( expression instanceof ColumnReference ) {
|
||||||
|
restrictionColumnReferenceConsumer.accept( (ColumnReference) expression );
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getProcessingStateStack().push( restrictionProcessingState );
|
||||||
|
try {
|
||||||
|
return (Predicate) sqmWhereClause.getPredicate().accept( this );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
getProcessingStateStack().pop();
|
||||||
|
this.parameterResolutionConsumer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Predicate visitWhereClause(SqmWhereClause whereClause) {
|
||||||
|
return (Predicate) super.visitWhereClause( whereClause );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Expression consumeSqmParameter(SqmParameter sqmParameter) {
|
||||||
|
assert parameterResolutionConsumer != null;
|
||||||
|
|
||||||
|
final Expression expression = super.consumeSqmParameter( sqmParameter );
|
||||||
|
|
||||||
|
final List<JdbcParameter> jdbcParameters = getJdbcParamsBySqmParam().get( sqmParameter );
|
||||||
|
parameterResolutionConsumer.accept( sqmParameter, jdbcParameters );
|
||||||
|
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlAstCreationState getSqlAstCreationState() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitSelectClause(
|
||||||
|
SqmSelectClause sqmSelectClause,
|
||||||
|
QuerySpec sqlQuerySpec,
|
||||||
|
Consumer<ColumnReference> columnReferenceConsumer,
|
||||||
|
BiConsumer<SqmParameter, List<JdbcParameter>> parameterResolutionConsumer) {
|
||||||
|
assert sqmSelectClause != null;
|
||||||
|
|
||||||
|
this.parameterResolutionConsumer = parameterResolutionConsumer;
|
||||||
|
|
||||||
|
final SqlAstProcessingState rootProcessingState = getProcessingStateStack().getCurrent();
|
||||||
|
final SqlAstProcessingStateImpl processingState = new SqlAstQuerySpecProcessingStateImpl(
|
||||||
|
sqlQuerySpec,
|
||||||
|
rootProcessingState,
|
||||||
|
this,
|
||||||
|
getCurrentClauseStack()::getCurrent
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public SqlExpressionResolver getSqlExpressionResolver() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression resolveSqlExpression(
|
||||||
|
String key, Function<SqlAstProcessingState, Expression> creator) {
|
||||||
|
final Expression expression = rootProcessingState.getSqlExpressionResolver().resolveSqlExpression(
|
||||||
|
key,
|
||||||
|
creator
|
||||||
|
);
|
||||||
|
if ( expression instanceof ColumnReference ) {
|
||||||
|
columnReferenceConsumer.accept( (ColumnReference) expression );
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getProcessingStateStack().push( processingState );
|
||||||
|
try {
|
||||||
|
for ( int i = 0; i < sqmSelectClause.getSelectionItems().size(); i++ ) {
|
||||||
|
final DomainResultProducer domainResultProducer = (DomainResultProducer) sqmSelectClause.getSelectionItems()
|
||||||
|
.get( i )
|
||||||
|
.accept( this );
|
||||||
|
domainResultProducer.applySqlSelections( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
getProcessingStateStack().pop();
|
||||||
|
this.parameterResolutionConsumer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,553 @@
|
||||||
|
/*
|
||||||
|
* 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.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||||
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Joinable;
|
||||||
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
|
import org.hibernate.query.spi.QueryParameterBindings;
|
||||||
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
|
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||||
|
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
|
||||||
|
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
|
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
|
||||||
|
import org.hibernate.sql.ast.JoinType;
|
||||||
|
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstProcessingState;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcDelete;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandler.ExecutionDelegate {
|
||||||
|
private static final Logger log = Logger.getLogger( RestrictedDeleteExecutionDelegate.class );
|
||||||
|
|
||||||
|
private final EntityMappingType entityDescriptor;
|
||||||
|
private final IdTable idTable;
|
||||||
|
private final SqmDeleteStatement sqmDelete;
|
||||||
|
private final DomainParameterXref domainParameterXref;
|
||||||
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
|
|
||||||
|
private final BeforeUseAction beforeUseAction;
|
||||||
|
private final AfterUseAction afterUseAction;
|
||||||
|
private final TempTableDdlTransactionHandling ddlTransactionHandling;
|
||||||
|
private final Supplier<IdTableExporter> idTableExporterAccess;
|
||||||
|
|
||||||
|
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
|
||||||
|
|
||||||
|
public RestrictedDeleteExecutionDelegate(
|
||||||
|
EntityMappingType entityDescriptor,
|
||||||
|
IdTable idTable,
|
||||||
|
SqmDeleteStatement sqmDelete,
|
||||||
|
DomainParameterXref domainParameterXref,
|
||||||
|
BeforeUseAction beforeUseAction,
|
||||||
|
AfterUseAction afterUseAction,
|
||||||
|
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||||
|
Supplier<IdTableExporter> idTableExporterAccess,
|
||||||
|
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
this.entityDescriptor = entityDescriptor;
|
||||||
|
this.idTable = idTable;
|
||||||
|
this.sqmDelete = sqmDelete;
|
||||||
|
this.domainParameterXref = domainParameterXref;
|
||||||
|
this.beforeUseAction = beforeUseAction;
|
||||||
|
this.afterUseAction = afterUseAction;
|
||||||
|
this.ddlTransactionHandling = ddlTransactionHandling;
|
||||||
|
this.idTableExporterAccess = idTableExporterAccess;
|
||||||
|
this.sessionUidAccess = sessionUidAccess;
|
||||||
|
this.sessionFactory = sessionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execute(ExecutionContext executionContext) {
|
||||||
|
final Converter converter = new Converter(
|
||||||
|
sessionFactory,
|
||||||
|
executionContext.getQueryOptions(),
|
||||||
|
domainParameterXref,
|
||||||
|
executionContext.getQueryParameterBindings()
|
||||||
|
);
|
||||||
|
|
||||||
|
final SqlAstProcessingStateImpl rootProcessingState = new SqlAstProcessingStateImpl(
|
||||||
|
null,
|
||||||
|
converter,
|
||||||
|
converter.getCurrentClauseStack()::getCurrent
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public Expression resolveSqlExpression(
|
||||||
|
String key, Function<SqlAstProcessingState, Expression> creator) {
|
||||||
|
return super.resolveSqlExpression( key, creator );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
converter.getProcessingStateStack().push( rootProcessingState );
|
||||||
|
|
||||||
|
final EntityPersister entityDescriptor = sessionFactory.getDomainModel().getEntityDescriptor( sqmDelete.getTarget().getEntityName() );
|
||||||
|
final String hierarchyRootTableName = ( (Joinable) entityDescriptor ).getTableName();
|
||||||
|
final NavigablePath navigablePath = new NavigablePath( entityDescriptor.getEntityName() );
|
||||||
|
final TableGroup deletingTableGroup = entityDescriptor.createRootTableGroup(
|
||||||
|
navigablePath,
|
||||||
|
null,
|
||||||
|
JoinType.LEFT,
|
||||||
|
LockMode.PESSIMISTIC_WRITE,
|
||||||
|
converter.getSqlAliasBaseGenerator(),
|
||||||
|
converter.getSqlExpressionResolver(),
|
||||||
|
() -> predicate -> {},
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
|
||||||
|
// because this is a multi-table update, here we expect multiple TableReferences
|
||||||
|
assert !deletingTableGroup.getTableReferenceJoins().isEmpty();
|
||||||
|
|
||||||
|
// Register this TableGroup with the "registry" in preparation for
|
||||||
|
converter.getFromClauseAccess().registerTableGroup( navigablePath, deletingTableGroup );
|
||||||
|
|
||||||
|
final TableReference hierarchyRootTableReference = deletingTableGroup.resolveTableReference( hierarchyRootTableName );
|
||||||
|
assert hierarchyRootTableReference != null;
|
||||||
|
|
||||||
|
// Use the converter to interpret the where-clause. We do this for 2 reasons:
|
||||||
|
// 1) the resolved Predicate is ultimately the base for applying restriction to the deletes
|
||||||
|
// 2) we also inspect each ColumnReference that is part of the where-clause to see which
|
||||||
|
// table it comes from. if all of the referenced columns (if any at all) are from the root table
|
||||||
|
// we can perform all of the deletes without using an id-table
|
||||||
|
final AtomicBoolean needsIdTableWrapper = new AtomicBoolean( false );
|
||||||
|
final Predicate predicate = converter.visitWhereClause(
|
||||||
|
sqmDelete.getWhereClause(),
|
||||||
|
columnReference -> {
|
||||||
|
if ( ! hierarchyRootTableReference.getIdentificationVariable().equals( columnReference.getQualifier() ) ) {
|
||||||
|
needsIdTableWrapper.set( true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
boolean needsIdTable = needsIdTableWrapper.get();
|
||||||
|
|
||||||
|
if ( needsIdTable ) {
|
||||||
|
return executeWithIdTable(
|
||||||
|
predicate,
|
||||||
|
deletingTableGroup,
|
||||||
|
converter.getRestrictionSqmParameterResolutions(),
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return executeWithoutIdTable(
|
||||||
|
predicate,
|
||||||
|
deletingTableGroup,
|
||||||
|
converter.getRestrictionSqmParameterResolutions(),
|
||||||
|
converter.getSqlExpressionResolver(),
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeWithoutIdTable(
|
||||||
|
Predicate suppliedPredicate,
|
||||||
|
TableGroup tableGroup,
|
||||||
|
Map<SqmParameter, List<JdbcParameter>> restrictionSqmParameterResolutions,
|
||||||
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
final EntityPersister rootEntityPersister;
|
||||||
|
final String rootEntityName = entityDescriptor.getEntityPersister().getRootEntityName();
|
||||||
|
if ( rootEntityName.equals( entityDescriptor.getEntityName() ) ) {
|
||||||
|
rootEntityPersister = entityDescriptor.getEntityPersister();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rootEntityPersister = sessionFactory.getDomainModel().findEntityDescriptor( rootEntityName );
|
||||||
|
}
|
||||||
|
|
||||||
|
final AtomicInteger rows = new AtomicInteger();
|
||||||
|
|
||||||
|
final String rootTableName = ( (Joinable) rootEntityPersister ).getTableName();
|
||||||
|
final TableReference rootTableReference = tableGroup.resolveTableReference( rootTableName );
|
||||||
|
|
||||||
|
final QuerySpec matchingIdSubQuerySpec = ExecuteWithoutIdTableHelper.createIdMatchingSubQuerySpec(
|
||||||
|
tableGroup.getNavigablePath(),
|
||||||
|
rootTableReference,
|
||||||
|
suppliedPredicate,
|
||||||
|
rootEntityPersister,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
|
||||||
|
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||||
|
executionContext.getQueryParameterBindings(),
|
||||||
|
domainParameterXref,
|
||||||
|
SqmUtil.generateJdbcParamsXref(
|
||||||
|
domainParameterXref,
|
||||||
|
() -> restrictionSqmParameterResolutions
|
||||||
|
),
|
||||||
|
sessionFactory.getDomainModel(),
|
||||||
|
navigablePath -> tableGroup,
|
||||||
|
executionContext.getSession()
|
||||||
|
);
|
||||||
|
|
||||||
|
entityDescriptor.visitConstraintOrderedTables(
|
||||||
|
(tableExpression, tableKeyColumnVisitationSupplier) -> {
|
||||||
|
if ( tableExpression.equals( rootTableName ) ) {
|
||||||
|
rows.set(
|
||||||
|
deleteFromRootTableWithoutIdTable(
|
||||||
|
rootTableReference,
|
||||||
|
suppliedPredicate,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deleteFromNonRootTableWithoutIdTable(
|
||||||
|
tableGroup.resolveTableReference( tableExpression ),
|
||||||
|
tableKeyColumnVisitationSupplier,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
tableGroup,
|
||||||
|
matchingIdSubQuerySpec,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return rows.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int deleteFromRootTableWithoutIdTable(
|
||||||
|
TableReference rootTableReference,
|
||||||
|
Predicate predicate,
|
||||||
|
JdbcParameterBindings jdbcParameterBindings,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
return executeSqlDelete(
|
||||||
|
new DeleteStatement( rootTableReference, predicate ),
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteFromNonRootTableWithoutIdTable(
|
||||||
|
TableReference targetTableReference,
|
||||||
|
Supplier<Consumer<ColumnConsumer>> tableKeyColumnVisitationSupplier,
|
||||||
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
|
TableGroup rootTableGroup,
|
||||||
|
QuerySpec matchingIdSubQuerySpec,
|
||||||
|
JdbcParameterBindings jdbcParameterBindings,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
assert targetTableReference != null;
|
||||||
|
log.trace( "deleteFromNonRootTable - " + targetTableReference.getTableExpression() );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* delete from sub_table
|
||||||
|
* where sub_id in (
|
||||||
|
* select root_id from root_table
|
||||||
|
* where {predicate}
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the `sub_id` reference as the LHS of the in-subquery predicate
|
||||||
|
*/
|
||||||
|
final List<ColumnReference> deletingTableColumnRefs = new ArrayList<>();
|
||||||
|
tableKeyColumnVisitationSupplier.get().accept(
|
||||||
|
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||||
|
assert targetTableReference.getTableExpression().equals( containingTableExpression );
|
||||||
|
|
||||||
|
final Expression expression = sqlExpressionResolver.resolveSqlExpression(
|
||||||
|
SqlExpressionResolver.createColumnReferenceKey( targetTableReference, columnExpression ),
|
||||||
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
|
rootTableGroup.getPrimaryTableReference(),
|
||||||
|
columnExpression,
|
||||||
|
jdbcMapping,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
deletingTableColumnRefs.add( (ColumnReference) expression );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
final Expression deletingTableColumnRefsExpression;
|
||||||
|
if ( deletingTableColumnRefs.size() == 1 ) {
|
||||||
|
deletingTableColumnRefsExpression = deletingTableColumnRefs.get( 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deletingTableColumnRefsExpression = new SqlTuple( deletingTableColumnRefs, entityDescriptor.getIdentifierMapping() );
|
||||||
|
}
|
||||||
|
|
||||||
|
final InSubQueryPredicate idMatchPredicate = new InSubQueryPredicate(
|
||||||
|
deletingTableColumnRefsExpression,
|
||||||
|
matchingIdSubQuerySpec,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
final DeleteStatement sqlAstDelete = new DeleteStatement( targetTableReference, idMatchPredicate );
|
||||||
|
final int rows = executeSqlDelete(
|
||||||
|
sqlAstDelete,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
log.debugf( "deleteFromNonRootTable - `%s` : %s rows", targetTableReference, rows );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int executeSqlDelete(
|
||||||
|
DeleteStatement sqlAst,
|
||||||
|
JdbcParameterBindings jdbcParameterBindings,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||||
|
|
||||||
|
final JdbcServices jdbcServices = factory.getJdbcServices();
|
||||||
|
|
||||||
|
final SqlAstDeleteTranslator sqlAstTranslator = jdbcServices.getJdbcEnvironment()
|
||||||
|
.getSqlAstTranslatorFactory()
|
||||||
|
.buildDeleteTranslator( factory );
|
||||||
|
final JdbcDelete jdbcDelete = sqlAstTranslator.translate( sqlAst );
|
||||||
|
|
||||||
|
return jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
|
jdbcDelete,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
sql -> executionContext.getSession()
|
||||||
|
.getJdbcCoordinator()
|
||||||
|
.getStatementPreparer()
|
||||||
|
.prepareStatement( sql ),
|
||||||
|
(integer, preparedStatement) -> {},
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeWithIdTable(
|
||||||
|
Predicate predicate,
|
||||||
|
TableGroup deletingTableGroup,
|
||||||
|
Map<SqmParameter, List<JdbcParameter>> restrictionSqmParameterResolutions,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||||
|
executionContext.getQueryParameterBindings(),
|
||||||
|
domainParameterXref,
|
||||||
|
SqmUtil.generateJdbcParamsXref(
|
||||||
|
domainParameterXref,
|
||||||
|
() -> restrictionSqmParameterResolutions
|
||||||
|
),
|
||||||
|
sessionFactory.getDomainModel(),
|
||||||
|
navigablePath -> deletingTableGroup,
|
||||||
|
executionContext.getSession()
|
||||||
|
);
|
||||||
|
|
||||||
|
ExecuteWithIdTableHelper.performBeforeIdTableUseActions(
|
||||||
|
beforeUseAction,
|
||||||
|
idTable,
|
||||||
|
idTableExporterAccess,
|
||||||
|
ddlTransactionHandling,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return executeUsingIdTable( predicate, executionContext, jdbcParameterBindings );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ExecuteWithIdTableHelper.performAfterIdTableUseActions(
|
||||||
|
afterUseAction,
|
||||||
|
idTable,
|
||||||
|
idTableExporterAccess,
|
||||||
|
ddlTransactionHandling,
|
||||||
|
sessionUidAccess,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeUsingIdTable(
|
||||||
|
Predicate predicate,
|
||||||
|
ExecutionContext executionContext,
|
||||||
|
JdbcParameterBindings jdbcParameterBindings) {
|
||||||
|
final int rows = ExecuteWithIdTableHelper.saveMatchingIdsIntoIdTable(
|
||||||
|
sqmDelete,
|
||||||
|
predicate,
|
||||||
|
idTable,
|
||||||
|
sessionUidAccess,
|
||||||
|
domainParameterXref,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
|
final QuerySpec idTableSubQuery = ExecuteWithIdTableHelper.createIdTableSelectQuerySpec(
|
||||||
|
idTable,
|
||||||
|
sessionUidAccess,
|
||||||
|
entityDescriptor,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
|
entityDescriptor.visitConstraintOrderedTables(
|
||||||
|
(tableExpression, tableKeyColumnVisitationSupplier) -> deleteFromTableUsingIdTable(
|
||||||
|
tableExpression,
|
||||||
|
tableKeyColumnVisitationSupplier,
|
||||||
|
idTableSubQuery,
|
||||||
|
executionContext
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteFromTableUsingIdTable(
|
||||||
|
String tableExpression,
|
||||||
|
Supplier<Consumer<ColumnConsumer>> tableKeyColumnVisitationSupplier,
|
||||||
|
QuerySpec idTableSubQuery,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
log.trace( "deleteFromTableUsingIdTable - " + tableExpression );
|
||||||
|
|
||||||
|
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||||
|
|
||||||
|
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( entityDescriptor );
|
||||||
|
|
||||||
|
tableKeyColumnVisitationSupplier.get().accept(
|
||||||
|
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||||
|
assert containingTableExpression.equals( tableExpression );
|
||||||
|
keyColumnCollector.apply(
|
||||||
|
new ColumnReference(
|
||||||
|
(String) null,
|
||||||
|
columnExpression,
|
||||||
|
jdbcMapping,
|
||||||
|
factory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
final InSubQueryPredicate predicate = new InSubQueryPredicate(
|
||||||
|
keyColumnCollector.buildKeyExpression(),
|
||||||
|
idTableSubQuery,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
executeSqlDelete(
|
||||||
|
new DeleteStatement(
|
||||||
|
new TableReference( tableExpression, null, true, factory ),
|
||||||
|
predicate
|
||||||
|
),
|
||||||
|
JdbcParameterBindings.NO_BINDINGS,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static class Converter extends BaseSqmToSqlAstConverter {
|
||||||
|
private Map<SqmParameter,List<JdbcParameter>> restrictionSqmParameterResolutions;
|
||||||
|
|
||||||
|
private BiConsumer<SqmParameter,List<JdbcParameter>> sqmParamResolutionConsumer;
|
||||||
|
|
||||||
|
Converter(
|
||||||
|
SqlAstCreationContext creationContext,
|
||||||
|
QueryOptions queryOptions,
|
||||||
|
DomainParameterXref domainParameterXref,
|
||||||
|
QueryParameterBindings domainParameterBindings) {
|
||||||
|
super( creationContext, queryOptions, domainParameterXref, domainParameterBindings );
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<SqmParameter, List<JdbcParameter>> getRestrictionSqmParameterResolutions() {
|
||||||
|
return restrictionSqmParameterResolutions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stack<SqlAstProcessingState> getProcessingStateStack() {
|
||||||
|
return super.getProcessingStateStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Predicate visitWhereClause(SqmWhereClause sqmWhereClause, Consumer<ColumnReference> restrictionColumnReferenceConsumer) {
|
||||||
|
if ( sqmWhereClause == null || sqmWhereClause.getPredicate() == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqmParamResolutionConsumer = (sqm, jdbcs) -> {
|
||||||
|
if ( restrictionSqmParameterResolutions == null ) {
|
||||||
|
restrictionSqmParameterResolutions = new IdentityHashMap<>();
|
||||||
|
}
|
||||||
|
restrictionSqmParameterResolutions.put( sqm, jdbcs );
|
||||||
|
};
|
||||||
|
|
||||||
|
final SqlAstProcessingState rootProcessingState = getProcessingStateStack().getCurrent();
|
||||||
|
final SqlAstProcessingStateImpl restrictionProcessingState = new SqlAstProcessingStateImpl(
|
||||||
|
rootProcessingState,
|
||||||
|
this,
|
||||||
|
getCurrentClauseStack()::getCurrent
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public SqlExpressionResolver getSqlExpressionResolver() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression resolveSqlExpression(
|
||||||
|
String key, Function<SqlAstProcessingState, Expression> creator) {
|
||||||
|
final Expression expression = rootProcessingState.getSqlExpressionResolver().resolveSqlExpression( key, creator );
|
||||||
|
if ( expression instanceof ColumnReference ) {
|
||||||
|
restrictionColumnReferenceConsumer.accept( (ColumnReference) expression );
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getProcessingStateStack().push( restrictionProcessingState );
|
||||||
|
try {
|
||||||
|
return (Predicate) sqmWhereClause.getPredicate().accept( this );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
getProcessingStateStack().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Expression consumeSqmParameter(SqmParameter sqmParameter) {
|
||||||
|
final Expression expression = super.consumeSqmParameter( sqmParameter );
|
||||||
|
final List<JdbcParameter> jdbcParameters = getJdbcParamsBySqmParam().get( sqmParameter );
|
||||||
|
sqmParamResolutionConsumer.accept( sqmParameter, jdbcParameters );
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlExpressionResolver getSqlExpressionResolver() {
|
||||||
|
return super.getSqlExpressionResolver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,33 +6,17 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.mutation.internal.idtable;
|
package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.function.Function;
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler;
|
||||||
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
||||||
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
||||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||||
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
|
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
|
||||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
import org.hibernate.sql.exec.spi.JdbcDelete;
|
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ -40,31 +24,68 @@ import org.jboss.logging.Logger;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class TableBasedDeleteHandler
|
public class TableBasedDeleteHandler
|
||||||
extends AbstractTableBasedHandler
|
extends AbstractMutationHandler
|
||||||
implements DeleteHandler {
|
implements DeleteHandler {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger( TableBasedDeleteHandler.class );
|
private static final Logger log = Logger.getLogger( TableBasedDeleteHandler.class );
|
||||||
|
|
||||||
|
public interface ExecutionDelegate {
|
||||||
|
int execute(ExecutionContext executionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final IdTable idTable;
|
||||||
|
private final TempTableDdlTransactionHandling ddlTransactionHandling;
|
||||||
|
private final BeforeUseAction beforeUseAction;
|
||||||
|
private final AfterUseAction afterUseAction;
|
||||||
|
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
|
||||||
|
private final Supplier<IdTableExporter> exporterSupplier;
|
||||||
|
|
||||||
|
private final DomainParameterXref domainParameterXref;
|
||||||
|
|
||||||
|
|
||||||
public TableBasedDeleteHandler(
|
public TableBasedDeleteHandler(
|
||||||
SqmDeleteStatement sqmDeleteStatement,
|
SqmDeleteStatement sqmDeleteStatement,
|
||||||
IdTable idTable,
|
DomainParameterXref domainParameterXref, IdTable idTable,
|
||||||
|
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||||
Supplier<IdTableExporter> exporterSupplier,
|
Supplier<IdTableExporter> exporterSupplier,
|
||||||
BeforeUseAction beforeUseAction,
|
BeforeUseAction beforeUseAction,
|
||||||
AfterUseAction afterUseAction,
|
AfterUseAction afterUseAction,
|
||||||
TempTableDdlTransactionHandling transactionality,
|
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||||
DomainParameterXref domainParameterXref,
|
|
||||||
HandlerCreationContext creationContext) {
|
HandlerCreationContext creationContext) {
|
||||||
super(
|
super( sqmDeleteStatement, creationContext );
|
||||||
sqmDeleteStatement,
|
this.idTable = idTable;
|
||||||
|
this.ddlTransactionHandling = ddlTransactionHandling;
|
||||||
|
this.beforeUseAction = beforeUseAction;
|
||||||
|
this.afterUseAction = afterUseAction;
|
||||||
|
|
||||||
|
this.domainParameterXref = domainParameterXref;
|
||||||
|
|
||||||
|
this.sessionUidAccess = sessionUidAccess;
|
||||||
|
this.exporterSupplier = exporterSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execute(ExecutionContext executionContext) {
|
||||||
|
log.tracef( "Starting multi-table delete execution - %s", getSqmDeleteOrUpdateStatement().getRoot().getModel().getName() );
|
||||||
|
return resolveDelegate( executionContext ).execute( executionContext );
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExecutionDelegate resolveDelegate(ExecutionContext executionContext) {
|
||||||
|
if ( getSqmDeleteOrUpdateStatement().getWhereClause() == null
|
||||||
|
|| getSqmDeleteOrUpdateStatement().getWhereClause().getPredicate() == null ) {
|
||||||
|
return new UnrestrictedDeleteExecutionDelegate( getEntityDescriptor() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RestrictedDeleteExecutionDelegate(
|
||||||
|
getEntityDescriptor(),
|
||||||
idTable,
|
idTable,
|
||||||
transactionality,
|
getSqmDeleteOrUpdateStatement(),
|
||||||
domainParameterXref,
|
domainParameterXref,
|
||||||
beforeUseAction,
|
beforeUseAction,
|
||||||
afterUseAction,
|
afterUseAction,
|
||||||
session -> session.getSessionIdentifier().toString(),
|
ddlTransactionHandling,
|
||||||
exporterSupplier,
|
exporterSupplier,
|
||||||
creationContext
|
sessionUidAccess,
|
||||||
|
getSessionFactory()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,110 +93,4 @@ public class TableBasedDeleteHandler
|
||||||
public SqmDeleteStatement getSqmDeleteOrUpdateStatement() {
|
public SqmDeleteStatement getSqmDeleteOrUpdateStatement() {
|
||||||
return (SqmDeleteStatement) super.getSqmDeleteOrUpdateStatement();
|
return (SqmDeleteStatement) super.getSqmDeleteOrUpdateStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void performMutations(ExecutionContext executionContext) {
|
|
||||||
log.trace( "performMutations - " + getEntityDescriptor().getEntityName() );
|
|
||||||
|
|
||||||
// create the selection of "matching ids" from the id-table. this is used as the subquery in
|
|
||||||
// used to restrict the deletions from each table
|
|
||||||
final QuerySpec idTableSelectSubQuerySpec = createIdTableSubQuery( executionContext );
|
|
||||||
|
|
||||||
getEntityDescriptor().visitConstraintOrderedTables(
|
|
||||||
(tableExpression, tableKeyColumnsVisitationSupplier) -> {
|
|
||||||
deleteFrom( tableExpression, tableKeyColumnsVisitationSupplier, idTableSelectSubQuerySpec, executionContext );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TableKeyExpressionCollector {
|
|
||||||
private final EntityMappingType entityMappingType;
|
|
||||||
|
|
||||||
public TableKeyExpressionCollector(EntityMappingType entityMappingType) {
|
|
||||||
this.entityMappingType = entityMappingType;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression firstColumnExpression;
|
|
||||||
List<Expression> collectedColumnExpressions;
|
|
||||||
|
|
||||||
void apply(ColumnReference columnReference) {
|
|
||||||
if ( firstColumnExpression == null ) {
|
|
||||||
firstColumnExpression = columnReference;
|
|
||||||
}
|
|
||||||
else if ( collectedColumnExpressions == null ) {
|
|
||||||
collectedColumnExpressions = new ArrayList<>();
|
|
||||||
collectedColumnExpressions.add( firstColumnExpression );
|
|
||||||
collectedColumnExpressions.add( columnReference );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
collectedColumnExpressions.add( columnReference );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression buildKeyExpression() {
|
|
||||||
if ( collectedColumnExpressions == null ) {
|
|
||||||
return firstColumnExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SqlTuple( collectedColumnExpressions, entityMappingType.getIdentifierMapping() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteFrom(
|
|
||||||
String tableExpression,
|
|
||||||
Supplier<Consumer<ColumnConsumer>> tableKeyColumnVisitationSupplier,
|
|
||||||
QuerySpec idTableSelectSubQuery,
|
|
||||||
ExecutionContext executionContext) {
|
|
||||||
log.trace( "deleteFrom - " + tableExpression );
|
|
||||||
|
|
||||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
|
||||||
|
|
||||||
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( getEntityDescriptor() );
|
|
||||||
|
|
||||||
tableKeyColumnVisitationSupplier.get().accept(
|
|
||||||
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
|
||||||
assert containingTableExpression.equals( tableExpression );
|
|
||||||
keyColumnCollector.apply(
|
|
||||||
new ColumnReference(
|
|
||||||
(String) null,
|
|
||||||
columnExpression,
|
|
||||||
jdbcMapping,
|
|
||||||
factory
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
final InSubQueryPredicate predicate = new InSubQueryPredicate(
|
|
||||||
keyColumnCollector.buildKeyExpression(),
|
|
||||||
idTableSelectSubQuery,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
final DeleteStatement deleteStatement = new DeleteStatement(
|
|
||||||
new TableReference( tableExpression, null, true, factory ),
|
|
||||||
predicate
|
|
||||||
);
|
|
||||||
|
|
||||||
final JdbcServices jdbcServices = factory.getJdbcServices();
|
|
||||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
|
||||||
|
|
||||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
|
||||||
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory );
|
|
||||||
final JdbcDelete jdbcDelete = sqlAstTranslator.translate( deleteStatement );
|
|
||||||
|
|
||||||
final int rows = jdbcServices.getJdbcDeleteExecutor().execute(
|
|
||||||
jdbcDelete,
|
|
||||||
JdbcParameterBindings.NO_BINDINGS,
|
|
||||||
sql -> executionContext.getSession()
|
|
||||||
.getJdbcCoordinator()
|
|
||||||
.getStatementPreparer()
|
|
||||||
.prepareStatement( sql ),
|
|
||||||
(integer, preparedStatement) -> {
|
|
||||||
},
|
|
||||||
executionContext
|
|
||||||
);
|
|
||||||
|
|
||||||
log.debugf( "delete-from `%s` : %s rows", tableExpression, rows );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
/*
|
||||||
|
* 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.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.spi.DomainMetamodel;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Joinable;
|
||||||
|
import org.hibernate.query.NavigablePath;
|
||||||
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
|
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
|
||||||
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
|
import org.hibernate.sql.ast.JoinType;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstProcessingState;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class TableBasedUpdateHandler
|
||||||
|
extends AbstractMutationHandler
|
||||||
|
implements UpdateHandler {
|
||||||
|
private static final Logger log = Logger.getLogger( TableBasedUpdateHandler.class );
|
||||||
|
|
||||||
|
public interface ExecutionDelegate {
|
||||||
|
int execute(ExecutionContext executionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final IdTable idTable;
|
||||||
|
private final TempTableDdlTransactionHandling ddlTransactionHandling;
|
||||||
|
private final BeforeUseAction beforeUseAction;
|
||||||
|
private final AfterUseAction afterUseAction;
|
||||||
|
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
|
||||||
|
private final Supplier<IdTableExporter> exporterSupplier;
|
||||||
|
|
||||||
|
private final DomainParameterXref domainParameterXref;
|
||||||
|
|
||||||
|
TableBasedUpdateHandler(
|
||||||
|
SqmUpdateStatement sqmDeleteStatement,
|
||||||
|
DomainParameterXref domainParameterXref,
|
||||||
|
IdTable idTable,
|
||||||
|
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||||
|
Supplier<IdTableExporter> exporterSupplier,
|
||||||
|
BeforeUseAction beforeUseAction,
|
||||||
|
AfterUseAction afterUseAction,
|
||||||
|
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||||
|
HandlerCreationContext creationContext) {
|
||||||
|
super( sqmDeleteStatement, creationContext );
|
||||||
|
this.idTable = idTable;
|
||||||
|
this.exporterSupplier = exporterSupplier;
|
||||||
|
this.beforeUseAction = beforeUseAction;
|
||||||
|
this.afterUseAction = afterUseAction;
|
||||||
|
this.ddlTransactionHandling = ddlTransactionHandling;
|
||||||
|
this.sessionUidAccess = sessionUidAccess;
|
||||||
|
this.domainParameterXref = domainParameterXref;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SqmUpdateStatement getSqmUpdate() {
|
||||||
|
return getSqmDeleteOrUpdateStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmUpdateStatement getSqmDeleteOrUpdateStatement() {
|
||||||
|
return (SqmUpdateStatement) super.getSqmDeleteOrUpdateStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execute(ExecutionContext executionContext) {
|
||||||
|
log.tracef( "Starting multi-table update execution - %s", getSqmDeleteOrUpdateStatement().getRoot().getModel().getName() );
|
||||||
|
|
||||||
|
return resolveDelegate( executionContext ).execute( executionContext );
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExecutionDelegate resolveDelegate(ExecutionContext executionContext) {
|
||||||
|
final SessionFactoryImplementor sessionFactory = getSessionFactory();
|
||||||
|
final DomainMetamodel domainModel = sessionFactory.getDomainModel();
|
||||||
|
final EntityPersister entityDescriptor = domainModel.getEntityDescriptor( getSqmDeleteOrUpdateStatement().getTarget().getEntityName() );
|
||||||
|
|
||||||
|
final String rootEntityName = entityDescriptor.getRootEntityName();
|
||||||
|
final EntityPersister rootEntityDescriptor = domainModel.getEntityDescriptor( rootEntityName );
|
||||||
|
|
||||||
|
final String hierarchyRootTableName = ( (Joinable) rootEntityDescriptor ).getTableName();
|
||||||
|
|
||||||
|
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(
|
||||||
|
entityDescriptor,
|
||||||
|
domainParameterXref,
|
||||||
|
executionContext.getQueryOptions(),
|
||||||
|
executionContext.getQueryParameterBindings(),
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
|
||||||
|
final Stack<SqlAstProcessingState> converterProcessingStateStack = converterDelegate.getProcessingStateStack();
|
||||||
|
|
||||||
|
final SqlAstProcessingStateImpl rootProcessingState = new SqlAstProcessingStateImpl(
|
||||||
|
null,
|
||||||
|
converterDelegate,
|
||||||
|
converterDelegate.getCurrentClauseStack()::getCurrent
|
||||||
|
);
|
||||||
|
|
||||||
|
converterProcessingStateStack.push( rootProcessingState );
|
||||||
|
|
||||||
|
final NavigablePath navigablePath = new NavigablePath( entityDescriptor.getEntityName() );
|
||||||
|
final TableGroup updatingTableGroup = entityDescriptor.createRootTableGroup(
|
||||||
|
navigablePath,
|
||||||
|
null,
|
||||||
|
JoinType.LEFT,
|
||||||
|
LockMode.PESSIMISTIC_WRITE,
|
||||||
|
converterDelegate.getSqlAliasBaseGenerator(),
|
||||||
|
converterDelegate.getSqlExpressionResolver(),
|
||||||
|
() -> predicate -> {},
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
|
||||||
|
// because this is a multi-table update, here we expect multiple TableReferences
|
||||||
|
assert !updatingTableGroup.getTableReferenceJoins().isEmpty();
|
||||||
|
converterDelegate.getFromClauseAccess().registerTableGroup( navigablePath, updatingTableGroup );
|
||||||
|
|
||||||
|
final TableReference hierarchyRootTableReference = updatingTableGroup.resolveTableReference( hierarchyRootTableName );
|
||||||
|
assert hierarchyRootTableReference != null;
|
||||||
|
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// cross-reference the TableReference by alias. The TableGroup already
|
||||||
|
// cross-references it by name, bu the ColumnReference only has the alias
|
||||||
|
|
||||||
|
final Map<String, TableReference> tableReferenceByAlias = new HashMap<>( updatingTableGroup.getTableReferenceJoins().size() + 1 );
|
||||||
|
collectTableReference( updatingTableGroup.getPrimaryTableReference(), tableReferenceByAlias::put );
|
||||||
|
for ( int i = 0; i < updatingTableGroup.getTableReferenceJoins().size(); i++ ) {
|
||||||
|
collectTableReference( updatingTableGroup.getTableReferenceJoins().get( i ), tableReferenceByAlias::put );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final Map<SqmParameter,List<JdbcParameter>> parameterResolutions;
|
||||||
|
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
|
||||||
|
parameterResolutions = Collections.emptyMap();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parameterResolutions = new IdentityHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// visit the set-clause using our special converter, collecting
|
||||||
|
// information about the assignments
|
||||||
|
|
||||||
|
final List<Assignment> assignments = new ArrayList<>();
|
||||||
|
|
||||||
|
converterDelegate.visitSetClause(
|
||||||
|
getSqmDeleteOrUpdateStatement().getSetClause(),
|
||||||
|
assignments::add,
|
||||||
|
parameterResolutions::put
|
||||||
|
);
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// visit the where-clause using our special converter, collecting information
|
||||||
|
// about the restrictions
|
||||||
|
|
||||||
|
final Predicate predicate;
|
||||||
|
final SqmWhereClause whereClause = getSqmUpdate().getWhereClause();
|
||||||
|
if ( whereClause == null || whereClause.getPredicate() == null ) {
|
||||||
|
predicate = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
predicate = converterDelegate.visitWhereClause(
|
||||||
|
whereClause,
|
||||||
|
columnReference -> {},
|
||||||
|
parameterResolutions::put
|
||||||
|
);
|
||||||
|
assert predicate != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return new UpdateExecutionDelegate(
|
||||||
|
getSqmUpdate(),
|
||||||
|
converterDelegate,
|
||||||
|
idTable,
|
||||||
|
ddlTransactionHandling,
|
||||||
|
beforeUseAction,
|
||||||
|
afterUseAction,
|
||||||
|
sessionUidAccess,
|
||||||
|
exporterSupplier,
|
||||||
|
domainParameterXref,
|
||||||
|
updatingTableGroup,
|
||||||
|
hierarchyRootTableReference,
|
||||||
|
tableReferenceByAlias,
|
||||||
|
assignments,
|
||||||
|
predicate,
|
||||||
|
parameterResolutions,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void collectTableReference(
|
||||||
|
TableReference tableReference,
|
||||||
|
BiConsumer<String, TableReference> consumer) {
|
||||||
|
consumer.accept( tableReference.getIdentificationVariable(), tableReference );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectTableReference(
|
||||||
|
TableReferenceJoin tableReferenceJoin,
|
||||||
|
BiConsumer<String, TableReference> consumer) {
|
||||||
|
collectTableReference( tableReferenceJoin.getJoinedTableReference(), consumer );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
class TableKeyExpressionCollector {
|
||||||
|
private final EntityMappingType entityMappingType;
|
||||||
|
|
||||||
|
TableKeyExpressionCollector(EntityMappingType entityMappingType) {
|
||||||
|
this.entityMappingType = entityMappingType;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression firstColumnExpression;
|
||||||
|
List<Expression> collectedColumnExpressions;
|
||||||
|
|
||||||
|
void apply(ColumnReference columnReference) {
|
||||||
|
if ( firstColumnExpression == null ) {
|
||||||
|
firstColumnExpression = columnReference;
|
||||||
|
}
|
||||||
|
else if ( collectedColumnExpressions == null ) {
|
||||||
|
collectedColumnExpressions = new ArrayList<>();
|
||||||
|
collectedColumnExpressions.add( firstColumnExpression );
|
||||||
|
collectedColumnExpressions.add( columnReference );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
collectedColumnExpressions.add( columnReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression buildKeyExpression() {
|
||||||
|
if ( collectedColumnExpressions == null ) {
|
||||||
|
return firstColumnExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SqlTuple( collectedColumnExpressions, entityMappingType.getIdentifierMapping() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,17 +7,11 @@
|
||||||
package org.hibernate.query.sqm.mutation.internal.idtable;
|
package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
|
||||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
|
||||||
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
|
||||||
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
|
||||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
|
||||||
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
|
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
|
@ -29,39 +23,28 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class UnrestrictedTableBasedDeleteHandler extends TableBasedDeleteHandler implements DeleteHandler {
|
public class UnrestrictedDeleteExecutionDelegate implements TableBasedDeleteHandler.ExecutionDelegate {
|
||||||
public UnrestrictedTableBasedDeleteHandler(
|
private final EntityMappingType entityDescriptor;
|
||||||
SqmDeleteStatement sqmDeleteStatement,
|
|
||||||
IdTable idTable,
|
public UnrestrictedDeleteExecutionDelegate(EntityMappingType entityDescriptor) {
|
||||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
this.entityDescriptor = entityDescriptor;
|
||||||
DomainParameterXref domainParameterXref,
|
|
||||||
BeforeUseAction beforeUseAction,
|
|
||||||
AfterUseAction afterUseAction,
|
|
||||||
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
|
||||||
HandlerCreationContext creationContext) {
|
|
||||||
super(
|
|
||||||
sqmDeleteStatement,
|
|
||||||
idTable,
|
|
||||||
() -> null,
|
|
||||||
beforeUseAction,
|
|
||||||
afterUseAction,
|
|
||||||
ddlTransactionHandling,
|
|
||||||
domainParameterXref,
|
|
||||||
creationContext
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int execute(ExecutionContext executionContext) {
|
public int execute(ExecutionContext executionContext) {
|
||||||
final AtomicInteger rows = new AtomicInteger();
|
// NOTE : we want the number of rows returned from this method to be the number of rows deleted
|
||||||
|
// from the root table of the entity hierarchy, which happens to be the last table we
|
||||||
|
// will visit
|
||||||
|
final AtomicInteger result = new AtomicInteger();
|
||||||
|
|
||||||
getEntityDescriptor().visitConstraintOrderedTables(
|
entityDescriptor.visitConstraintOrderedTables(
|
||||||
(tableExpression, tableKeyColumnsVisitationSupplier) -> {
|
(tableExpression, tableKeyColumnsVisitationSupplier) -> {
|
||||||
rows.set( deleteFrom( tableExpression, executionContext ) );
|
final int rows = deleteFrom( tableExpression, executionContext );
|
||||||
|
result.set( rows );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return rows.get();
|
return result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int deleteFrom(
|
private int deleteFrom(
|
||||||
|
@ -81,7 +64,7 @@ public class UnrestrictedTableBasedDeleteHandler extends TableBasedDeleteHandler
|
||||||
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory );
|
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory );
|
||||||
final JdbcDelete jdbcDelete = sqlAstTranslator.translate( deleteStatement );
|
final JdbcDelete jdbcDelete = sqlAstTranslator.translate( deleteStatement );
|
||||||
|
|
||||||
return jdbcServices.getJdbcDeleteExecutor().execute(
|
return jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
jdbcDelete,
|
jdbcDelete,
|
||||||
JdbcParameterBindings.NO_BINDINGS,
|
JdbcParameterBindings.NO_BINDINGS,
|
||||||
sql -> executionContext.getSession()
|
sql -> executionContext.getSession()
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* 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.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||||
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
|
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
|
import org.hibernate.sql.ast.SqlAstUpdateTranslator;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcUpdate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class UpdateExecutionDelegate implements TableBasedUpdateHandler.ExecutionDelegate {
|
||||||
|
private final SqmUpdateStatement sqmUpdate;
|
||||||
|
private final MultiTableSqmMutationConverter sqmConverter;
|
||||||
|
private final IdTable idTable;
|
||||||
|
private final TempTableDdlTransactionHandling ddlTransactionHandling;
|
||||||
|
private final BeforeUseAction beforeUseAction;
|
||||||
|
private final AfterUseAction afterUseAction;
|
||||||
|
private final Function<SharedSessionContractImplementor, String> sessionUidAccess;
|
||||||
|
private final Supplier<IdTableExporter> idTableExporterAccess;
|
||||||
|
private final DomainParameterXref domainParameterXref;
|
||||||
|
private final TableGroup updatingTableGroup;
|
||||||
|
private final Predicate suppliedPredicate;
|
||||||
|
|
||||||
|
private final EntityMappingType entityDescriptor;
|
||||||
|
|
||||||
|
private final JdbcParameterBindings jdbcParameterBindings;
|
||||||
|
|
||||||
|
private final Map<TableReference, List<Assignment>> assignmentsByTable;
|
||||||
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
|
|
||||||
|
public UpdateExecutionDelegate(
|
||||||
|
SqmUpdateStatement sqmUpdate,
|
||||||
|
MultiTableSqmMutationConverter sqmConverter,
|
||||||
|
IdTable idTable,
|
||||||
|
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||||
|
BeforeUseAction beforeUseAction,
|
||||||
|
AfterUseAction afterUseAction,
|
||||||
|
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||||
|
Supplier<IdTableExporter> idTableExporterAccess,
|
||||||
|
DomainParameterXref domainParameterXref,
|
||||||
|
TableGroup updatingTableGroup,
|
||||||
|
TableReference hierarchyRootTableReference,
|
||||||
|
Map<String, TableReference> tableReferenceByAlias,
|
||||||
|
List<Assignment> assignments,
|
||||||
|
Predicate suppliedPredicate,
|
||||||
|
Map<SqmParameter, List<JdbcParameter>> parameterResolutions,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
this.sqmUpdate = sqmUpdate;
|
||||||
|
this.sqmConverter = sqmConverter;
|
||||||
|
this.idTable = idTable;
|
||||||
|
this.ddlTransactionHandling = ddlTransactionHandling;
|
||||||
|
this.beforeUseAction = beforeUseAction;
|
||||||
|
this.afterUseAction = afterUseAction;
|
||||||
|
this.sessionUidAccess = sessionUidAccess;
|
||||||
|
this.idTableExporterAccess = idTableExporterAccess;
|
||||||
|
this.domainParameterXref = domainParameterXref;
|
||||||
|
this.updatingTableGroup = updatingTableGroup;
|
||||||
|
this.suppliedPredicate = suppliedPredicate;
|
||||||
|
|
||||||
|
this.sessionFactory = executionContext.getSession().getFactory();
|
||||||
|
|
||||||
|
final ModelPartContainer updatingModelPart = updatingTableGroup.getModelPart();
|
||||||
|
assert updatingModelPart instanceof EntityMappingType;
|
||||||
|
|
||||||
|
this.entityDescriptor = (EntityMappingType) updatingModelPart;
|
||||||
|
|
||||||
|
this.assignmentsByTable = new HashMap<>( updatingTableGroup.getTableReferenceJoins().size() + 1 );
|
||||||
|
|
||||||
|
jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||||
|
executionContext.getQueryParameterBindings(),
|
||||||
|
domainParameterXref,
|
||||||
|
SqmUtil.generateJdbcParamsXref(
|
||||||
|
domainParameterXref,
|
||||||
|
() -> parameterResolutions
|
||||||
|
),
|
||||||
|
sessionFactory.getDomainModel(),
|
||||||
|
navigablePath -> updatingTableGroup,
|
||||||
|
executionContext.getSession()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// segment the assignments by table-reference
|
||||||
|
|
||||||
|
for ( int i = 0; i < assignments.size(); i++ ) {
|
||||||
|
final Assignment assignment = assignments.get( i );
|
||||||
|
final List<ColumnReference> assignmentColumnRefs = assignment.getAssignable().getColumnReferences();
|
||||||
|
|
||||||
|
TableReference assignmentTableReference = null;
|
||||||
|
|
||||||
|
for ( int c = 0; c < assignmentColumnRefs.size(); c++ ) {
|
||||||
|
final ColumnReference columnReference = assignmentColumnRefs.get( c );
|
||||||
|
final TableReference tableReference = resolveTableReference(
|
||||||
|
columnReference,
|
||||||
|
updatingTableGroup,
|
||||||
|
tableReferenceByAlias
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( assignmentTableReference != null && assignmentTableReference != tableReference ) {
|
||||||
|
throw new IllegalStateException( "Assignment referred to columns from multiple tables" );
|
||||||
|
}
|
||||||
|
|
||||||
|
assignmentTableReference = tableReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Assignment> assignmentsForTable = assignmentsByTable.get( assignmentTableReference );
|
||||||
|
if ( assignmentsForTable == null ) {
|
||||||
|
assignmentsForTable = new ArrayList<>();
|
||||||
|
assignmentsByTable.put( assignmentTableReference, assignmentsForTable );
|
||||||
|
}
|
||||||
|
assignmentsForTable.add( assignment );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execute(ExecutionContext executionContext) {
|
||||||
|
ExecuteWithIdTableHelper.performBeforeIdTableUseActions(
|
||||||
|
beforeUseAction,
|
||||||
|
idTable,
|
||||||
|
idTableExporterAccess,
|
||||||
|
ddlTransactionHandling,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
final int rows = ExecuteWithIdTableHelper.saveMatchingIdsIntoIdTable(
|
||||||
|
sqmUpdate,
|
||||||
|
sqmConverter,
|
||||||
|
updatingTableGroup,
|
||||||
|
suppliedPredicate,
|
||||||
|
idTable,
|
||||||
|
sessionUidAccess,
|
||||||
|
domainParameterXref,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
|
final QuerySpec idTableSubQuery = ExecuteWithIdTableHelper.createIdTableSelectQuerySpec(
|
||||||
|
idTable,
|
||||||
|
sessionUidAccess,
|
||||||
|
entityDescriptor,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
|
entityDescriptor.visitConstraintOrderedTables(
|
||||||
|
(tableExpression, tableKeyColumnVisitationSupplier) -> updateTable(
|
||||||
|
tableExpression,
|
||||||
|
tableKeyColumnVisitationSupplier,
|
||||||
|
idTableSubQuery,
|
||||||
|
executionContext
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ExecuteWithIdTableHelper.performAfterIdTableUseActions(
|
||||||
|
afterUseAction,
|
||||||
|
idTable,
|
||||||
|
idTableExporterAccess,
|
||||||
|
ddlTransactionHandling,
|
||||||
|
sessionUidAccess,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TableReference resolveTableReference(
|
||||||
|
ColumnReference columnReference,
|
||||||
|
TableGroup updatingTableGroup,
|
||||||
|
Map<String, TableReference> tableReferenceByAlias) {
|
||||||
|
final TableReference tableReferenceByQualifier = tableReferenceByAlias.get( columnReference.getQualifier() );
|
||||||
|
if ( tableReferenceByQualifier != null ) {
|
||||||
|
return tableReferenceByQualifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
final TableReference tableReferenceByName = updatingTableGroup.resolveTableReference( columnReference.getQualifier() );
|
||||||
|
if ( tableReferenceByName != null ) {
|
||||||
|
return tableReferenceByName;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException( "Could not resolve restricted column's table-reference" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTable(
|
||||||
|
String tableExpression,
|
||||||
|
Supplier<Consumer<ColumnConsumer>> tableKeyColumnVisitationSupplier,
|
||||||
|
QuerySpec idTableSubQuery,
|
||||||
|
ExecutionContext executionContext) {
|
||||||
|
final TableReference updatingTableReference = updatingTableGroup.resolveTableReference( tableExpression );
|
||||||
|
|
||||||
|
final List<Assignment> assignments = assignmentsByTable.get( updatingTableReference );
|
||||||
|
if ( assignments == null || assignments.isEmpty() ) {
|
||||||
|
// no assignments for this table - skip it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// create the in-subquery predicate to restrict the updates to just
|
||||||
|
// matching ids
|
||||||
|
|
||||||
|
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( entityDescriptor );
|
||||||
|
|
||||||
|
tableKeyColumnVisitationSupplier.get().accept(
|
||||||
|
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||||
|
assert containingTableExpression.equals( tableExpression );
|
||||||
|
keyColumnCollector.apply(
|
||||||
|
new ColumnReference(
|
||||||
|
(String) null,
|
||||||
|
columnExpression,
|
||||||
|
jdbcMapping,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
final InSubQueryPredicate idTableSubQueryPredicate = new InSubQueryPredicate(
|
||||||
|
keyColumnCollector.buildKeyExpression(),
|
||||||
|
idTableSubQuery,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// Create the SQL AST and convert it into a JdbcOperation
|
||||||
|
final UpdateStatement sqlAst = new UpdateStatement( updatingTableReference, assignments, idTableSubQueryPredicate );
|
||||||
|
|
||||||
|
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||||
|
final SqlAstUpdateTranslator sqlAstTranslator = jdbcServices.getJdbcEnvironment()
|
||||||
|
.getSqlAstTranslatorFactory()
|
||||||
|
.buildUpdateTranslator( sessionFactory );
|
||||||
|
|
||||||
|
final JdbcUpdate jdbcUpdate = sqlAstTranslator.translate( sqlAst );
|
||||||
|
|
||||||
|
jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
|
jdbcUpdate,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
sql -> executionContext.getSession()
|
||||||
|
.getJdbcCoordinator()
|
||||||
|
.getStatementPreparer()
|
||||||
|
.prepareStatement( sql ),
|
||||||
|
(integer, preparedStatement) -> {},
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.mutation.spi;
|
package org.hibernate.query.sqm.mutation.spi;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
|
||||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,13 +15,22 @@ import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractMutationHandler implements Handler {
|
public abstract class AbstractMutationHandler implements Handler {
|
||||||
private final SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement;
|
private final SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement;
|
||||||
private final HandlerCreationContext creationContext;
|
|
||||||
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
|
private final EntityMappingType entityDescriptor;
|
||||||
|
|
||||||
public AbstractMutationHandler(
|
public AbstractMutationHandler(
|
||||||
SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement,
|
SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement,
|
||||||
HandlerCreationContext creationContext) {
|
HandlerCreationContext creationContext) {
|
||||||
this.sqmDeleteOrUpdateStatement = sqmDeleteOrUpdateStatement;
|
this.sqmDeleteOrUpdateStatement = sqmDeleteOrUpdateStatement;
|
||||||
this.creationContext = creationContext;
|
this.sessionFactory = creationContext.getSessionFactory();
|
||||||
|
|
||||||
|
final String entityName = sqmDeleteOrUpdateStatement.getTarget()
|
||||||
|
.getReferencedPathSource()
|
||||||
|
.getHibernateEntityName();
|
||||||
|
|
||||||
|
this.entityDescriptor = sessionFactory.getMetamodel().getEntityDescriptor( entityName );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqmDeleteOrUpdateStatement getSqmDeleteOrUpdateStatement() {
|
public SqmDeleteOrUpdateStatement getSqmDeleteOrUpdateStatement() {
|
||||||
|
@ -29,15 +38,10 @@ public abstract class AbstractMutationHandler implements Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityMappingType getEntityDescriptor() {
|
public EntityMappingType getEntityDescriptor() {
|
||||||
final String entityName = sqmDeleteOrUpdateStatement.getTarget()
|
return entityDescriptor;
|
||||||
.getReferencedPathSource()
|
|
||||||
.getHibernateEntityName();
|
|
||||||
|
|
||||||
return creationContext.getSessionFactory().getMetamodel().getEntityDescriptor( entityName );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HandlerCreationContext getCreationContext() {
|
public SessionFactoryImplementor getSessionFactory() {
|
||||||
return creationContext;
|
return sessionFactory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -783,7 +783,7 @@ public abstract class BaseSqmToSqlAstConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Expression consumeSqmParameter(SqmParameter sqmParameter) {
|
protected Expression consumeSqmParameter(SqmParameter sqmParameter) {
|
||||||
final MappingModelExpressable valueMapping = determineValueMapping( sqmParameter );
|
final MappingModelExpressable valueMapping = determineValueMapping( sqmParameter );
|
||||||
final List<JdbcParameter> jdbcParametersForSqm = new ArrayList<>();
|
final List<JdbcParameter> jdbcParametersForSqm = new ArrayList<>();
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,13 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignable;
|
||||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface AssignableSqmPathInterpretation<T> extends SqmPathInterpretation<T> {
|
public interface AssignableSqmPathInterpretation<T> extends SqmPathInterpretation<T>, Assignable {
|
||||||
// need to be able to collect assignments per-table, including
|
// need to be able to collect assignments per-table, including
|
||||||
// SqmParameter -> JdbcParameter mapping
|
// SqmParameter -> JdbcParameter mapping
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.sql.internal;
|
package org.hibernate.query.sqm.sql.internal;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
|
@ -132,4 +134,14 @@ public class BasicValuedPathInterpretation<T> implements AssignableSqmPathInterp
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "BasicValuedPathInterpretation(" + sqmPath.getNavigablePath().getFullPath() + ')';
|
return "BasicValuedPathInterpretation(" + sqmPath.getNavigablePath().getFullPath() + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitColumnReferences(Consumer<ColumnReference> columnReferenceConsumer) {
|
||||||
|
columnReferenceConsumer.accept( columnReference );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ColumnReference> getColumnReferences() {
|
||||||
|
return Collections.singletonList( columnReference );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.sql.internal;
|
package org.hibernate.query.sqm.sql.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
|
@ -17,7 +19,9 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
import org.hibernate.sql.results.spi.DomainResult;
|
import org.hibernate.sql.results.spi.DomainResult;
|
||||||
|
@ -45,7 +49,6 @@ public class EmbeddableValuedPathInterpretation<T> implements AssignableSqmPathI
|
||||||
return new EmbeddableValuedPathInterpretation<>(
|
return new EmbeddableValuedPathInterpretation<>(
|
||||||
mapping.toSqlExpression(
|
mapping.toSqlExpression(
|
||||||
tableGroup,
|
tableGroup,
|
||||||
|
|
||||||
converter.getCurrentClauseStack().getCurrent(),
|
converter.getCurrentClauseStack().getCurrent(),
|
||||||
converter,
|
converter,
|
||||||
converter
|
converter
|
||||||
|
@ -114,4 +117,30 @@ public class EmbeddableValuedPathInterpretation<T> implements AssignableSqmPathI
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "EmbeddableValuedPathInterpretation(" + sqmPath.getNavigablePath().getFullPath() + ')';
|
return "EmbeddableValuedPathInterpretation(" + sqmPath.getNavigablePath().getFullPath() + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitColumnReferences(Consumer<ColumnReference> columnReferenceConsumer) {
|
||||||
|
if ( sqlExpression instanceof ColumnReference ) {
|
||||||
|
columnReferenceConsumer.accept( (ColumnReference) sqlExpression );
|
||||||
|
}
|
||||||
|
else if ( sqlExpression instanceof SqlTuple ) {
|
||||||
|
final SqlTuple sqlTuple = (SqlTuple) sqlExpression;
|
||||||
|
for ( Expression expression : sqlTuple.getExpressions() ) {
|
||||||
|
if ( ! ( expression instanceof ColumnReference ) ) {
|
||||||
|
throw new IllegalArgumentException( "Expecting ColumnReference, found : " + expression );
|
||||||
|
}
|
||||||
|
columnReferenceConsumer.accept( (ColumnReference) expression );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// error or warning...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ColumnReference> getColumnReferences() {
|
||||||
|
final List<ColumnReference> results = new ArrayList<>();
|
||||||
|
visitColumnReferences( results::add );
|
||||||
|
return results;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,12 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.ast;
|
package org.hibernate.sql.ast;
|
||||||
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter;
|
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcInsert;
|
import org.hibernate.sql.exec.spi.JdbcInsert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface SqlAstInsertSelectTranslator extends SqlAstToJdbcOperationConverter {
|
public interface SqlAstInsertSelectTranslator extends SqlAstTranslator {
|
||||||
JdbcInsert translate(InsertSelectStatement sqlAst);
|
JdbcInsert translate(InsertSelectStatement sqlAst);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.ast;
|
package org.hibernate.sql.ast;
|
||||||
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter;
|
|
||||||
import org.hibernate.sql.ast.spi.SqlSelectAstWalker;
|
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcSelect;
|
import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||||
|
@ -15,7 +13,7 @@ import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface SqlAstSelectTranslator extends SqlSelectAstWalker, SqlAstToJdbcOperationConverter {
|
public interface SqlAstSelectTranslator extends SqlAstTranslator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate the SelectStatement into the executable JdbcSelect
|
* Translate the SelectStatement into the executable JdbcSelect
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.sql.ast;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public interface SqlAstTranslator extends SqlAstWalker, SqlTypeDescriptorIndicators {
|
||||||
|
/**
|
||||||
|
* Not the best spot for this. Its the table names collected while walking the SQL AST.
|
||||||
|
* Its ok here because the translator is consider a one-time-use. It just needs to be called
|
||||||
|
* after translation.
|
||||||
|
*
|
||||||
|
* A better option is probably to have "translation" objects that expose the affected table-names.
|
||||||
|
*/
|
||||||
|
Set<String> getAffectedTableNames();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generalized support for translating a CTE statement. The underlying
|
||||||
|
* {@link CteStatement#getCteConsumer()} could be a SELECT, UPDATE, DELETE, etc.
|
||||||
|
*
|
||||||
|
* Implementors may throw an exception is the CTE-consumer is of the incorrect type
|
||||||
|
*/
|
||||||
|
JdbcOperation translate(CteStatement cteStatement);
|
||||||
|
}
|
|
@ -6,8 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.ast;
|
package org.hibernate.sql.ast;
|
||||||
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter;
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcUpdate;
|
import org.hibernate.sql.exec.spi.JdbcUpdate;
|
||||||
|
@ -15,7 +13,7 @@ import org.hibernate.sql.exec.spi.JdbcUpdate;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface SqlAstUpdateTranslator extends SqlAstWalker, SqlAstToJdbcOperationConverter {
|
public interface SqlAstUpdateTranslator extends SqlAstTranslator {
|
||||||
JdbcUpdate translate(UpdateStatement sqlAst);
|
JdbcUpdate translate(UpdateStatement sqlAst);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.SqlTreeCreationException;
|
import org.hibernate.sql.ast.SqlTreeCreationException;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
|
@ -17,13 +18,13 @@ import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractSqlAstToJdbcOperationConverter
|
public abstract class AbstractSqlAstTranslator
|
||||||
extends AbstractSqlAstWalker
|
extends AbstractSqlAstWalker
|
||||||
implements SqlAstToJdbcOperationConverter {
|
implements SqlAstTranslator {
|
||||||
|
|
||||||
private final Set<String> affectedTableNames = new HashSet<>();
|
private final Set<String> affectedTableNames = new HashSet<>();
|
||||||
|
|
||||||
protected AbstractSqlAstToJdbcOperationConverter(SessionFactoryImplementor sessionFactory) {
|
protected AbstractSqlAstTranslator(SessionFactoryImplementor sessionFactory) {
|
||||||
super( sessionFactory );
|
super( sessionFactory );
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class StandardSqlAstDeleteTranslator extends AbstractSqlAstToJdbcOperationConverter implements SqlAstDeleteTranslator {
|
public class StandardSqlAstDeleteTranslator extends AbstractSqlAstTranslator implements SqlAstDeleteTranslator {
|
||||||
public StandardSqlAstDeleteTranslator(SessionFactoryImplementor sessionFactory) {
|
public StandardSqlAstDeleteTranslator(SessionFactoryImplementor sessionFactory) {
|
||||||
super( sessionFactory );
|
super( sessionFactory );
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class StandardSqlAstInsertSelectTranslator
|
public class StandardSqlAstInsertSelectTranslator
|
||||||
extends AbstractSqlAstToJdbcOperationConverter
|
extends AbstractSqlAstTranslator
|
||||||
implements SqlAstInsertSelectTranslator {
|
implements SqlAstInsertSelectTranslator {
|
||||||
public StandardSqlAstInsertSelectTranslator(SessionFactoryImplementor sessionFactory) {
|
public StandardSqlAstInsertSelectTranslator(SessionFactoryImplementor sessionFactory) {
|
||||||
super( sessionFactory );
|
super( sessionFactory );
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.hibernate.sql.results.internal.JdbcValuesMappingProducerStandard;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class StandardSqlAstSelectTranslator
|
public class StandardSqlAstSelectTranslator
|
||||||
extends AbstractSqlAstToJdbcOperationConverter
|
extends AbstractSqlAstTranslator
|
||||||
implements SqlAstSelectTranslator {
|
implements SqlAstSelectTranslator {
|
||||||
|
|
||||||
public StandardSqlAstSelectTranslator(SessionFactoryImplementor sessionFactory) {
|
public StandardSqlAstSelectTranslator(SessionFactoryImplementor sessionFactory) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.sql.ast.SqlAstUpdateTranslator;
|
import org.hibernate.sql.ast.SqlAstUpdateTranslator;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
|
@ -23,14 +24,25 @@ import org.hibernate.sql.exec.spi.JdbcUpdate;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class StandardSqlAstUpdateTranslator
|
public class StandardSqlAstUpdateTranslator
|
||||||
extends AbstractSqlAstToJdbcOperationConverter
|
extends AbstractSqlAstTranslator
|
||||||
implements SqlAstUpdateTranslator {
|
implements SqlAstUpdateTranslator {
|
||||||
|
private final Dialect dialect;
|
||||||
|
|
||||||
public StandardSqlAstUpdateTranslator(SessionFactoryImplementor sessionFactory) {
|
public StandardSqlAstUpdateTranslator(SessionFactoryImplementor sessionFactory) {
|
||||||
super( sessionFactory );
|
super( sessionFactory );
|
||||||
|
|
||||||
|
// todo (6.0) : use the Dialect to determine how to handle column references
|
||||||
|
// - specifically should they use the table-alias, the table-expression
|
||||||
|
// or neither for its qualifier
|
||||||
|
dialect = getSessionFactory().getJdbcServices().getJdbcEnvironment().getDialect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String updatingTableAlias;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JdbcUpdate translate(UpdateStatement sqlAst) {
|
public JdbcUpdate translate(UpdateStatement sqlAst) {
|
||||||
|
updatingTableAlias = sqlAst.getTargetTable().getIdentificationVariable();
|
||||||
|
|
||||||
appendSql( "update " );
|
appendSql( "update " );
|
||||||
appendSql( sqlAst.getTargetTable().getTableExpression() );
|
appendSql( sqlAst.getTargetTable().getTableExpression() );
|
||||||
|
|
||||||
|
@ -45,7 +57,17 @@ public class StandardSqlAstUpdateTranslator
|
||||||
}
|
}
|
||||||
|
|
||||||
final Assignment assignment = sqlAst.getAssignments().get( i );
|
final Assignment assignment = sqlAst.getAssignments().get( i );
|
||||||
assignment.getColumnReference().accept( this );
|
final List<ColumnReference> columnReferences = assignment.getAssignable().getColumnReferences();
|
||||||
|
if ( columnReferences.size() == 1 ) {
|
||||||
|
columnReferences.get( 0 ).accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( " (" );
|
||||||
|
for ( int cri = 0; cri < columnReferences.size(); cri++ ) {
|
||||||
|
columnReferences.get( cri ).accept( this );
|
||||||
|
}
|
||||||
|
appendSql( ") " );
|
||||||
|
}
|
||||||
appendSql( " = " );
|
appendSql( " = " );
|
||||||
assignment.getAssignedValue().accept( this );
|
assignment.getAssignedValue().accept( this );
|
||||||
}
|
}
|
||||||
|
@ -75,7 +97,17 @@ public class StandardSqlAstUpdateTranslator
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitColumnReference(ColumnReference columnReference) {
|
public void visitColumnReference(ColumnReference columnReference) {
|
||||||
super.visitColumnReference( columnReference );
|
if ( updatingTableAlias != null && updatingTableAlias.equals( columnReference.getQualifier() ) ) {
|
||||||
|
// todo (6.0) : use the Dialect to determine how to handle column references
|
||||||
|
// - specifically should they use the table-alias, the table-expression
|
||||||
|
// or neither for its qualifier
|
||||||
|
|
||||||
|
// for now, use the unqualified form
|
||||||
|
appendSql( columnReference.getColumnExpression() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitColumnReference( columnReference );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,8 +7,11 @@
|
||||||
|
|
||||||
package org.hibernate.sql.ast.tree.expression;
|
package org.hibernate.sql.ast.tree.expression;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
@ -17,6 +20,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignable;
|
||||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
@ -26,7 +30,7 @@ import org.hibernate.type.spi.TypeConfiguration;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class ColumnReference implements Expression {
|
public class ColumnReference implements Expression, Assignable {
|
||||||
private final String qualifier;
|
private final String qualifier;
|
||||||
private final String columnExpression;
|
private final String columnExpression;
|
||||||
private final String referenceExpression;
|
private final String referenceExpression;
|
||||||
|
@ -123,4 +127,14 @@ public class ColumnReference implements Expression {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return referenceExpression.hashCode();
|
return referenceExpression.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitColumnReferences(Consumer<ColumnReference> columnReferenceConsumer) {
|
||||||
|
columnReferenceConsumer.accept( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ColumnReference> getColumnReferences() {
|
||||||
|
return Collections.singletonList( this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -123,19 +121,6 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
protected void renderTableReference(
|
|
||||||
TableReference tableBinding,
|
|
||||||
SqlAppender sqlAppender,
|
|
||||||
@SuppressWarnings("unused") SqlAstWalker walker) {
|
|
||||||
sqlAppender.appendSql( tableBinding.getTableExpression() );
|
|
||||||
|
|
||||||
final String identificationVariable = tableBinding.getIdentificationVariable();
|
|
||||||
if ( identificationVariable != null ) {
|
|
||||||
sqlAppender.appendSql( " as " + identificationVariable );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInnerJoinPossible() {
|
public boolean isInnerJoinPossible() {
|
||||||
return isInnerJoinPossible;
|
return isInnerJoinPossible;
|
||||||
|
|
|
@ -12,10 +12,10 @@ import java.util.function.Supplier;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|
||||||
import org.hibernate.sql.ast.JoinType;
|
import org.hibernate.sql.ast.JoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,18 +6,13 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.ast.tree.from;
|
package org.hibernate.sql.ast.tree.from;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.JoinType;
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.sql.ast.tree.from;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface TableAliasResolver {
|
||||||
|
String resolveAlias(String tableExpression, String aliasStem);
|
||||||
|
}
|
|
@ -8,10 +8,10 @@ package org.hibernate.sql.ast.tree.from;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|
||||||
import org.hibernate.sql.ast.JoinType;
|
import org.hibernate.sql.ast.JoinType;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.sql.ast.tree.update;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public interface Assignable {
|
||||||
|
List<ColumnReference> getColumnReferences();
|
||||||
|
|
||||||
|
void visitColumnReferences(Consumer<ColumnReference> columnReferenceConsumer);
|
||||||
|
|
||||||
|
}
|
|
@ -8,26 +8,25 @@ package org.hibernate.sql.ast.tree.update;
|
||||||
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class Assignment implements SqlAstNode {
|
public class Assignment implements SqlAstNode {
|
||||||
private final ColumnReference columnReference;
|
private final Assignable assignable;
|
||||||
private final Expression assignedValue;
|
private final Expression assignedValue;
|
||||||
|
|
||||||
public Assignment(ColumnReference columnReference, Expression assignedValue) {
|
public Assignment(Assignable assignable, Expression assignedValue) {
|
||||||
this.columnReference = columnReference;
|
this.assignable = assignable;
|
||||||
this.assignedValue = assignedValue;
|
this.assignedValue = assignedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The column being updated.
|
* The column being updated.
|
||||||
*/
|
*/
|
||||||
public ColumnReference getColumnReference() {
|
public Assignable getAssignable() {
|
||||||
return columnReference;
|
return assignable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expression getAssignedValue() {
|
public Expression getAssignedValue() {
|
||||||
|
|
|
@ -6,16 +6,12 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.sql.exec;
|
package org.hibernate.orm.test.sql.exec;
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.hibernate.orm.test.metamodel.mapping.SecondaryTableTests;
|
import org.hibernate.orm.test.metamodel.mapping.SecondaryTableTests;
|
||||||
import org.hibernate.orm.test.metamodel.mapping.inheritance.joined.JoinedInheritanceTest;
|
import org.hibernate.orm.test.metamodel.mapping.inheritance.joined.JoinedInheritanceTest;
|
||||||
|
|
||||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
|
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
|
||||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
@ -29,18 +25,21 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
@DomainModel(
|
@DomainModel(
|
||||||
standardModels = StandardDomainModel.GAMBIT
|
standardModels = StandardDomainModel.GAMBIT,
|
||||||
// standardModels = StandardDomainModel.GAMBIT,
|
annotatedClasses = {
|
||||||
// annotatedClasses = {
|
SecondaryTableTests.SimpleEntityWithSecondaryTables.class,
|
||||||
// SecondaryTableTests.SimpleEntityWithSecondaryTables.class,
|
JoinedInheritanceTest.Customer.class,
|
||||||
// JoinedInheritanceTest.Customer.class,
|
JoinedInheritanceTest.DomesticCustomer.class,
|
||||||
// JoinedInheritanceTest.DomesticCustomer.class,
|
JoinedInheritanceTest.ForeignCustomer.class
|
||||||
// JoinedInheritanceTest.ForeignCustomer.class
|
}
|
||||||
// }
|
|
||||||
)
|
)
|
||||||
@ServiceRegistry
|
@ServiceRegistry
|
||||||
@SessionFactory( exportSchema = true )
|
@SessionFactory( exportSchema = true )
|
||||||
public class HqlUpdateExecutionTests {
|
public class HqlUpdateExecutionTests {
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// single table
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleUpdate(SessionFactoryScope scope) {
|
public void testSimpleUpdate(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
|
@ -130,174 +129,61 @@ public class HqlUpdateExecutionTests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
|
||||||
// public void testSimpleMultiTableDelete(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete SimpleEntityWithSecondaryTables" )
|
|
||||||
// .executeUpdate()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testSimpleMultiTableRestrictedDelete(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete SimpleEntityWithSecondaryTables where data = :filter" )
|
|
||||||
// .setParameter( "filter", "abc" )
|
|
||||||
// .executeUpdate()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// @FailureExpected( reason = "Saving of entities with secondary tables is broken atm" )
|
|
||||||
// public void testSimpleMultiTableRestrictedDeleteResults(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// session.save(
|
|
||||||
// new SecondaryTableTests.SimpleEntityWithSecondaryTables(
|
|
||||||
// 1,
|
|
||||||
// "first",
|
|
||||||
// Date.from( Instant.now() ),
|
|
||||||
// "1 - cfdjdjvokfobkofbvovoijjbvoijofjdbiof"
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// session.save(
|
|
||||||
// new SecondaryTableTests.SimpleEntityWithSecondaryTables(
|
|
||||||
// 2,
|
|
||||||
// "second",
|
|
||||||
// Date.from( Instant.now() ),
|
|
||||||
// "2 - s3o2rj9 fcojv9j gj9jfv943jv29j9j4"
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// session.save(
|
|
||||||
// new SecondaryTableTests.SimpleEntityWithSecondaryTables(
|
|
||||||
// 3,
|
|
||||||
// "third",
|
|
||||||
// Date.from( Instant.now() ),
|
|
||||||
// "abc"
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// final int rows = session.createQuery( "delete SimpleEntityWithSecondaryTables where data = :filter" )
|
|
||||||
// .setParameter( "filter", "abc" )
|
|
||||||
// .executeUpdate();
|
|
||||||
// assertThat( rows, is ( 1 ) );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete SimpleEntityWithSecondaryTables" ).executeUpdate()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testJoinedSubclassRootDelete(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete Customer" ).executeUpdate()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testJoinedSubclassRootRestrictedDelete(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete Customer where name = 'abc'" ).executeUpdate()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testJoinedSubclassRootRestrictedDeleteResults(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// session.save(
|
|
||||||
// new JoinedInheritanceTest.ForeignCustomer( 1, "Adventures Abroad", "123" )
|
|
||||||
// );
|
|
||||||
// session.save(
|
|
||||||
// new JoinedInheritanceTest.DomesticCustomer( 2, "Domestic Wonders", "456" )
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// final int rows = session.createQuery( "delete Customer where name = 'Adventures Abroad'" ).executeUpdate();
|
|
||||||
// assertThat( rows, is( 1 ) );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// final int rows = session.createQuery( "delete from Customer" ).executeUpdate();
|
|
||||||
// assertThat( rows, is( 1 ) );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// final int rows = session.createQuery( "delete from Customer" ).executeUpdate();
|
|
||||||
// assertThat( rows, is( 0 ) );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testJoinedSubclassLeafDelete(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete ForeignCustomer" ).executeUpdate()
|
|
||||||
// );
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete DomesticCustomer" ).executeUpdate()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testJoinedSubclassLeafRestrictedDelete(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete ForeignCustomer where name = 'abc'" ).executeUpdate()
|
|
||||||
// );
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> session.createQuery( "delete DomesticCustomer where name = 'abc'" ).executeUpdate()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testJoinedSubclassLeafRestrictedDeleteResult(SessionFactoryScope scope) {
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// session.save(
|
|
||||||
// new JoinedInheritanceTest.ForeignCustomer( 1, "Adventures Abroad", "123" )
|
|
||||||
// );
|
|
||||||
// session.save(
|
|
||||||
// new JoinedInheritanceTest.DomesticCustomer( 2, "Domestic Wonders", "456" )
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// final int rows = session.createQuery( "delete ForeignCustomer where name = 'Adventures Abroad'" )
|
|
||||||
// .executeUpdate();
|
|
||||||
// assertThat( rows, is( 1 ) );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// final int rows = session.createQuery( "delete DomesticCustomer where name = 'Domestic Wonders'" )
|
|
||||||
// .executeUpdate();
|
|
||||||
// assertThat( rows, is( 1 ) );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// scope.inTransaction(
|
|
||||||
// session -> {
|
|
||||||
// final int rows = session.createQuery( "delete Customer" )
|
|
||||||
// .executeUpdate();
|
|
||||||
// assertThat( rows, is( 0 ) );
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// secondary tables
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecondaryTableUpdate(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createQuery( "update SimpleEntityWithSecondaryTables set name = :p" )
|
||||||
|
.setParameter( "p", "xyz" )
|
||||||
|
.executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecondaryTableRestrictedUpdatePrimary(SessionFactoryScope scope) {
|
||||||
|
// attempts to update the entity referring to just columns in the root table
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createQuery( "update SimpleEntityWithSecondaryTables set name = :p where name = :x" )
|
||||||
|
.setParameter( "p", "xyz" )
|
||||||
|
.setParameter( "x", "abc" )
|
||||||
|
.executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecondaryTableRestrictedUpdate(SessionFactoryScope scope) {
|
||||||
|
// attempts to update the entity referring to columns in non-root table
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createQuery( "update SimpleEntityWithSecondaryTables set name = :p where data = :x" )
|
||||||
|
.setParameter( "p", "xyz" )
|
||||||
|
.setParameter( "x", "123" )
|
||||||
|
.executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// joined subclassing
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJoinedSubclassRootUpdateUnrestricted(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createQuery( "update Customer set name = :n" )
|
||||||
|
.setParameter( "n", "abc" )
|
||||||
|
.executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJoinedSubclassRootUpdateRestricted(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createQuery( "update Customer set name = :n where id = :d" )
|
||||||
|
.setParameter( "n", "abc" )
|
||||||
|
.setParameter( "d", 1 )
|
||||||
|
.executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue