HHH-13715 - working support for "multi-table" HQL/Criteria UPDATE and DELETE queries
CTE, id-table and in-line strategies are all implemented (though only id-table is tested); refactoring for performance (direct creation of SQL AST object directly, rather than SQM -> SQL AST) and as part of initial impls for remaining strategies (global temp and persistent id tables, and the "inline" strategy; fixed concurrency bug (thanks Luis!)
This commit is contained in:
parent
b04599cbe5
commit
032fdb5d2e
|
@ -89,7 +89,7 @@ import org.hibernate.engine.transaction.jta.platform.internal.WebSphereJtaPlatfo
|
|||
import org.hibernate.engine.transaction.jta.platform.internal.WebSphereLibertyJtaPlatform;
|
||||
import org.hibernate.engine.transaction.jta.platform.internal.WeblogicJtaPlatform;
|
||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||
import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
|
||||
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
|
||||
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
|
||||
import org.hibernate.query.sqm.mutation.internal.idtable.PersistentTableStrategy;
|
||||
|
@ -410,8 +410,8 @@ public class StrategySelectorBuilder {
|
|||
private void addSqmMultiTableMutationStrategies(StrategySelectorImpl strategySelector) {
|
||||
strategySelector.registerStrategyImplementor(
|
||||
SqmMultiTableMutationStrategy.class,
|
||||
CteBasedMutationStrategy.SHORT_NAME,
|
||||
CteBasedMutationStrategy.class
|
||||
CteStrategy.SHORT_NAME,
|
||||
CteStrategy.class
|
||||
);
|
||||
strategySelector.registerStrategyImplementor(
|
||||
SqmMultiTableMutationStrategy.class,
|
||||
|
|
|
@ -223,7 +223,8 @@ abstract class AbstractTransactSQLDialect extends Dialect {
|
|||
// // sql-server, at least needed this dropped after use; strange!
|
||||
this::getTypeName,
|
||||
AfterUseAction.DROP,
|
||||
TempTableDdlTransactionHandling.NONE
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
runtimeModelCreationContext.getSessionFactory()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -618,7 +618,8 @@ public class DerbyDialect extends DB2Dialect {
|
|||
}
|
||||
},
|
||||
AfterUseAction.CLEAN,
|
||||
TempTableDdlTransactionHandling.NONE
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
runtimeModelCreationContext.getSessionFactory()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -379,7 +379,8 @@ public class H2Dialect extends Dialect {
|
|||
new IdTable( entityDescriptor, basename -> "HT_" + basename ),
|
||||
this::getTypeName,
|
||||
AfterUseAction.CLEAN,
|
||||
TempTableDdlTransactionHandling.NONE
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
runtimeModelCreationContext.getSessionFactory()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -301,7 +301,8 @@ public class InformixDialect extends Dialect {
|
|||
}
|
||||
},
|
||||
AfterUseAction.NONE,
|
||||
TempTableDdlTransactionHandling.NONE
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
runtimeModelCreationContext.getSessionFactory()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -366,7 +366,8 @@ public class MySQLDialect extends Dialect {
|
|||
}
|
||||
},
|
||||
AfterUseAction.DROP,
|
||||
TempTableDdlTransactionHandling.NONE
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
runtimeModelCreationContext.getSessionFactory()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -144,7 +144,6 @@ import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
|||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityVersionMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.Queryable;
|
||||
|
@ -169,7 +168,6 @@ import org.hibernate.property.access.spi.Setter;
|
|||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
||||
import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.Alias;
|
||||
|
@ -6201,6 +6199,18 @@ public abstract class AbstractEntityPersister
|
|||
else {
|
||||
sqmMultiTableMutationStrategy = null;
|
||||
}
|
||||
|
||||
// register a callback for after all `#prepareMappingModel` calls have finished. here we want to delay the
|
||||
// generation of `staticFetchableList` because we need to wait until after all sub-classes have had their
|
||||
// `#prepareMappingModel` called (and their declared attribute mappings resolved)
|
||||
creationProcess.registerInitializationCallback(
|
||||
() -> {
|
||||
staticFetchableList = new ArrayList<>( attributeMappings.size() );
|
||||
visitAttributeMappings( attributeMapping -> staticFetchableList.add( (Fetchable) attributeMapping ) );
|
||||
visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( (Fetchable) attributeMapping ) );
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected static SqmMultiTableMutationStrategy interpretSqmMultiTableStrategy(
|
||||
|
@ -6220,7 +6230,7 @@ public abstract class AbstractEntityPersister
|
|||
return SqmMutationStrategyHelper.resolveStrategy(
|
||||
entityBootDescriptor,
|
||||
entityMappingDescriptor,
|
||||
creationProcess.getCreationContext()
|
||||
creationProcess
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -6607,12 +6617,6 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
|
||||
protected List<Fetchable> getStaticFetchableList() {
|
||||
if ( staticFetchableList == null ) {
|
||||
staticFetchableList = new ArrayList<>( attributeMappings.size() );
|
||||
visitAttributeMappings( attributeMapping -> staticFetchableList.add( (Fetchable) attributeMapping ) );
|
||||
|
||||
visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( (Fetchable) attributeMapping ) );
|
||||
}
|
||||
return staticFetchableList;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ import org.hibernate.sql.CaseFragment;
|
|||
import org.hibernate.sql.InFragment;
|
||||
import org.hibernate.sql.Insert;
|
||||
import org.hibernate.sql.SelectFragment;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.JoinType;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
|
@ -1320,8 +1319,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
final Expression expression =
|
||||
new QueryLiteral<>(
|
||||
discriminatorValuesByTableName.get( table.getTableExpression() ),
|
||||
resultType,
|
||||
Clause.SELECT
|
||||
resultType
|
||||
);
|
||||
|
||||
caseSearchedExpression.when( predicate, expression );
|
||||
|
|
|
@ -46,7 +46,6 @@ import org.hibernate.query.NavigablePath;
|
|||
import org.hibernate.sql.InFragment;
|
||||
import org.hibernate.sql.Insert;
|
||||
import org.hibernate.sql.SelectFragment;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.JoinType;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
|
@ -915,8 +914,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
ComparisonOperator.EQUAL,
|
||||
new QueryLiteral<>(
|
||||
getDiscriminatorValue(),
|
||||
( (BasicType) getDiscriminatorType() ),
|
||||
Clause.WHERE
|
||||
( (BasicType) getDiscriminatorType() )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,21 +7,29 @@
|
|||
package org.hibernate.query.sqm.internal;
|
||||
|
||||
import org.hibernate.query.spi.NonSelectQueryPlan;
|
||||
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiTableDeleteQueryPlan implements NonSelectQueryPlan {
|
||||
private final DeleteHandler deleteHandler;
|
||||
private final SqmDeleteStatement sqmDelete;
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
private final SqmMultiTableMutationStrategy deleteStrategy;
|
||||
|
||||
public MultiTableDeleteQueryPlan(DeleteHandler deleteHandler) {
|
||||
this.deleteHandler = deleteHandler;
|
||||
public MultiTableDeleteQueryPlan(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
SqmMultiTableMutationStrategy deleteStrategy) {
|
||||
this.sqmDelete = sqmDelete;
|
||||
this.domainParameterXref = domainParameterXref;
|
||||
this.deleteStrategy = deleteStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(ExecutionContext executionContext) {
|
||||
return deleteHandler.execute( executionContext );
|
||||
return deleteStrategy.executeDelete( sqmDelete, domainParameterXref, executionContext );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,21 +7,29 @@
|
|||
package org.hibernate.query.sqm.internal;
|
||||
|
||||
import org.hibernate.query.spi.NonSelectQueryPlan;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiTableUpdateQueryPlan implements NonSelectQueryPlan {
|
||||
private final UpdateHandler updateHandler;
|
||||
private final SqmUpdateStatement sqmUpdate;
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
private final SqmMultiTableMutationStrategy mutationStrategy;
|
||||
|
||||
public MultiTableUpdateQueryPlan(UpdateHandler updateHandler) {
|
||||
this.updateHandler = updateHandler;
|
||||
public MultiTableUpdateQueryPlan(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
SqmMultiTableMutationStrategy mutationStrategy) {
|
||||
this.sqmUpdate = sqmUpdate;
|
||||
this.domainParameterXref = domainParameterXref;
|
||||
this.mutationStrategy = mutationStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(ExecutionContext executionContext) {
|
||||
return updateHandler.execute( executionContext );
|
||||
return mutationStrategy.executeUpdate( sqmUpdate, domainParameterXref, executionContext );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -545,34 +545,22 @@ public class QuerySqmImpl<R>
|
|||
return new SimpleDeleteQueryPlan( sqmDelete, domainParameterXref );
|
||||
}
|
||||
else {
|
||||
return new MultiTableDeleteQueryPlan(
|
||||
multiTableStrategy.buildDeleteHandler(
|
||||
sqmDelete,
|
||||
domainParameterXref,
|
||||
this::getSessionFactory
|
||||
)
|
||||
);
|
||||
return new MultiTableDeleteQueryPlan( sqmDelete, domainParameterXref, multiTableStrategy );
|
||||
}
|
||||
}
|
||||
|
||||
private NonSelectQueryPlan buildUpdateQueryPlan() {
|
||||
final SqmUpdateStatement sqmStatement = (SqmUpdateStatement) getSqmStatement();
|
||||
final SqmUpdateStatement sqmUpdate = (SqmUpdateStatement) getSqmStatement();
|
||||
|
||||
final String entityNameToUpdate = sqmStatement.getTarget().getReferencedPathSource().getHibernateEntityName();
|
||||
final String entityNameToUpdate = sqmUpdate.getTarget().getReferencedPathSource().getHibernateEntityName();
|
||||
final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToUpdate );
|
||||
|
||||
final SqmMultiTableMutationStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableMutationStrategy();
|
||||
if ( multiTableStrategy == null ) {
|
||||
return new SimpleUpdateQueryPlan( sqmStatement, domainParameterXref );
|
||||
return new SimpleUpdateQueryPlan( sqmUpdate, domainParameterXref );
|
||||
}
|
||||
else {
|
||||
return new MultiTableUpdateQueryPlan(
|
||||
multiTableStrategy.buildUpdateHandler(
|
||||
sqmStatement,
|
||||
domainParameterXref,
|
||||
this::getSessionFactory
|
||||
)
|
||||
);
|
||||
return new MultiTableUpdateQueryPlan( sqmUpdate, domainParameterXref, multiTableStrategy );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
package org.hibernate.query.sqm.internal;
|
||||
|
||||
import org.hibernate.query.spi.NonSelectQueryPlan;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 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.spi;
|
||||
package org.hibernate.query.sqm.mutation.internal;
|
||||
|
||||
/**
|
||||
* Handler for dealing with multi-table SQM DELETE queries.
|
|
@ -4,11 +4,17 @@
|
|||
* 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.spi;
|
||||
package org.hibernate.query.sqm.mutation.internal;
|
||||
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Simply as a matter of code structuring it is often worthwhile to put all of the execution code into a separate
|
||||
* handler (executor) class. This contract helps unify those helpers.
|
||||
*
|
||||
* Hiding this "behind the strategy" also allows mixing approaches based on the nature of specific
|
||||
* queries
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface Handler {
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.sql.ast.SqlAstSelectTranslator;
|
||||
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.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
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.JdbcSelect;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.sql.results.internal.domain.basic.BasicResult;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Helper used to generate the SELECT for selection of an entity's identifier, here specifically intended to be used
|
||||
* as the SELECT portion of a multi-table SQM mutation
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MatchingIdSelectionHelper {
|
||||
private static final Logger log = Logger.getLogger( MatchingIdSelectionHelper.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 MatchingIdSelectionHelper#selectMatchingIds}
|
||||
* * as a sub-query restriction to insert rows into an "id table"
|
||||
*/
|
||||
public static SelectStatement generateMatchingIdSelectStatement(
|
||||
EntityMappingType targetEntityDescriptor,
|
||||
SqmDeleteOrUpdateStatement sqmStatement,
|
||||
Predicate restriction,
|
||||
MultiTableSqmMutationConverter sqmConverter,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final EntityDomainType entityDomainType = sqmStatement.getTarget().getModel();
|
||||
log.tracef( "Starting generation of entity-id SQM selection - %s", entityDomainType.getHibernateEntityName() );
|
||||
|
||||
final QuerySpec idSelectionQuery = new QuerySpec( true, 1 );
|
||||
|
||||
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
|
||||
idSelectionQuery.getFromClause().addRoot( mutatingTableGroup );
|
||||
|
||||
final List<DomainResult> domainResults = new ArrayList<>();
|
||||
final AtomicInteger i = new AtomicInteger();
|
||||
targetEntityDescriptor.getIdentifierMapping().visitColumns(
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||
final int position = i.getAndIncrement();
|
||||
final TableReference tableReference = mutatingTableGroup.resolveTableReference( containingTableExpression );
|
||||
final Expression expression = sqmConverter.getSqlExpressionResolver().resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
tableReference,
|
||||
columnExpression,
|
||||
jdbcMapping,
|
||||
sessionFactory
|
||||
)
|
||||
);
|
||||
idSelectionQuery.getSelectClause().addSqlSelection(
|
||||
new SqlSelectionImpl(
|
||||
position,
|
||||
position + 1,
|
||||
expression,
|
||||
jdbcMapping
|
||||
)
|
||||
);
|
||||
|
||||
//noinspection unchecked
|
||||
domainResults.add( new BasicResult( position, null, jdbcMapping.getJavaTypeDescriptor() ) );
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
idSelectionQuery.applyPredicate( restriction );
|
||||
|
||||
return new SelectStatement( idSelectionQuery, domainResults );
|
||||
}
|
||||
/**
|
||||
* @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 MatchingIdSelectionHelper#selectMatchingIds}
|
||||
* * as a sub-query restriction to insert rows into an "id table"
|
||||
*/
|
||||
public static QuerySpec generateMatchingIdSelectQuery(
|
||||
EntityMappingType targetEntityDescriptor,
|
||||
SqmDeleteOrUpdateStatement sqmStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
Predicate restriction,
|
||||
MultiTableSqmMutationConverter sqmConverter,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final EntityDomainType entityDomainType = sqmStatement.getTarget().getModel();
|
||||
log.tracef( "Starting generation of entity-id SQM selection - %s", entityDomainType.getHibernateEntityName() );
|
||||
|
||||
|
||||
final QuerySpec idSelectionQuery = new QuerySpec( true, 1 );
|
||||
|
||||
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
|
||||
idSelectionQuery.getFromClause().addRoot( mutatingTableGroup );
|
||||
|
||||
final AtomicInteger i = new AtomicInteger();
|
||||
targetEntityDescriptor.getIdentifierMapping().visitColumns(
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||
final int position = i.getAndIncrement();
|
||||
final TableReference tableReference = mutatingTableGroup.resolveTableReference( containingTableExpression );
|
||||
final Expression expression = sqmConverter.getSqlExpressionResolver().resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
tableReference,
|
||||
columnExpression,
|
||||
jdbcMapping,
|
||||
sessionFactory
|
||||
)
|
||||
);
|
||||
idSelectionQuery.getSelectClause().addSqlSelection(
|
||||
new SqlSelectionImpl(
|
||||
position,
|
||||
position + 1,
|
||||
expression,
|
||||
jdbcMapping
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
idSelectionQuery.applyPredicate( restriction );
|
||||
|
||||
return idSelectionQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Centralized selection of ids matching the restriction of the DELETE
|
||||
* or UPDATE SQM query
|
||||
*/
|
||||
public static List<Object> selectMatchingIds(
|
||||
SqmDeleteOrUpdateStatement sqmMutationStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext executionContext) {
|
||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||
|
||||
final EntityMappingType entityDescriptor = factory.getDomainModel()
|
||||
.getEntityDescriptor( sqmMutationStatement.getTarget().getModel().getHibernateEntityName() );
|
||||
|
||||
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
|
||||
entityDescriptor,
|
||||
domainParameterXref,
|
||||
executionContext.getQueryOptions(),
|
||||
executionContext.getQueryParameterBindings(),
|
||||
factory
|
||||
);
|
||||
|
||||
|
||||
final Map<SqmParameter, List<JdbcParameter>> parameterResolutions;
|
||||
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
|
||||
parameterResolutions = Collections.emptyMap();
|
||||
}
|
||||
else {
|
||||
parameterResolutions = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
final Predicate restriction = sqmConverter.visitWhereClause(
|
||||
sqmMutationStatement.getWhereClause(),
|
||||
columnReference -> {},
|
||||
parameterResolutions::put
|
||||
);
|
||||
|
||||
final SelectStatement matchingIdSelection = generateMatchingIdSelectStatement(
|
||||
entityDescriptor,
|
||||
sqmMutationStatement,
|
||||
restriction,
|
||||
sqmConverter,
|
||||
factory
|
||||
);
|
||||
|
||||
|
||||
final JdbcServices jdbcServices = factory.getJdbcServices();
|
||||
final SqlAstSelectTranslator sqlAstSelectTranslator = jdbcServices.getJdbcEnvironment()
|
||||
.getSqlAstTranslatorFactory()
|
||||
.buildSelectTranslator( factory );
|
||||
|
||||
final JdbcSelect idSelectJdbcOperation = sqlAstSelectTranslator.translate( matchingIdSelection );
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||
executionContext.getQueryParameterBindings(),
|
||||
domainParameterXref,
|
||||
SqmUtil.generateJdbcParamsXref( domainParameterXref, sqmConverter ),
|
||||
factory.getDomainModel(),
|
||||
navigablePath -> sqmConverter.getMutatingTableGroup(),
|
||||
executionContext.getSession()
|
||||
);
|
||||
|
||||
return jdbcServices.getJdbcSelectExecutor().list(
|
||||
idSelectJdbcOperation,
|
||||
jdbcParameterBindings,
|
||||
executionContext,
|
||||
row -> row
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* 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;
|
||||
package org.hibernate.query.sqm.mutation.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
@ -109,7 +109,6 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter imp
|
|||
return super.getProcessingStateStack();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specialized hook to visit the assignments defined by the update SQM allow
|
||||
* "listening" for each SQL assignment.
|
||||
|
@ -128,6 +127,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter imp
|
|||
public List<Assignment> visitSetClause(SqmSetClause setClause) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void visitAssignment(
|
||||
SqmAssignment sqmAssignment,
|
||||
Consumer<Assignment> assignmentConsumer) {
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.internal.util.collections.StandardStack;
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.hql.spi.SqmCreationOptions;
|
||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
|
||||
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFromClause;
|
||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Helper used to generate the SELECT for selection of an entity's
|
||||
* identifier, here specifically intended to be used as the SELECT
|
||||
* portion of a multi-table SQM mutation
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqmIdSelectGenerator {
|
||||
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(
|
||||
SqmDeleteOrUpdateStatement sqmStatement,
|
||||
SqmCreationContext sqmCreationContext) {
|
||||
final EntityDomainType entityDomainType = sqmStatement.getTarget().getModel();
|
||||
|
||||
log.tracef( "Starting generation of entity-id SQM selection - %s", entityDomainType.getHibernateEntityName() );
|
||||
|
||||
final SqmQuerySpec sqmQuerySpec = new SqmQuerySpec( sqmCreationContext.getNodeBuilder() );
|
||||
|
||||
final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
|
||||
|
||||
final SqmCreationState creationState = new SqmCreationState() {
|
||||
@Override
|
||||
public SqmCreationContext getCreationContext() {
|
||||
return sqmCreationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmCreationOptions getCreationOptions() {
|
||||
return () -> false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stack<SqmCreationProcessingState> getProcessingStateStack() {
|
||||
return processingStateStack;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// temporary - used just for creating processingState
|
||||
final SqmSelectStatement sqmSelectStatement = new SqmSelectStatement( sqmCreationContext.getNodeBuilder() );
|
||||
//noinspection unchecked
|
||||
sqmSelectStatement.setQuerySpec( sqmQuerySpec );
|
||||
|
||||
final SqmCreationProcessingState processingState = new SqmQuerySpecCreationProcessingStateStandardImpl(
|
||||
null,
|
||||
sqmSelectStatement,
|
||||
creationState
|
||||
);
|
||||
|
||||
processingStateStack.push( processingState );
|
||||
|
||||
final SqmFromClause sqmFromClause = new SqmFromClause();
|
||||
sqmQuerySpec.setFromClause( sqmFromClause );
|
||||
|
||||
|
||||
//noinspection unchecked
|
||||
// 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 );
|
||||
|
||||
final SqmSelectClause sqmSelectClause = new SqmSelectClause( true, sqmCreationContext.getNodeBuilder() );
|
||||
sqmQuerySpec.setSelectClause( sqmSelectClause );
|
||||
applySelections( sqmQuerySpec, sqmRoot, processingState );
|
||||
|
||||
if ( sqmStatement.getWhereClause() != null ) {
|
||||
sqmQuerySpec.applyPredicate( sqmStatement.getWhereClause().getPredicate() );
|
||||
}
|
||||
|
||||
return sqmQuerySpec;
|
||||
}
|
||||
|
||||
private static void applySelections(
|
||||
SqmQuerySpec sqmQuerySpec,
|
||||
SqmRoot<?> sqmRoot,
|
||||
SqmCreationProcessingState processingState) {
|
||||
//noinspection unchecked
|
||||
final SqmPath idPath = sqmRoot.getModel().getIdentifierDescriptor().createSqmPath( sqmRoot, processingState.getCreationState() );
|
||||
|
||||
//noinspection unchecked
|
||||
sqmQuerySpec.getSelectClause().add(
|
||||
new SqmSelection(
|
||||
idPath,
|
||||
null,
|
||||
processingState.getCreationState().getCreationContext().getNodeBuilder()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -6,46 +6,14 @@
|
|||
*/
|
||||
package org.hibernate.query.sqm.mutation.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.RootClass;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
||||
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.sql.SqmSelectTranslation;
|
||||
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
|
||||
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmGroupedPredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmInListPredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmJunctivePredicate;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
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.JdbcSelect;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -66,220 +34,22 @@ public class SqmMutationStrategyHelper {
|
|||
public static SqmMultiTableMutationStrategy resolveStrategy(
|
||||
RootClass entityBootDescriptor,
|
||||
EntityMappingType rootEntityDescriptor,
|
||||
RuntimeModelCreationContext creationContext) {
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
|
||||
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
|
||||
final SessionFactoryOptions options = sessionFactory.getSessionFactoryOptions();
|
||||
|
||||
final SqmMultiTableMutationStrategy specifiedStrategy = options.getSqmMultiTableMutationStrategy();
|
||||
if ( specifiedStrategy != null ) {
|
||||
return specifiedStrategy;
|
||||
}
|
||||
|
||||
// todo (6.0) : add capability define strategy per-hierarchy
|
||||
|
||||
return sessionFactory.getServiceRegistry().getService( JdbcServices.class )
|
||||
.getJdbcEnvironment()
|
||||
.getDialect()
|
||||
.getFallbackSqmMutationStrategy( rootEntityDescriptor, creationContext );
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized "Supplier" or "tri Function" for creating the
|
||||
* fallback handler if the query matches no "special cases"
|
||||
*/
|
||||
public interface FallbackDeleteHandlerCreator {
|
||||
DeleteHandler create(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard DeleteHandler resolution applying "special case" resolution
|
||||
*/
|
||||
public static DeleteHandler resolveDeleteHandler(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext,
|
||||
FallbackDeleteHandlerCreator fallbackCreator) {
|
||||
if ( sqmDelete.getWhereClause() == null ) {
|
||||
// special case : unrestricted
|
||||
// -> delete all rows, no need to use the id table
|
||||
}
|
||||
else {
|
||||
// if the predicate contains refers to any non-id Navigable, we will need to use the id table
|
||||
if ( ! hasNonIdReferences( sqmDelete.getWhereClause().getPredicate() ) ) {
|
||||
// special case : not restricted on non-id Navigable reference
|
||||
// -> we can apply the original restriction to the individual
|
||||
//
|
||||
// todo (6.0) : technically non-id references where the reference is mapped to the primary table
|
||||
// can also be handled by this special case. Really the special case condition is "has
|
||||
// any non-id references to Navigables not mapped to the primary table of the container"
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, use the fallback....
|
||||
return fallbackCreator.create( sqmDelete, domainParameterXref, creationContext );
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized "Supplier" or "tri Function" for creating the
|
||||
* fallback handler if the query mmatches no "special cases"
|
||||
*/
|
||||
public interface FallbackUpdateHandlerCreator {
|
||||
UpdateHandler create(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard UpdateHandler resolution applying "special case" resolution
|
||||
*/
|
||||
public static UpdateHandler resolveUpdateHandler(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext,
|
||||
FallbackUpdateHandlerCreator fallbackCreator) {
|
||||
if ( sqmUpdate.getWhereClause() == null ) {
|
||||
// special case : unrestricted
|
||||
// -> delete all rows, no need to use the id table
|
||||
}
|
||||
else {
|
||||
// see if the predicate contains any non-id Navigable references
|
||||
if ( ! hasNonIdReferences( sqmUpdate.getWhereClause().getPredicate() ) ) {
|
||||
// special case : not restricted on non-id Navigable reference
|
||||
// -> we can apply the original restriction to the individual updates without needing to use the id-table
|
||||
//
|
||||
// todo (6.0) : technically non-id references where the reference is mapped to the primary table
|
||||
// can also be handled by this special case. Really the special case condition is "has
|
||||
// any non-id references to Navigables not mapped to the primary table of the container"
|
||||
}
|
||||
}
|
||||
|
||||
// todo (6.0) : implement the above special cases
|
||||
|
||||
// otherwise, use the fallback....
|
||||
return fallbackCreator.create( sqmUpdate, domainParameterXref, creationContext );
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given `predicate` "non-identifier Navigable references"?
|
||||
*
|
||||
* @see #isNonIdentifierReference
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static boolean hasNonIdReferences(SqmPredicate predicate) {
|
||||
if ( predicate instanceof SqmGroupedPredicate ) {
|
||||
return hasNonIdReferences( ( (SqmGroupedPredicate) predicate ).getSubPredicate() );
|
||||
}
|
||||
|
||||
if ( predicate instanceof SqmJunctivePredicate ) {
|
||||
return hasNonIdReferences( ( (SqmJunctivePredicate) predicate ).getLeftHandPredicate() )
|
||||
&& hasNonIdReferences( ( (SqmJunctivePredicate) predicate ).getRightHandPredicate() );
|
||||
}
|
||||
|
||||
if ( predicate instanceof SqmComparisonPredicate ) {
|
||||
final SqmExpression lhs = ( (SqmComparisonPredicate) predicate ).getLeftHandExpression();
|
||||
final SqmExpression rhs = ( (SqmComparisonPredicate) predicate ).getRightHandExpression();
|
||||
|
||||
return isNonIdentifierReference( lhs ) || isNonIdentifierReference( rhs );
|
||||
}
|
||||
|
||||
if ( predicate instanceof SqmInListPredicate ) {
|
||||
final SqmInListPredicate<?> inPredicate = (SqmInListPredicate) predicate;
|
||||
if ( isNonIdentifierReference( inPredicate.getTestExpression() ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for ( SqmExpression listExpression : inPredicate.getListExpressions() ) {
|
||||
if ( isNonIdentifierReference( listExpression ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( predicate instanceof SqmBetweenPredicate ) {
|
||||
final SqmBetweenPredicate betweenPredicate = (SqmBetweenPredicate) predicate;
|
||||
return isNonIdentifierReference( betweenPredicate.getExpression() )
|
||||
|| isNonIdentifierReference( betweenPredicate.getLowerBound() )
|
||||
|| isNonIdentifierReference( betweenPredicate.getUpperBound() );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given `expression` a `SqmNavigableReference` that is also a reference
|
||||
* to a non-`EntityIdentifier` `Navigable`?
|
||||
*
|
||||
* @see SqmSimplePath
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static boolean isNonIdentifierReference(SqmExpression expression) {
|
||||
// if ( expression instanceof SqmNavigableReference ) {
|
||||
// return ! EntityIdentifier.class.isInstance( expression );
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Centralized selection of ids matching the restriction of the DELETE
|
||||
* or UPDATE SQM query
|
||||
*/
|
||||
public static List<Object> selectMatchingIds(
|
||||
SqmDeleteOrUpdateStatement sqmDeleteStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext executionContext) {
|
||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||
final QueryEngine queryEngine = factory.getQueryEngine();
|
||||
final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory();
|
||||
|
||||
final SqmSelectTranslator selectConverter = sqmTranslatorFactory.createSelectTranslator(
|
||||
executionContext.getQueryOptions(),
|
||||
domainParameterXref,
|
||||
executionContext.getQueryParameterBindings(),
|
||||
executionContext.getLoadQueryInfluencers(),
|
||||
factory
|
||||
);
|
||||
|
||||
final SqmQuerySpec sqmIdSelectQuerySpec = SqmIdSelectGenerator.generateSqmEntityIdSelect(
|
||||
sqmDeleteStatement,
|
||||
factory
|
||||
);
|
||||
|
||||
final SqmSelectStatement sqmIdSelect = new SqmSelectStatement( factory.getNodeBuilder() );
|
||||
//noinspection unchecked
|
||||
sqmIdSelect.setQuerySpec( sqmIdSelectQuerySpec );
|
||||
|
||||
final SqmSelectTranslation sqmInterpretation = selectConverter.translate( sqmIdSelect );
|
||||
|
||||
final JdbcServices jdbcServices = factory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
|
||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( factory ).translate( sqmInterpretation.getSqlAst() );
|
||||
|
||||
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
|
||||
domainParameterXref,
|
||||
sqmInterpretation::getJdbcParamsBySqmParam
|
||||
);
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||
executionContext.getQueryParameterBindings(),
|
||||
domainParameterXref,
|
||||
jdbcParamsXref,
|
||||
factory.getDomainModel(),
|
||||
selectConverter.getFromClauseAccess()::findTableGroup,
|
||||
executionContext.getSession()
|
||||
);
|
||||
|
||||
|
||||
return factory.getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParameterBindings,
|
||||
executionContext,
|
||||
row -> row
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 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.spi;
|
||||
package org.hibernate.query.sqm.mutation.internal;
|
||||
|
||||
/**
|
||||
* Handler for dealing with multi-table SQM UPDATE queries.
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
package org.hibernate.query.sqm.mutation.internal.cte;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
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.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteTable;
|
||||
|
||||
|
@ -22,15 +22,15 @@ import org.hibernate.sql.ast.tree.cte.CteTable;
|
|||
public abstract class AbstractCteMutationHandler extends AbstractMutationHandler {
|
||||
private final CteTable cteTable;
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
private final CteBasedMutationStrategy strategy;
|
||||
private final CteStrategy strategy;
|
||||
|
||||
public AbstractCteMutationHandler(
|
||||
CteTable cteTable,
|
||||
SqmDeleteOrUpdateStatement sqmStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
CteBasedMutationStrategy strategy,
|
||||
HandlerCreationContext creationContext) {
|
||||
super( sqmStatement, creationContext );
|
||||
CteStrategy strategy,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( sqmStatement, sessionFactory );
|
||||
this.cteTable = cteTable;
|
||||
this.domainParameterXref = domainParameterXref;
|
||||
|
||||
|
@ -45,7 +45,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
|||
return domainParameterXref;
|
||||
}
|
||||
|
||||
public CteBasedMutationStrategy getStrategy() {
|
||||
public CteStrategy getStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@ import org.hibernate.metamodel.mapping.ColumnConsumer;
|
|||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
||||
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
||||
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
||||
import org.hibernate.query.sqm.mutation.internal.DeleteHandler;
|
||||
import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
|
@ -54,12 +53,11 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
|||
CteTable cteTable,
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
CteBasedMutationStrategy strategy,
|
||||
HandlerCreationContext creationContext) {
|
||||
super( cteTable, sqmDeleteStatement, domainParameterXref, strategy, creationContext );
|
||||
CteStrategy strategy,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( cteTable, sqmDeleteStatement, domainParameterXref, strategy, sessionFactory );
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcServices jdbcServices = getSessionFactory().getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
}
|
||||
|
@ -71,7 +69,7 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
|||
|
||||
@Override
|
||||
public int execute(ExecutionContext executionContext) {
|
||||
final List<Object> ids = SqmMutationStrategyHelper.selectMatchingIds(
|
||||
final List<Object> ids = MatchingIdSelectionHelper.selectMatchingIds(
|
||||
getSqmDeleteOrUpdateStatement(),
|
||||
getDomainParameterXref(),
|
||||
executionContext
|
||||
|
@ -122,17 +120,15 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
|||
);
|
||||
|
||||
getEntityDescriptor().visitConstraintOrderedTables(
|
||||
(tableExpression, tableColumnsVisitationSupplier) -> {
|
||||
executeDelete(
|
||||
cteDefinitionQuerySpec,
|
||||
tableExpression,
|
||||
tableColumnsVisitationSupplier,
|
||||
getEntityDescriptor().getIdentifierMapping(),
|
||||
cteQuerySpec,
|
||||
jdbcParameterBindings,
|
||||
executionContext
|
||||
);
|
||||
}
|
||||
(tableExpression, tableColumnsVisitationSupplier) -> executeDelete(
|
||||
cteDefinitionQuerySpec,
|
||||
tableExpression,
|
||||
tableColumnsVisitationSupplier,
|
||||
getEntityDescriptor().getIdentifierMapping(),
|
||||
cteQuerySpec,
|
||||
jdbcParameterBindings,
|
||||
executionContext
|
||||
)
|
||||
);
|
||||
|
||||
return ids.size();
|
||||
|
@ -201,7 +197,7 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
|||
);
|
||||
return new CteStatement(
|
||||
cteDefinition,
|
||||
CteBasedMutationStrategy.TABLE_NAME,
|
||||
CteStrategy.TABLE_NAME,
|
||||
getCteTable(),
|
||||
deleteStatement
|
||||
);
|
||||
|
@ -225,16 +221,14 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
|||
final List<ColumnReference> columnsToMatchReferences = new ArrayList<>();
|
||||
|
||||
columnsToMatchVisitationSupplier.get().accept(
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||
columnsToMatchReferences.add(
|
||||
new ColumnReference(
|
||||
targetTableReference,
|
||||
columnExpression,
|
||||
jdbcMapping,
|
||||
sessionFactory
|
||||
)
|
||||
);
|
||||
}
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> columnsToMatchReferences.add(
|
||||
new ColumnReference(
|
||||
targetTableReference,
|
||||
columnExpression,
|
||||
jdbcMapping,
|
||||
sessionFactory
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
final Expression columnsToMatchExpression;
|
||||
|
|
|
@ -10,17 +10,16 @@ import java.util.Locale;
|
|||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
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.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteTable;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* @asciidoc
|
||||
|
@ -50,51 +49,25 @@ import org.hibernate.sql.ast.tree.cte.CteTable;
|
|||
* )
|
||||
* ````
|
||||
*
|
||||
* todo (6.0) : why not:
|
||||
*
|
||||
* ````
|
||||
* with cte_id (id) as (
|
||||
* select id
|
||||
* from Person p
|
||||
* where ...
|
||||
* )
|
||||
* delete from Contact
|
||||
* where (id) in (
|
||||
* select id
|
||||
* from cte_id
|
||||
* )
|
||||
*
|
||||
* with cte_id (id) as (
|
||||
* select id
|
||||
* from Person p
|
||||
* where ...
|
||||
* )
|
||||
* delete from Person
|
||||
* where (id) in (
|
||||
* select id
|
||||
* from cte_id
|
||||
* )
|
||||
* ````
|
||||
*
|
||||
* @author Evandro Pires da Silva
|
||||
* @author Vlad Mihalcea
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy {
|
||||
public class CteStrategy implements SqmMultiTableMutationStrategy {
|
||||
public static final String SHORT_NAME = "cte";
|
||||
public static final String TABLE_NAME = "id_cte";
|
||||
|
||||
private final EntityPersister rootDescriptor;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final CteTable cteTable;
|
||||
|
||||
public CteBasedMutationStrategy(
|
||||
public CteStrategy(
|
||||
EntityPersister rootDescriptor,
|
||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||
this.rootDescriptor = rootDescriptor;
|
||||
this.sessionFactory = runtimeModelCreationContext.getSessionFactory();
|
||||
|
||||
final Dialect dialect = runtimeModelCreationContext.getTypeConfiguration()
|
||||
.getSessionFactory()
|
||||
.getServiceRegistry()
|
||||
final Dialect dialect = sessionFactory.getServiceRegistry()
|
||||
.getService( JdbcServices.class )
|
||||
.getJdbcEnvironment()
|
||||
.getDialect();
|
||||
|
@ -120,24 +93,30 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy {
|
|||
);
|
||||
}
|
||||
|
||||
this.cteTable = new CteTable( rootDescriptor, runtimeModelCreationContext.getTypeConfiguration() );
|
||||
this.cteTable = new CteTable( rootDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SqmUpdateStatement sqmUpdateStatement,
|
||||
public int executeDelete(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
checkMatch( sqmUpdateStatement, creationContext );
|
||||
|
||||
return new CteUpdateHandler( cteTable, sqmUpdateStatement, domainParameterXref, this, creationContext );
|
||||
ExecutionContext context) {
|
||||
checkMatch( sqmDelete );
|
||||
return new CteDeleteHandler( cteTable, sqmDelete, domainParameterXref, this, sessionFactory ).execute( context );
|
||||
}
|
||||
|
||||
private void checkMatch(SqmDeleteOrUpdateStatement sqmStatement, HandlerCreationContext creationContext) {
|
||||
@Override
|
||||
public int executeUpdate(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
checkMatch( sqmUpdate );
|
||||
return new CteUpdateHandler( cteTable, sqmUpdate, domainParameterXref, this, sessionFactory ).execute( context );
|
||||
}
|
||||
|
||||
private void checkMatch(SqmDeleteOrUpdateStatement sqmStatement) {
|
||||
final String targetEntityName = sqmStatement.getTarget().getEntityName();
|
||||
final EntityPersister targetEntityDescriptor = creationContext.getSessionFactory()
|
||||
.getDomainModel()
|
||||
.getEntityDescriptor( targetEntityName );
|
||||
final EntityPersister targetEntityDescriptor = sessionFactory.getDomainModel().getEntityDescriptor( targetEntityName );
|
||||
|
||||
if ( targetEntityDescriptor != rootDescriptor && ! rootDescriptor.isSubclassEntityName( targetEntityDescriptor.getEntityName() ) ) {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -151,14 +130,4 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
checkMatch( sqmDeleteStatement, creationContext );
|
||||
|
||||
return new CteDeleteHandler( cteTable, sqmDeleteStatement, domainParameterXref, this, creationContext );
|
||||
}
|
||||
}
|
|
@ -7,9 +7,9 @@
|
|||
package org.hibernate.query.sqm.mutation.internal.cte;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteTable;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
@ -24,13 +24,14 @@ public class CteUpdateHandler
|
|||
extends AbstractCteMutationHandler
|
||||
implements UpdateHandler {
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public CteUpdateHandler(
|
||||
CteTable cteTable,
|
||||
SqmUpdateStatement sqmStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
CteBasedMutationStrategy strategy,
|
||||
HandlerCreationContext creationContext) {
|
||||
super( cteTable, sqmStatement, domainParameterXref, strategy, creationContext );
|
||||
CteStrategy strategy,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( cteTable, sqmStatement, domainParameterXref, strategy, sessionFactory );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -15,25 +16,16 @@ import org.hibernate.boot.TempTableDdlTransactionHandling;
|
|||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.transaction.spi.IsolationDelegate;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.internal.SqmTreePrinter;
|
||||
import org.hibernate.query.sqm.mutation.internal.SqmIdSelectGenerator;
|
||||
import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation;
|
||||
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
|
||||
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
|
||||
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
||||
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.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
||||
import org.hibernate.sql.ast.SqlAstInsertSelectTranslator;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||
|
@ -47,6 +39,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
|
|||
import org.hibernate.sql.exec.spi.JdbcInsert;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.UUIDCharType;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -63,158 +56,83 @@ public final class ExecuteWithIdTableHelper {
|
|||
}
|
||||
|
||||
public static int saveMatchingIdsIntoIdTable(
|
||||
SqmUpdateStatement sqmMutation,
|
||||
MultiTableSqmMutationConverter sqmConverter,
|
||||
TableGroup mutatingTableGroup,
|
||||
Predicate suppliedPredicate,
|
||||
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
|
||||
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
|
||||
|
||||
assert mutatingTableGroup.getModelPart() instanceof EntityMappingType;
|
||||
final EntityMappingType mutatingEntityDescriptor = (EntityMappingType) mutatingTableGroup.getModelPart();
|
||||
|
||||
final InsertSelectStatement idTableInsert = new InsertSelectStatement();
|
||||
|
||||
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
|
||||
idTableInsert.setTargetTable( idTableReference );
|
||||
|
||||
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
|
||||
final IdTableColumn column = idTable.getIdTableColumns().get( i );
|
||||
idTableInsert.addTargetColumnReferences(
|
||||
new ColumnReference( idTableReference, column.getColumnName(), column.getJdbcMapping(), factory )
|
||||
);
|
||||
}
|
||||
|
||||
final QuerySpec matchingIdSelection = new QuerySpec( true, 1 );
|
||||
idTableInsert.setSourceSelectStatement( matchingIdSelection );
|
||||
|
||||
matchingIdSelection.getFromClause().addRoot( mutatingTableGroup );
|
||||
|
||||
final AtomicInteger positionWrapper = new AtomicInteger();
|
||||
|
||||
mutatingEntityDescriptor.getIdentifierMapping().visitColumns(
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||
final int jdbcPosition = positionWrapper.getAndIncrement();
|
||||
final TableReference tableReference = mutatingTableGroup.resolveTableReference( containingTableExpression );
|
||||
matchingIdSelection.getSelectClause().addSqlSelection(
|
||||
new SqlSelectionImpl(
|
||||
jdbcPosition,
|
||||
jdbcPosition + 1,
|
||||
sqmConverter.getSqlExpressionResolver().resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
tableReference,
|
||||
columnExpression,
|
||||
jdbcMapping,
|
||||
factory
|
||||
)
|
||||
),
|
||||
jdbcMapping
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if ( idTable.getSessionUidColumn() != null ) {
|
||||
//noinspection unchecked
|
||||
sqmIdSelect.getSelectClause().add(
|
||||
new SqmSelection(
|
||||
new SqmLiteral(
|
||||
final int jdbcPosition = positionWrapper.getAndIncrement();
|
||||
matchingIdSelection.getSelectClause().addSqlSelection(
|
||||
new SqlSelectionImpl(
|
||||
jdbcPosition,
|
||||
jdbcPosition + 1,
|
||||
new QueryLiteral(
|
||||
sessionUidAccess.apply( executionContext.getSession() ),
|
||||
UUIDCharType.INSTANCE,
|
||||
executionContext.getSession().getFactory().getNodeBuilder()
|
||||
StandardBasicTypes.STRING
|
||||
),
|
||||
null,
|
||||
executionContext.getSession().getFactory().getNodeBuilder()
|
||||
idTable.getSessionUidColumn().getJdbcMapping()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
SqmTreePrinter.logTree( sqmIdSelect, "Entity-identifier Selection SqmQuerySpec" );
|
||||
|
||||
final InsertSelectStatement insertSelectStatement = new InsertSelectStatement();
|
||||
|
||||
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
|
||||
insertSelectStatement.setTargetTable( idTableReference );
|
||||
|
||||
final QuerySpec matchingIdRestrictionQuerySpec = generateTempTableInsertValuesQuerySpec(
|
||||
sqmConverter,
|
||||
mutatingTableGroup,
|
||||
suppliedPredicate,
|
||||
sqmIdSelect
|
||||
);
|
||||
insertSelectStatement.setSourceSelectStatement( matchingIdRestrictionQuerySpec );
|
||||
|
||||
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 )
|
||||
);
|
||||
}
|
||||
matchingIdSelection.applyPredicate( suppliedPredicate );
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
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 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 );
|
||||
final JdbcInsert jdbcInsert = sqlAstTranslator.translate( idTableInsert );
|
||||
|
||||
return jdbcServices.getJdbcMutationExecutor().execute(
|
||||
jdbcInsert,
|
||||
|
@ -302,8 +220,7 @@ public final class ExecuteWithIdTableHelper {
|
|||
ComparisonOperator.EQUAL,
|
||||
new QueryLiteral(
|
||||
sessionUidAccess.apply( executionContext.getSession() ),
|
||||
UUIDCharType.INSTANCE,
|
||||
Clause.WHERE
|
||||
UUIDCharType.INSTANCE
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -317,12 +234,23 @@ public final class ExecuteWithIdTableHelper {
|
|||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
ExecutionContext executionContext) {
|
||||
if ( beforeUseAction == BeforeUseAction.CREATE ) {
|
||||
IdTableHelper.createIdTable(
|
||||
final IdTableHelper.IdTableCreationWork idTableCreationWork = new IdTableHelper.IdTableCreationWork(
|
||||
idTable,
|
||||
idTableExporterAccess.get(),
|
||||
ddlTransactionHandling,
|
||||
executionContext.getSession()
|
||||
executionContext.getSession().getFactory()
|
||||
);
|
||||
|
||||
if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) {
|
||||
( (SessionImplementor) executionContext.getSession() ).doWork( idTableCreationWork );
|
||||
}
|
||||
else {
|
||||
final IsolationDelegate isolationDelegate = executionContext.getSession()
|
||||
.getJdbcCoordinator()
|
||||
.getJdbcSessionOwner()
|
||||
.getTransactionCoordinator()
|
||||
.createIsolationDelegate();
|
||||
isolationDelegate.delegateWork( idTableCreationWork, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,12 +270,23 @@ public final class ExecuteWithIdTableHelper {
|
|||
);
|
||||
}
|
||||
else if ( afterUseAction == AfterUseAction.DROP ) {
|
||||
IdTableHelper.dropIdTable(
|
||||
final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork(
|
||||
idTable,
|
||||
idTableExporterAccess.get(),
|
||||
ddlTransactionHandling,
|
||||
executionContext.getSession()
|
||||
executionContext.getSession().getFactory()
|
||||
);
|
||||
|
||||
if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) {
|
||||
( (SessionImplementor) executionContext.getSession() ).doWork( idTableDropWork );
|
||||
}
|
||||
else {
|
||||
final IsolationDelegate isolationDelegate = executionContext.getSession()
|
||||
.getJdbcCoordinator()
|
||||
.getJdbcSessionOwner()
|
||||
.getTransactionCoordinator()
|
||||
.createIsolationDelegate();
|
||||
isolationDelegate.delegateWork( idTableDropWork, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,23 @@
|
|||
*/
|
||||
package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.SessionFactoryObserver;
|
||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
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.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Strategy based on ANSI SQL's definition of a "global temporary table".
|
||||
|
@ -21,22 +30,168 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrategy {
|
||||
private static final Logger log = Logger.getLogger( GlobalTemporaryTableStrategy.class );
|
||||
|
||||
public static final String SHORT_NAME = "global_temporary";
|
||||
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.global_temporary.drop_tables";
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SqmUpdateStatement sqmUpdateStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
private final IdTable idTable;
|
||||
private final AfterUseAction afterUseAction;
|
||||
private final Supplier<IdTableExporter> idTableExporterAccess;
|
||||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
private boolean prepared;
|
||||
private boolean created;
|
||||
private boolean released;
|
||||
|
||||
public GlobalTemporaryTableStrategy(
|
||||
IdTable idTable,
|
||||
Supplier<IdTableExporter> idTableExporterAccess,
|
||||
AfterUseAction afterUseAction,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.idTable = idTable;
|
||||
this.idTableExporterAccess = idTableExporterAccess;
|
||||
this.afterUseAction = afterUseAction;
|
||||
this.sessionFactory = sessionFactory;
|
||||
|
||||
if ( afterUseAction == AfterUseAction.DROP ) {
|
||||
throw new IllegalArgumentException( "Global-temp ID tables cannot use AfterUseAction.DROP : " + idTable.getTableExpression() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
public int executeUpdate(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
ExecutionContext context) {
|
||||
return new TableBasedUpdateHandler(
|
||||
sqmUpdate,
|
||||
domainParameterXref,
|
||||
idTable,
|
||||
// generally a global temp table should already track a Connection-specific uid,
|
||||
// but just in case a particular env needs it...
|
||||
session -> session.getSessionIdentifier().toString(),
|
||||
idTableExporterAccess,
|
||||
BeforeUseAction.CREATE,
|
||||
afterUseAction,
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
sessionFactory
|
||||
).execute( context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeDelete(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
return new TableBasedDeleteHandler(
|
||||
sqmDelete,
|
||||
domainParameterXref,
|
||||
idTable,
|
||||
// generally a global temp table should already track a Connection-specific uid,
|
||||
// but just in case a particular env needs it...
|
||||
session -> session.getSessionIdentifier().toString(),
|
||||
idTableExporterAccess,
|
||||
BeforeUseAction.CREATE,
|
||||
afterUseAction,
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
sessionFactory
|
||||
).execute( context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
MappingModelCreationProcess mappingModelCreationProcess,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
if ( prepared ) {
|
||||
return;
|
||||
}
|
||||
|
||||
prepared = true;
|
||||
|
||||
log.debugf( "Creating global-temp ID table : %s", idTable.getTableExpression() );
|
||||
|
||||
final IdTableHelper.IdTableCreationWork idTableCreationWork = new IdTableHelper.IdTableCreationWork(
|
||||
idTable,
|
||||
idTableExporterAccess.get(),
|
||||
sessionFactory
|
||||
);
|
||||
Connection connection;
|
||||
try {
|
||||
connection = connectionAccess.obtainConnection();
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
|
||||
log.debug( "Unable to obtain JDBC connection; assuming ID tables already exist or wont be needed" );
|
||||
return;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable obtain JDBC Connection", e );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
idTableCreationWork.execute( connection );
|
||||
created = true;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
connectionAccess.releaseConnection( connection );
|
||||
}
|
||||
catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
if ( created ) {
|
||||
// todo (6.0) : register strategy for dropping of the table if requested - DROP_ID_TABLES
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
if ( released ) {
|
||||
return;
|
||||
}
|
||||
|
||||
released = true;
|
||||
|
||||
if ( ! created ) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.debugf( "Dropping global-temp ID table : %s", idTable.getTableExpression() );
|
||||
|
||||
final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork(
|
||||
idTable,
|
||||
idTableExporterAccess.get(),
|
||||
sessionFactory
|
||||
);
|
||||
Connection connection;
|
||||
try {
|
||||
connection = connectionAccess.obtainConnection();
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
|
||||
log.debugf( "Unable to obtain JDBC connection; unable to drop global-temp ID table : %s", idTable.getTableExpression() );
|
||||
return;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable obtain JDBC Connection", e );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
idTableDropWork.execute( connection );
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
connectionAccess.releaseConnection( connection );
|
||||
}
|
||||
catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ import java.sql.SQLWarning;
|
|||
import java.sql.Statement;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||
import org.hibernate.engine.jdbc.internal.FormatStyle;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -26,6 +26,7 @@ import org.hibernate.jdbc.AbstractWork;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class IdTableHelper {
|
||||
private final static CoreMessageLogger log = CoreLogging.messageLogger( IdTableHelper.class );
|
||||
|
||||
|
@ -34,35 +35,23 @@ public class IdTableHelper {
|
|||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Creation
|
||||
|
||||
public static void createIdTable(
|
||||
IdTable idTable,
|
||||
IdTableExporter exporter,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
SharedSessionContractImplementor session) {
|
||||
executeWork(
|
||||
new IdTableCreationWork( idTable, exporter, session ),
|
||||
ddlTransactionHandling,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
private static class IdTableCreationWork extends AbstractWork {
|
||||
public static class IdTableCreationWork extends AbstractWork {
|
||||
private final IdTable idTable;
|
||||
private final IdTableExporter exporter;
|
||||
private final SharedSessionContractImplementor session;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
IdTableCreationWork(
|
||||
public IdTableCreationWork(
|
||||
IdTable idTable,
|
||||
IdTableExporter exporter,
|
||||
SharedSessionContractImplementor session) {
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.idTable = idTable;
|
||||
this.exporter = exporter;
|
||||
this.session = session;
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Connection connection) {
|
||||
final JdbcServices jdbcServices = session.getFactory().getJdbcServices();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
|
||||
try {
|
||||
final String creationCommand = exporter.getSqlCreateCommand( idTable );
|
||||
|
@ -91,35 +80,23 @@ public class IdTableHelper {
|
|||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Drop
|
||||
|
||||
public static void dropIdTable(
|
||||
IdTable idTable,
|
||||
IdTableExporter exporter,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
SharedSessionContractImplementor session) {
|
||||
executeWork(
|
||||
new IdTableDropWork( idTable, exporter, session ),
|
||||
ddlTransactionHandling,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
private static class IdTableDropWork extends AbstractWork {
|
||||
public static class IdTableDropWork extends AbstractWork {
|
||||
private final IdTable idTable;
|
||||
private final IdTableExporter exporter;
|
||||
private final SharedSessionContractImplementor session;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
IdTableDropWork(
|
||||
IdTable idTable,
|
||||
IdTableExporter exporter,
|
||||
SharedSessionContractImplementor session) {
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.idTable = idTable;
|
||||
this.exporter = exporter;
|
||||
this.session = session;
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Connection connection) {
|
||||
final JdbcServices jdbcServices = session.getFactory().getJdbcServices();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
|
||||
try {
|
||||
final String dropCommand = exporter.getSqlCreateCommand( idTable );
|
||||
|
@ -182,35 +159,6 @@ public class IdTableHelper {
|
|||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Misc
|
||||
|
||||
private static void executeWork(
|
||||
AbstractWork work,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) {
|
||||
// simply execute the work using a Connection obtained from JdbcConnectionAccess
|
||||
//
|
||||
// NOTE : we do not (potentially) release the Connection here
|
||||
// via LogicalConnectionImplementor#afterStatement because
|
||||
// for sure we will be immediately using it again to
|
||||
// populate the id table and use it...
|
||||
|
||||
try {
|
||||
work.execute( session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection() );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable to use JDBC Connection to create perform id table management", e );
|
||||
}
|
||||
}
|
||||
else {
|
||||
session.getTransactionCoordinator()
|
||||
.createIsolationDelegate()
|
||||
.delegateWork( work, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT );
|
||||
}
|
||||
}
|
||||
|
||||
private static SqlExceptionHelper.WarningHandler WARNING_HANDLER = new SqlExceptionHelper.WarningHandlerLoggingSupport() {
|
||||
public boolean doProcess() {
|
||||
return log.isDebugEnabled();
|
||||
|
|
|
@ -10,14 +10,12 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
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.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Strategy based on ANSI SQL's definition of a "local temporary table" (local to each db session).
|
||||
|
@ -31,63 +29,73 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg
|
|||
private final Supplier<IdTableExporter> idTableExporterAccess;
|
||||
private final AfterUseAction afterUseAction;
|
||||
private final TempTableDdlTransactionHandling ddlTransactionHandling;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
public LocalTemporaryTableStrategy(
|
||||
IdTable idTable,
|
||||
Supplier<IdTableExporter> idTableExporterAccess,
|
||||
AfterUseAction afterUseAction,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling) {
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.idTable = idTable;
|
||||
this.idTableExporterAccess = idTableExporterAccess;
|
||||
this.afterUseAction = afterUseAction;
|
||||
this.ddlTransactionHandling = ddlTransactionHandling;
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
public LocalTemporaryTableStrategy(
|
||||
IdTable idTable,
|
||||
Function<Integer, String> databaseTypeNameResolver,
|
||||
AfterUseAction afterUseAction,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling) {
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this(
|
||||
idTable,
|
||||
() -> new TempIdTableExporter( true, databaseTypeNameResolver ),
|
||||
afterUseAction,
|
||||
ddlTransactionHandling
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SqmUpdateStatement sqmUpdateStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
return new TableBasedUpdateHandler(
|
||||
sqmUpdateStatement,
|
||||
domainParameterXref, idTable,
|
||||
SharedSessionContractImplementor::getTenantIdentifier,
|
||||
idTableExporterAccess,
|
||||
BeforeUseAction.CREATE,
|
||||
afterUseAction,
|
||||
ddlTransactionHandling,
|
||||
creationContext
|
||||
sessionFactory
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
public int executeUpdate(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
return new TableBasedDeleteHandler(
|
||||
sqmDeleteStatement,
|
||||
ExecutionContext context) {
|
||||
return new TableBasedUpdateHandler(
|
||||
sqmUpdate,
|
||||
domainParameterXref,
|
||||
idTable,
|
||||
SharedSessionContractImplementor::getTenantIdentifier,
|
||||
session -> {
|
||||
throw new UnsupportedOperationException( "Unexpected call to access Session uid" );
|
||||
},
|
||||
idTableExporterAccess,
|
||||
BeforeUseAction.CREATE,
|
||||
afterUseAction,
|
||||
ddlTransactionHandling,
|
||||
creationContext
|
||||
);
|
||||
sessionFactory
|
||||
).execute( context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeDelete(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
return new TableBasedDeleteHandler(
|
||||
sqmDelete,
|
||||
domainParameterXref,
|
||||
idTable,
|
||||
session -> {
|
||||
throw new UnsupportedOperationException( "Unexpected call to access Session uid" );
|
||||
},
|
||||
idTableExporterAccess,
|
||||
BeforeUseAction.CREATE,
|
||||
afterUseAction,
|
||||
ddlTransactionHandling,
|
||||
sessionFactory
|
||||
).execute( context );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,21 @@
|
|||
*/
|
||||
package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
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.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* This is a strategy that mimics temporary tables for databases which do not support
|
||||
|
@ -23,6 +30,8 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
|
||||
private static final Logger log = Logger.getLogger( PersistentTableStrategy.class );
|
||||
|
||||
public static final String SHORT_NAME = "persistent";
|
||||
|
||||
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.persistent.drop_tables";
|
||||
|
@ -30,19 +39,160 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
|
|||
public static final String SCHEMA = "hibernate.hql.bulk_id_strategy.persistent.schema";
|
||||
public static final String CATALOG = "hibernate.hql.bulk_id_strategy.persistent.catalog";
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SqmUpdateStatement sqmUpdateStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
private final IdTable idTable;
|
||||
private final AfterUseAction afterUseAction;
|
||||
private final Supplier<IdTableExporter> idTableExporterAccess;
|
||||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
private boolean prepared;
|
||||
private boolean created;
|
||||
private boolean released;
|
||||
|
||||
public PersistentTableStrategy(
|
||||
IdTable idTable,
|
||||
AfterUseAction afterUseAction,
|
||||
Supplier<IdTableExporter> idTableExporterAccess,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.idTable = idTable;
|
||||
this.afterUseAction = afterUseAction;
|
||||
this.idTableExporterAccess = idTableExporterAccess;
|
||||
this.sessionFactory = sessionFactory;
|
||||
|
||||
if ( afterUseAction == AfterUseAction.DROP ) {
|
||||
throw new IllegalArgumentException( "Persistent ID tables cannot use AfterUseAction.DROP : " + idTable.getTableExpression() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
public int executeUpdate(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
ExecutionContext context) {
|
||||
return new TableBasedUpdateHandler(
|
||||
sqmUpdate,
|
||||
domainParameterXref,
|
||||
idTable,
|
||||
session -> session.getSessionIdentifier().toString(),
|
||||
idTableExporterAccess,
|
||||
BeforeUseAction.CREATE,
|
||||
afterUseAction,
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
sessionFactory
|
||||
).execute( context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeDelete(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
return new TableBasedDeleteHandler(
|
||||
sqmDelete,
|
||||
domainParameterXref,
|
||||
idTable,
|
||||
session -> session.getSessionIdentifier().toString(),
|
||||
idTableExporterAccess,
|
||||
BeforeUseAction.CREATE,
|
||||
afterUseAction,
|
||||
TempTableDdlTransactionHandling.NONE,
|
||||
sessionFactory
|
||||
).execute( context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
MappingModelCreationProcess mappingModelCreationProcess,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
if ( prepared ) {
|
||||
return;
|
||||
}
|
||||
|
||||
prepared = true;
|
||||
|
||||
log.debugf( "Creating persistent ID table : %s", idTable.getTableExpression() );
|
||||
|
||||
final IdTableHelper.IdTableCreationWork idTableCreationWork = new IdTableHelper.IdTableCreationWork(
|
||||
idTable,
|
||||
idTableExporterAccess.get(),
|
||||
sessionFactory
|
||||
);
|
||||
Connection connection;
|
||||
try {
|
||||
connection = connectionAccess.obtainConnection();
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
|
||||
log.debug( "Unable to obtain JDBC connection; assuming ID tables already exist or wont be needed" );
|
||||
return;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable obtain JDBC Connection", e );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
idTableCreationWork.execute( connection );
|
||||
created = true;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
connectionAccess.releaseConnection( connection );
|
||||
}
|
||||
catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
if ( created ) {
|
||||
// todo (6.0) : register strategy for dropping of the table if requested - DROP_ID_TABLES
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
if ( released ) {
|
||||
return;
|
||||
}
|
||||
|
||||
released = true;
|
||||
|
||||
if ( created ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
log.debugf( "Dropping persistent ID table : %s", idTable.getTableExpression() );
|
||||
|
||||
final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork(
|
||||
idTable,
|
||||
idTableExporterAccess.get(),
|
||||
sessionFactory
|
||||
);
|
||||
Connection connection;
|
||||
try {
|
||||
connection = connectionAccess.obtainConnection();
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
|
||||
log.debugf( "Unable to obtain JDBC connection; unable to drop persistent ID table : %s", idTable.getTableExpression() );
|
||||
return;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
log.error( "Unable obtain JDBC Connection", e );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
idTableDropWork.execute( connection );
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
connectionAccess.releaseConnection( connection );
|
||||
}
|
||||
catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,40 +7,32 @@
|
|||
package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
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.mutation.internal.MultiTableSqmMutationConverter;
|
||||
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;
|
||||
|
@ -76,7 +68,9 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
private final Supplier<IdTableExporter> idTableExporterAccess;
|
||||
|
||||
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
|
||||
private final MultiTableSqmMutationConverter converter;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public RestrictedDeleteExecutionDelegate(
|
||||
EntityMappingType entityDescriptor,
|
||||
IdTable idTable,
|
||||
|
@ -86,7 +80,9 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
AfterUseAction afterUseAction,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
Supplier<IdTableExporter> idTableExporterAccess,
|
||||
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
||||
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||
QueryOptions queryOptions,
|
||||
QueryParameterBindings queryParameterBindings,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.entityDescriptor = entityDescriptor;
|
||||
this.idTable = idTable;
|
||||
|
@ -98,54 +94,35 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
this.idTableExporterAccess = idTableExporterAccess;
|
||||
this.sessionUidAccess = sessionUidAccess;
|
||||
this.sessionFactory = sessionFactory;
|
||||
|
||||
converter = new MultiTableSqmMutationConverter(
|
||||
entityDescriptor,
|
||||
domainParameterXref,
|
||||
queryOptions,
|
||||
queryParameterBindings,
|
||||
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 TableGroup deletingTableGroup = converter.getMutatingTableGroup();
|
||||
|
||||
final TableReference hierarchyRootTableReference = deletingTableGroup.resolveTableReference( hierarchyRootTableName );
|
||||
assert hierarchyRootTableReference != null;
|
||||
|
||||
final Map<SqmParameter, List<JdbcParameter>> parameterResolutions;
|
||||
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
|
||||
parameterResolutions = Collections.emptyMap();
|
||||
}
|
||||
else {
|
||||
parameterResolutions = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -158,7 +135,8 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
if ( ! hierarchyRootTableReference.getIdentificationVariable().equals( columnReference.getQualifier() ) ) {
|
||||
needsIdTableWrapper.set( true );
|
||||
}
|
||||
}
|
||||
},
|
||||
parameterResolutions::put
|
||||
);
|
||||
|
||||
boolean needsIdTable = needsIdTableWrapper.get();
|
||||
|
@ -167,7 +145,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
return executeWithIdTable(
|
||||
predicate,
|
||||
deletingTableGroup,
|
||||
converter.getRestrictionSqmParameterResolutions(),
|
||||
parameterResolutions,
|
||||
executionContext
|
||||
);
|
||||
}
|
||||
|
@ -175,7 +153,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
return executeWithoutIdTable(
|
||||
predicate,
|
||||
deletingTableGroup,
|
||||
converter.getRestrictionSqmParameterResolutions(),
|
||||
parameterResolutions,
|
||||
converter.getSqlExpressionResolver(),
|
||||
executionContext
|
||||
);
|
||||
|
@ -399,11 +377,10 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
ExecutionContext executionContext,
|
||||
JdbcParameterBindings jdbcParameterBindings) {
|
||||
final int rows = ExecuteWithIdTableHelper.saveMatchingIdsIntoIdTable(
|
||||
sqmDelete,
|
||||
converter,
|
||||
predicate,
|
||||
idTable,
|
||||
sessionUidAccess,
|
||||
domainParameterXref,
|
||||
jdbcParameterBindings,
|
||||
executionContext
|
||||
);
|
||||
|
@ -468,86 +445,4 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,11 +10,11 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.boot.TempTableDdlTransactionHandling;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.internal.DeleteHandler;
|
||||
import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler;
|
||||
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.exec.spi.ExecutionContext;
|
||||
|
||||
|
@ -50,8 +50,8 @@ public class TableBasedDeleteHandler
|
|||
BeforeUseAction beforeUseAction,
|
||||
AfterUseAction afterUseAction,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
HandlerCreationContext creationContext) {
|
||||
super( sqmDeleteStatement, creationContext );
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( sqmDeleteStatement, sessionFactory );
|
||||
this.idTable = idTable;
|
||||
this.ddlTransactionHandling = ddlTransactionHandling;
|
||||
this.beforeUseAction = beforeUseAction;
|
||||
|
@ -85,6 +85,8 @@ public class TableBasedDeleteHandler
|
|||
ddlTransactionHandling,
|
||||
exporterSupplier,
|
||||
sessionUidAccess,
|
||||
executionContext.getQueryOptions(),
|
||||
executionContext.getQueryParameterBindings(),
|
||||
getSessionFactory()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ 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;
|
||||
|
@ -24,16 +23,14 @@ 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.internal.MultiTableSqmMutationConverter;
|
||||
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
|
||||
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;
|
||||
|
@ -63,11 +60,12 @@ public class TableBasedUpdateHandler
|
|||
private final AfterUseAction afterUseAction;
|
||||
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
|
||||
private final Supplier<IdTableExporter> exporterSupplier;
|
||||
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
|
||||
private final EntityPersister entityDescriptor;
|
||||
|
||||
TableBasedUpdateHandler(
|
||||
SqmUpdateStatement sqmDeleteStatement,
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
IdTable idTable,
|
||||
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||
|
@ -75,8 +73,8 @@ public class TableBasedUpdateHandler
|
|||
BeforeUseAction beforeUseAction,
|
||||
AfterUseAction afterUseAction,
|
||||
TempTableDdlTransactionHandling ddlTransactionHandling,
|
||||
HandlerCreationContext creationContext) {
|
||||
super( sqmDeleteStatement, creationContext );
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( sqmUpdate, sessionFactory );
|
||||
this.idTable = idTable;
|
||||
this.exporterSupplier = exporterSupplier;
|
||||
this.beforeUseAction = beforeUseAction;
|
||||
|
@ -84,6 +82,9 @@ public class TableBasedUpdateHandler
|
|||
this.ddlTransactionHandling = ddlTransactionHandling;
|
||||
this.sessionUidAccess = sessionUidAccess;
|
||||
this.domainParameterXref = domainParameterXref;
|
||||
|
||||
final String targetEntityName = sqmUpdate.getTarget().getEntityName();
|
||||
this.entityDescriptor = sessionFactory.getDomainModel().getEntityDescriptor( targetEntityName );
|
||||
}
|
||||
|
||||
protected SqmUpdateStatement getSqmUpdate() {
|
||||
|
@ -131,21 +132,7 @@ public class TableBasedUpdateHandler
|
|||
|
||||
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 TableGroup updatingTableGroup = converterDelegate.getMutatingTableGroup();
|
||||
|
||||
final TableReference hierarchyRootTableReference = updatingTableGroup.resolveTableReference( hierarchyRootTableName );
|
||||
assert hierarchyRootTableReference != null;
|
||||
|
|
|
@ -23,6 +23,7 @@ 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.mutation.internal.MultiTableSqmMutationConverter;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.ast.SqlAstUpdateTranslator;
|
||||
|
@ -157,15 +158,11 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
|
|||
);
|
||||
|
||||
try {
|
||||
|
||||
final int rows = ExecuteWithIdTableHelper.saveMatchingIdsIntoIdTable(
|
||||
sqmUpdate,
|
||||
sqmConverter,
|
||||
updatingTableGroup,
|
||||
suppliedPredicate,
|
||||
idTable,
|
||||
sessionUidAccess,
|
||||
domainParameterXref,
|
||||
jdbcParameterBindings,
|
||||
executionContext
|
||||
);
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.inline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Junction;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* MatchingIdRestrictionProducer producing a restriction based on a disjunction (OR) predicate. E.g.:
|
||||
*
|
||||
* ````
|
||||
* delete
|
||||
* from
|
||||
* entity-table
|
||||
* where
|
||||
* ( id = 1 )
|
||||
* or ( id = 2 )
|
||||
* or ( id = 3 )
|
||||
* or ( id = 4 )
|
||||
* ````
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DisjunctionRestrictionProducer implements MatchingIdRestrictionProducer {
|
||||
@Override
|
||||
public Junction produceRestriction(
|
||||
List<?> matchingIdValues,
|
||||
EntityMappingType entityDescriptor,
|
||||
TableReference mutatingTableReference,
|
||||
Supplier<Consumer<ColumnConsumer>> columnsToMatchVisitationSupplier,
|
||||
ExecutionContext executionContext) {
|
||||
assert matchingIdValues != null;
|
||||
assert ! matchingIdValues.isEmpty();
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory();
|
||||
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
final int idColumnCount = identifierMapping.getJdbcTypeCount( sessionFactory.getTypeConfiguration() );
|
||||
assert idColumnCount > 0;
|
||||
|
||||
final Junction predicate = new Junction( Junction.Nature.DISJUNCTION );
|
||||
|
||||
if ( idColumnCount == 1 ) {
|
||||
final BasicValuedModelPart basicIdMapping = (BasicValuedModelPart) identifierMapping;
|
||||
final String idColumn = basicIdMapping.getMappedColumnExpression();
|
||||
final ColumnReference idColumnReference = new ColumnReference(
|
||||
mutatingTableReference,
|
||||
idColumn,
|
||||
basicIdMapping.getJdbcMapping(),
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
for ( int i = 0; i < matchingIdValues.size(); i++ ) {
|
||||
final Object matchingId = matchingIdValues.get( i );
|
||||
|
||||
predicate.add(
|
||||
new ComparisonPredicate(
|
||||
idColumnReference,
|
||||
ComparisonOperator.EQUAL,
|
||||
new JdbcLiteral<>( matchingId, basicIdMapping.getJdbcMapping() )
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
final List<ColumnReference> columnReferences = new ArrayList<>( idColumnCount );
|
||||
final List<JdbcMapping> jdbcMappings = new ArrayList<>( idColumnCount );
|
||||
identifierMapping.visitColumns(
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||
columnReferences.add( new ColumnReference( mutatingTableReference, columnExpression, jdbcMapping, sessionFactory ) );
|
||||
jdbcMappings.add( jdbcMapping );
|
||||
}
|
||||
);
|
||||
|
||||
for ( int i = 0; i < matchingIdValues.size(); i++ ) {
|
||||
final Junction idMatch = new Junction( Junction.Nature.CONJUNCTION );
|
||||
|
||||
final Object matchingId = matchingIdValues.get( i );
|
||||
assert matchingId instanceof Object[];
|
||||
|
||||
final Object[] matchingIdParts = (Object[]) matchingId;
|
||||
|
||||
for ( int p = 0; p < matchingIdParts.length; p++ ) {
|
||||
idMatch.add(
|
||||
new ComparisonPredicate(
|
||||
columnReferences.get( p ),
|
||||
ComparisonOperator.EQUAL,
|
||||
new JdbcLiteral<>( matchingIdParts[ p ], jdbcMappings.get( p ) )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
predicate.add( idMatch );
|
||||
}
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.inline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* MatchingIdRestrictionProducer producing a restriction based on an in-values-list predicate. E.g.:
|
||||
*
|
||||
* ````
|
||||
* delete
|
||||
* from
|
||||
* entity-table
|
||||
* where
|
||||
* ( id ) in (
|
||||
* ( 1 ),
|
||||
* ( 2 ),
|
||||
* ( 3 ),
|
||||
* ( 4 )
|
||||
* )
|
||||
* ````
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class InPredicateRestrictionProducer implements MatchingIdRestrictionProducer {
|
||||
@Override
|
||||
public InListPredicate produceRestriction(
|
||||
List<?> matchingIdValues,
|
||||
EntityMappingType entityDescriptor,
|
||||
TableReference mutatingTableReference,
|
||||
Supplier<Consumer<ColumnConsumer>> columnsToMatchVisitationSupplier,
|
||||
ExecutionContext executionContext) {
|
||||
assert matchingIdValues != null;
|
||||
assert ! matchingIdValues.isEmpty();
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory();
|
||||
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
final int idColumnCount = identifierMapping.getJdbcTypeCount( sessionFactory.getTypeConfiguration() );
|
||||
assert idColumnCount > 0;
|
||||
|
||||
final InListPredicate predicate;
|
||||
|
||||
if ( idColumnCount == 1 ) {
|
||||
final BasicValuedModelPart basicIdMapping = (BasicValuedModelPart) identifierMapping;
|
||||
final String idColumn = basicIdMapping.getMappedColumnExpression();
|
||||
final Expression inFixture = new ColumnReference(
|
||||
mutatingTableReference,
|
||||
idColumn,
|
||||
basicIdMapping.getJdbcMapping(),
|
||||
sessionFactory
|
||||
);
|
||||
predicate = new InListPredicate( inFixture );
|
||||
|
||||
for ( int i = 0; i < matchingIdValues.size(); i++ ) {
|
||||
final Object matchingId = matchingIdValues.get( i );
|
||||
predicate.addExpression( new JdbcLiteral<>( matchingId, basicIdMapping.getJdbcMapping() ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
final List<ColumnReference> columnReferences = new ArrayList<>( idColumnCount );
|
||||
final List<JdbcMapping> jdbcMappings = new ArrayList<>( idColumnCount );
|
||||
identifierMapping.visitColumns(
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> {
|
||||
columnReferences.add( new ColumnReference( mutatingTableReference, columnExpression, jdbcMapping, sessionFactory ) );
|
||||
jdbcMappings.add( jdbcMapping );
|
||||
}
|
||||
);
|
||||
|
||||
final Expression inFixture = new SqlTuple( columnReferences, identifierMapping );
|
||||
predicate = new InListPredicate( inFixture );
|
||||
|
||||
for ( int i = 0; i < matchingIdValues.size(); i++ ) {
|
||||
final Object matchingId = matchingIdValues.get( i );
|
||||
assert matchingId instanceof Object[];
|
||||
final Object[] matchingIdParts = (Object[]) matchingId;
|
||||
|
||||
final List<JdbcLiteral> tupleParts = new ArrayList<>( idColumnCount );
|
||||
for ( int p = 0; p < matchingIdParts.length; p++ ) {
|
||||
tupleParts.add(
|
||||
new JdbcLiteral<>( matchingIdParts[p],jdbcMappings.get( p ) )
|
||||
);
|
||||
}
|
||||
|
||||
predicate.addExpression( new SqlTuple( tupleParts, identifierMapping ) );
|
||||
}
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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.inline;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.internal.DeleteHandler;
|
||||
import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper;
|
||||
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.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcDelete;
|
||||
import org.hibernate.sql.exec.spi.JdbcMutationExecutor;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.spi.StatementCreatorHelper;
|
||||
|
||||
/**
|
||||
* DeleteHandler for the in-line strategy
|
||||
*
|
||||
* @author Evandro Pires da Silva
|
||||
* @author Vlad Mihalcea
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class InlineDeleteHandler implements DeleteHandler {
|
||||
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
||||
private final SqmDeleteStatement sqmDeleteStatement;
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
|
||||
private final ExecutionContext executionContext;
|
||||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final SqlAstTranslatorFactory sqlAstTranslatorFactory;
|
||||
private final JdbcMutationExecutor jdbcMutationExecutor;
|
||||
|
||||
protected InlineDeleteHandler(
|
||||
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
this.sqmDeleteStatement = sqmDeleteStatement;
|
||||
|
||||
this.domainParameterXref = domainParameterXref;
|
||||
this.matchingIdsPredicateProducer = matchingIdsPredicateProducer;
|
||||
|
||||
this.executionContext = context;
|
||||
|
||||
this.sessionFactory = executionContext.getSession().getFactory();
|
||||
this.sqlAstTranslatorFactory = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
|
||||
this.jdbcMutationExecutor = sessionFactory.getJdbcServices().getJdbcMutationExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(ExecutionContext executionContext) {
|
||||
final List<Object> ids = MatchingIdSelectionHelper.selectMatchingIds(
|
||||
sqmDeleteStatement,
|
||||
domainParameterXref,
|
||||
executionContext
|
||||
);
|
||||
|
||||
if ( ids == null || ids.isEmpty() ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||
|
||||
final String mutatingEntityName = sqmDeleteStatement.getTarget().getModel().getHibernateEntityName();
|
||||
final EntityMappingType entityDescriptor = factory.getDomainModel().getEntityDescriptor( mutatingEntityName );
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( domainParameterXref.getQueryParameterCount() );
|
||||
|
||||
// delete from the tables
|
||||
|
||||
entityDescriptor.visitAttributeMappings(
|
||||
attribute -> {
|
||||
if ( attribute instanceof PluralAttributeMapping ) {
|
||||
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
|
||||
|
||||
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
|
||||
// this collection has a separate collection table, meaning it is one of:
|
||||
// 1) element-collection
|
||||
// 2) many-to-many
|
||||
// 3) one-to many using a dedicated join-table
|
||||
//
|
||||
// in all of these cases, we should clean up the matching rows in the
|
||||
// collection table
|
||||
|
||||
// todo (6.0) : implement this
|
||||
// executeDelete(
|
||||
// pluralAttribute.getSeparateCollectionTable(),
|
||||
// matchingIdsPredicateProducer.produceRestriction(
|
||||
// ids,
|
||||
// () -> columnConsumer -> ,
|
||||
// executionContext
|
||||
// ),
|
||||
// jdbcParameterBindings,
|
||||
// executionContext
|
||||
// );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
entityDescriptor.visitConstraintOrderedTables(
|
||||
(tableExpression, tableKeyColumnsVisitationSupplier) -> {
|
||||
executeDelete(
|
||||
tableExpression,
|
||||
entityDescriptor,
|
||||
tableKeyColumnsVisitationSupplier,
|
||||
ids,
|
||||
jdbcParameterBindings,
|
||||
executionContext
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return ids.size();
|
||||
}
|
||||
|
||||
private void executeDelete(
|
||||
String targetTableExpression,
|
||||
EntityMappingType entityDescriptor,
|
||||
Supplier<Consumer<ColumnConsumer>> tableKeyColumnsVisitationSupplier,
|
||||
List<Object> ids,
|
||||
JdbcParameterBindings jdbcParameterBindings,
|
||||
ExecutionContext executionContext) {
|
||||
final TableReference targetTableReference = new TableReference(
|
||||
targetTableExpression,
|
||||
null,
|
||||
false,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
final Predicate matchingIdsPredicate = matchingIdsPredicateProducer.produceRestriction(
|
||||
ids,
|
||||
entityDescriptor,
|
||||
targetTableReference,
|
||||
tableKeyColumnsVisitationSupplier,
|
||||
executionContext
|
||||
);
|
||||
|
||||
final DeleteStatement deleteStatement = new DeleteStatement( targetTableReference, matchingIdsPredicate );
|
||||
|
||||
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( sessionFactory );
|
||||
final JdbcDelete jdbcOperation = sqlAstTranslator.translate( deleteStatement );
|
||||
|
||||
jdbcMutationExecutor.execute(
|
||||
jdbcOperation,
|
||||
jdbcParameterBindings,
|
||||
this::prepareQueryStatement,
|
||||
(integer, preparedStatement) -> {},
|
||||
executionContext
|
||||
);
|
||||
}
|
||||
|
||||
private PreparedStatement prepareQueryStatement(String sql) {
|
||||
return StatementCreatorHelper.prepareQueryStatement( sql, executionContext.getSession() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.inline;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Support for multi-table SQM mutation operations which select the matching id values from the database back into
|
||||
* the VM and uses that list of values to produce a restriction for the mutations. The exact form of that
|
||||
* restriction is based on the {@link MatchingIdRestrictionProducer} implementation used
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class InlineStrategy implements SqmMultiTableMutationStrategy {
|
||||
private final Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy;
|
||||
|
||||
public InlineStrategy(Dialect dialect) {
|
||||
this( determinePredicateProducer( dialect ) );
|
||||
}
|
||||
|
||||
private static Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> determinePredicateProducer(Dialect dialect) {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
}
|
||||
|
||||
public InlineStrategy(Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy) {
|
||||
this.matchingIdsStrategy = matchingIdsStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
final InlineUpdateHandler handler = new InlineUpdateHandler(
|
||||
matchingIdsStrategy.apply( sqmUpdate ),
|
||||
sqmUpdate,
|
||||
domainParameterXref,
|
||||
context
|
||||
);
|
||||
return handler.execute( context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeDelete(
|
||||
SqmDeleteStatement sqmDelete,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
final InlineDeleteHandler deleteHandler = new InlineDeleteHandler(
|
||||
matchingIdsStrategy.apply( sqmDelete ),
|
||||
sqmDelete,
|
||||
domainParameterXref,
|
||||
context
|
||||
);
|
||||
|
||||
return deleteHandler.execute( context );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.inline;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcMutationExecutor;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class InlineUpdateHandler implements UpdateHandler {
|
||||
private final SqmUpdateStatement sqmUpdate;
|
||||
private final DomainParameterXref domainParameterXref;
|
||||
private final MatchingIdRestrictionProducer matchingIdsPredicateProducer;
|
||||
|
||||
private final ExecutionContext executionContext;
|
||||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final SqlAstTranslatorFactory sqlAstTranslatorFactory;
|
||||
private final JdbcMutationExecutor jdbcMutationExecutor;
|
||||
|
||||
public InlineUpdateHandler(
|
||||
MatchingIdRestrictionProducer matchingIdsPredicateProducer,
|
||||
SqmUpdateStatement sqmUpdate,
|
||||
DomainParameterXref domainParameterXref,
|
||||
ExecutionContext context) {
|
||||
this.matchingIdsPredicateProducer = matchingIdsPredicateProducer;
|
||||
this.domainParameterXref = domainParameterXref;
|
||||
this.sqmUpdate = sqmUpdate;
|
||||
|
||||
this.executionContext = context;
|
||||
|
||||
this.sessionFactory = executionContext.getSession().getFactory();
|
||||
this.sqlAstTranslatorFactory = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
|
||||
this.jdbcMutationExecutor = sessionFactory.getJdbcServices().getJdbcMutationExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(ExecutionContext executionContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.query.sqm.mutation.internal.inline;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Strategy (pattern) for producing the restriction used when mutating a
|
||||
* particular table in the group of tables
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface MatchingIdRestrictionProducer {
|
||||
/**
|
||||
* Produce the restriction predicate
|
||||
*
|
||||
* @param matchingIdValues The matching id values.
|
||||
* @param mutatingTableReference The TableReference for the table being mutated
|
||||
* @param columnsToMatchVisitationSupplier The columns against which to restrict the mutations
|
||||
*/
|
||||
Predicate produceRestriction(
|
||||
List<?> matchingIdValues,
|
||||
EntityMappingType entityDescriptor,
|
||||
TableReference mutatingTableReference,
|
||||
Supplier<Consumer<ColumnConsumer>> columnsToMatchVisitationSupplier,
|
||||
ExecutionContext executionContext);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.inline;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.metamodel.mapping.ColumnConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* MatchingIdRestrictionProducer producing a restriction based on a SQL table-value-constructor. E.g.:
|
||||
*
|
||||
* ````
|
||||
* delete
|
||||
* from
|
||||
* entity-table
|
||||
* where
|
||||
* ( id ) in (
|
||||
* select
|
||||
* id
|
||||
* from (
|
||||
* values
|
||||
* ( 1 ),
|
||||
* ( 2 ),
|
||||
* ( 3 ),
|
||||
* ( 4 )
|
||||
* ) as HT (id)
|
||||
* )
|
||||
* ````
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class TableValueConstructorRestrictionProducer implements MatchingIdRestrictionProducer {
|
||||
@Override
|
||||
public InSubQueryPredicate produceRestriction(
|
||||
List<?> matchingIdValues,
|
||||
EntityMappingType entityDescriptor,
|
||||
TableReference mutatingTableReference,
|
||||
Supplier<Consumer<ColumnConsumer>> columnsToMatchVisitationSupplier,
|
||||
ExecutionContext executionContext) {
|
||||
// Not "yet" implemented. Not sure we will. This requires the ability to define
|
||||
// "in-line views" with a table-ctor which the SQL AST does not yet define support for
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ package org.hibernate.query.sqm.mutation.spi;
|
|||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.query.sqm.mutation.internal.Handler;
|
||||
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||
|
||||
/**
|
||||
|
@ -21,9 +22,9 @@ public abstract class AbstractMutationHandler implements Handler {
|
|||
|
||||
public AbstractMutationHandler(
|
||||
SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement,
|
||||
HandlerCreationContext creationContext) {
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.sqmDeleteOrUpdateStatement = sqmDeleteOrUpdateStatement;
|
||||
this.sessionFactory = creationContext.getSessionFactory();
|
||||
this.sessionFactory = sessionFactory;
|
||||
|
||||
final String entityName = sqmDeleteOrUpdateStatement.getTarget()
|
||||
.getReferencedPathSource()
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
||||
/**
|
||||
* Parameter object (pattern) for contextual information for
|
||||
* {@link SqmMultiTableMutationStrategy#buildUpdateHandler} and
|
||||
* {@link SqmMultiTableMutationStrategy#buildDeleteHandler}
|
||||
*/
|
||||
public interface HandlerCreationContext {
|
||||
/**
|
||||
* Access to the SessionFactory
|
||||
*/
|
||||
SessionFactoryImplementor getSessionFactory();
|
||||
}
|
|
@ -9,27 +9,24 @@ package org.hibernate.query.sqm.mutation.spi;
|
|||
import org.hibernate.Metamodel;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Pluggable strategy for defining how mutation (`UPDATE` or `DELETE`)
|
||||
* queries should be handled when the target entity is mapped to multiple
|
||||
* tables (generally via secondary tables or joined-inheritance).
|
||||
* Pluggable strategy for defining how mutation (`UPDATE` or `DELETE`) queries should be handled when the target
|
||||
* entity is mapped to multiple tables via secondary tables or certain inheritance strategies.
|
||||
*
|
||||
* {@link #prepare} and {@link #release} allow the strategy to perform
|
||||
* any one time preparation and cleanup.
|
||||
* The main contracts here are {@link #executeUpdate} and {@link #executeDelete}.
|
||||
*
|
||||
* The heavy lifting is handled by the {@link UpdateHandler} and {@link DeleteHandler}
|
||||
* delegates obtained via {@link #buildUpdateHandler} and {@link #buildDeleteHandler}
|
||||
* methods.
|
||||
* {@link #prepare} and {@link #release} allow the strategy to perform any one time preparation and cleanup.
|
||||
*
|
||||
* @apiNote See {@link SqmMutationStrategyHelper#resolveStrategy} for standard resolution
|
||||
* of the strategy to use. See also {@link SqmMutationStrategyHelper#resolveDeleteHandler}
|
||||
* and {@link SqmMutationStrategyHelper#resolveUpdateHandler} for standard resolution of
|
||||
* the delete and update handler to use applying standard special-case handling
|
||||
* @apiNote See {@link SqmMutationStrategyHelper#resolveStrategy} for standard resolution of the strategy to use
|
||||
* for each hierarchy
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -40,8 +37,7 @@ public interface SqmMultiTableMutationStrategy {
|
|||
* is being built.
|
||||
*/
|
||||
default void prepare(
|
||||
Metamodel runtimeMetadata,
|
||||
SessionFactoryOptions sessionFactoryOptions,
|
||||
MappingModelCreationProcess mappingModelCreationProcess,
|
||||
JdbcConnectionAccess connectionAccess) {
|
||||
// by default, nothing to do...
|
||||
}
|
||||
|
@ -49,35 +45,28 @@ public interface SqmMultiTableMutationStrategy {
|
|||
/**
|
||||
* Release the strategy. Called one time as the SessionFactory is
|
||||
* being shut down.
|
||||
*
|
||||
* @param runtimeMetadata Access to the runtime mappings
|
||||
* @param connectionAccess Access to the JDBC Connection
|
||||
*/
|
||||
default void release(Metamodel runtimeMetadata, JdbcConnectionAccess connectionAccess) {
|
||||
default void release(SessionFactoryImplementor sessionFactory, JdbcConnectionAccess connectionAccess) {
|
||||
// by default, nothing to do...
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a handler capable of handling the update query indicated by the given SQM tree.
|
||||
* Execute the multi-table update indicated by the passed SqmUpdateStatement
|
||||
*
|
||||
* @param sqmUpdateStatement The SQM AST representing the update query
|
||||
* @param domainParameterXref cross references between SqmParameters and QueryParameters
|
||||
* @param creationContext Context info for the creation
|
||||
* @return The number of rows affected
|
||||
*/
|
||||
UpdateHandler buildUpdateHandler(
|
||||
int executeUpdate(
|
||||
SqmUpdateStatement sqmUpdateStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext);
|
||||
ExecutionContext context);
|
||||
|
||||
/**
|
||||
* Build a handler capable of handling the delete query indicated by the given SQM tree.
|
||||
* Execute the multi-table update indicated by the passed SqmUpdateStatement
|
||||
*
|
||||
* @param sqmDeleteStatement The SQM AST representing the delete query
|
||||
* @param domainParameterXref cross references between SqmParameters
|
||||
* @param creationContext Context info for the creation
|
||||
* @return The number of rows affected
|
||||
*/
|
||||
DeleteHandler buildDeleteHandler(
|
||||
int executeDelete(
|
||||
SqmDeleteStatement sqmDeleteStatement,
|
||||
DomainParameterXref domainParameterXref,
|
||||
HandlerCreationContext creationContext);
|
||||
ExecutionContext context);
|
||||
}
|
||||
|
|
|
@ -764,8 +764,7 @@ public abstract class BaseSqmToSqlAstConverter
|
|||
literal,
|
||||
getCreationContext().getDomainModel(),
|
||||
getFromClauseAccess()::findTableGroup
|
||||
),
|
||||
getCurrentClauseStack().getCurrent()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1450,8 +1449,7 @@ public abstract class BaseSqmToSqlAstConverter
|
|||
public Object visitEnumLiteral(SqmEnumLiteral sqmEnumLiteral) {
|
||||
return new QueryLiteral(
|
||||
sqmEnumLiteral.getEnumValue(),
|
||||
(BasicValuedMapping) determineValueMapping( sqmEnumLiteral ),
|
||||
getCurrentClauseStack().getCurrent()
|
||||
(BasicValuedMapping) determineValueMapping( sqmEnumLiteral )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1459,8 +1457,7 @@ public abstract class BaseSqmToSqlAstConverter
|
|||
public Object visitFieldLiteral(SqmFieldLiteral sqmFieldLiteral) {
|
||||
return new QueryLiteral(
|
||||
sqmFieldLiteral.getValue(),
|
||||
(BasicValuedMapping) determineValueMapping( sqmFieldLiteral ),
|
||||
getCurrentClauseStack().getCurrent()
|
||||
(BasicValuedMapping) determineValueMapping( sqmFieldLiteral )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
|||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||
|
@ -445,9 +446,15 @@ public class SqlTreePrinter implements SqlAstWalker {
|
|||
// logNode( "positional-param (%s)", parameter.getPosition() );
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public void visitJdbcLiteral(JdbcLiteral jdbcLiteral) {
|
||||
logNode( "literal (" + jdbcLiteral.getLiteralValue() + ')' );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQueryLiteral(QueryLiteral queryLiteral) {
|
||||
logNode( "literal (" + queryLiteral.getValue() + ')' );
|
||||
logNode( "literal (" + queryLiteral.getLiteralValue() + ')' );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.internal.util.collections.StandardStack;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.QueryLiteralRendering;
|
||||
import org.hibernate.query.UnaryArithmeticOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
|
@ -25,6 +26,8 @@ import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
|||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||
|
@ -792,6 +795,12 @@ public abstract class AbstractSqlAstWalker
|
|||
// visitJdbcParameterBinder( positionalParameter );
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public void visitJdbcLiteral(JdbcLiteral jdbcLiteral) {
|
||||
renderAsLiteral( jdbcLiteral );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQueryLiteral(QueryLiteral queryLiteral) {
|
||||
final QueryLiteralRendering queryLiteralRendering = getSessionFactory().getSessionFactoryOptions().getQueryLiteralRenderingMode();
|
||||
|
@ -807,7 +816,7 @@ public abstract class AbstractSqlAstWalker
|
|||
}
|
||||
case AUTO:
|
||||
case AS_PARAM_OUTSIDE_SELECT: {
|
||||
if ( queryLiteral.isInSelect() ) {
|
||||
if ( clauseStack.getCurrent() == Clause.SELECT ) {
|
||||
renderAsLiteral( queryLiteral );
|
||||
}
|
||||
else {
|
||||
|
@ -824,25 +833,21 @@ public abstract class AbstractSqlAstWalker
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void renderAsLiteral(QueryLiteral<?> queryLiteral) {
|
||||
if ( queryLiteral.getValue() == null ) {
|
||||
private void renderAsLiteral(Literal literal) {
|
||||
if ( literal.getLiteralValue() == null ) {
|
||||
// todo : not sure we allow this "higher up"
|
||||
appendSql( SqlAppender.NULL_KEYWORD );
|
||||
}
|
||||
else {
|
||||
assert queryLiteral.getExpressionType().getJdbcTypeCount( getTypeConfiguration() ) == 1;
|
||||
queryLiteral.visitJdbcTypes(
|
||||
jdbcMapping -> {
|
||||
final JdbcLiteralFormatter literalFormatter = jdbcMapping.getSqlTypeDescriptor().getJdbcLiteralFormatter( jdbcMapping.getJavaTypeDescriptor() );
|
||||
appendSql(
|
||||
literalFormatter.toJdbcLiteral(
|
||||
queryLiteral.getValue(),
|
||||
dialect,
|
||||
null
|
||||
)
|
||||
);
|
||||
},
|
||||
getTypeConfiguration()
|
||||
assert literal.getExpressionType().getJdbcTypeCount( getTypeConfiguration() ) == 1;
|
||||
final JdbcMapping jdbcMapping = literal.getJdbcMapping();
|
||||
final JdbcLiteralFormatter literalFormatter = jdbcMapping.getSqlTypeDescriptor().getJdbcLiteralFormatter( jdbcMapping.getJavaTypeDescriptor() );
|
||||
appendSql(
|
||||
literalFormatter.toJdbcLiteral(
|
||||
literal.getLiteralValue(),
|
||||
dialect,
|
||||
null
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public class DerbyCaseExpressionWalker implements CaseExpressionWalker {
|
|||
if ( otherwise != null ) {
|
||||
sqlBuffer.append( " else " );
|
||||
if ( otherwise instanceof QueryLiteral ) {
|
||||
Object value = ( (QueryLiteral) otherwise ).getValue();
|
||||
Object value = ( (QueryLiteral) otherwise ).getLiteralValue();
|
||||
if ( value == null ) {
|
||||
// null is not considered the same type as Integer.
|
||||
sqlBuffer.append( "-1" );
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
|||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||
|
@ -99,6 +100,8 @@ public interface SqlAstWalker {
|
|||
|
||||
void visitParameter(JdbcParameter jdbcParameter);
|
||||
|
||||
void visitJdbcLiteral(JdbcLiteral jdbcLiteral);
|
||||
|
||||
void visitQueryLiteral(QueryLiteral queryLiteral);
|
||||
|
||||
void visitUnaryOperationExpression(UnaryOperation unaryOperationExpression);
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.hibernate.metamodel.mapping.Bindable;
|
|||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||
|
@ -29,7 +29,6 @@ import org.hibernate.sql.exec.spi.JdbcParameter;
|
|||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Describes the table definition for the CTE - its name amd its columns
|
||||
|
@ -41,10 +40,10 @@ public class CteTable {
|
|||
|
||||
private final List<CteColumn> cteColumns;
|
||||
|
||||
public CteTable(EntityMappingType entityDescriptor, TypeConfiguration typeConfiguration) {
|
||||
public CteTable(EntityMappingType entityDescriptor) {
|
||||
this.sessionFactory = entityDescriptor.getEntityPersister().getFactory();
|
||||
|
||||
final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount( typeConfiguration );
|
||||
final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount( sessionFactory.getTypeConfiguration() );
|
||||
cteColumns = new ArrayList<>( numberOfColumns );
|
||||
entityDescriptor.getIdentifierMapping().visitColumns(
|
||||
(columnExpression, containingTableExpression, jdbcMapping) -> cteColumns.add(
|
||||
|
@ -62,7 +61,7 @@ public class CteTable {
|
|||
}
|
||||
|
||||
public String getTableExpression() {
|
||||
return CteBasedMutationStrategy.TABLE_NAME;
|
||||
return CteStrategy.TABLE_NAME;
|
||||
}
|
||||
|
||||
public List<CteColumn> getCteColumns() {
|
||||
|
@ -150,13 +149,13 @@ public class CteTable {
|
|||
|
||||
return new TableReference(
|
||||
tableValueCtorExpressionBuffer.toString(),
|
||||
CteBasedMutationStrategy.TABLE_NAME,
|
||||
CteStrategy.TABLE_NAME,
|
||||
false,
|
||||
sessionFactory
|
||||
);
|
||||
}
|
||||
|
||||
public QuerySpec createCteSubQuery(ExecutionContext executionContext) {
|
||||
public QuerySpec createCteSubQuery(@SuppressWarnings("unused") ExecutionContext executionContext) {
|
||||
final QuerySpec querySpec = new QuerySpec( false );
|
||||
|
||||
final TableReference cteTableReference = new TableReference(
|
||||
|
|
|
@ -15,7 +15,7 @@ import java.util.function.Supplier;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy;
|
||||
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
|
@ -31,8 +31,9 @@ public class CteTableGroup implements TableGroup {
|
|||
private final NavigablePath navigablePath;
|
||||
private final TableReference cteTableReference;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public CteTableGroup(TableReference cteTableReference) {
|
||||
this.navigablePath = new NavigablePath( CteBasedMutationStrategy.TABLE_NAME );
|
||||
this.navigablePath = new NavigablePath( CteStrategy.TABLE_NAME );
|
||||
this.cteTableReference = cteTableReference;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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.expression;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.sql.results.internal.domain.basic.BasicResult;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Represents a literal in the SQL AST. This form accepts a {@link JdbcMapping} and acts
|
||||
* as its own MappingModelExpressable.
|
||||
*
|
||||
* @see QueryLiteral
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class JdbcLiteral<T> implements Literal, MappingModelExpressable<T>, DomainResultProducer<T> {
|
||||
private final T literalValue;
|
||||
private final JdbcMapping jdbcMapping;
|
||||
|
||||
public JdbcLiteral(T literalValue, JdbcMapping jdbcMapping) {
|
||||
this.literalValue = literalValue;
|
||||
this.jdbcMapping = jdbcMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLiteralValue() {
|
||||
return literalValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMapping getJdbcMapping() {
|
||||
return jdbcMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker sqlTreeWalker) {
|
||||
sqlTreeWalker.visitJdbcLiteral( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindParameterValue(
|
||||
PreparedStatement statement,
|
||||
int startPosition,
|
||||
JdbcParameterBindings jdbcParameterBindings,
|
||||
ExecutionContext executionContext) throws SQLException {
|
||||
//noinspection unchecked
|
||||
jdbcMapping.getJdbcValueBinder().bind(
|
||||
statement,
|
||||
literalValue,
|
||||
startPosition,
|
||||
executionContext.getSession()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// MappingModelExpressable
|
||||
|
||||
@Override
|
||||
public MappingModelExpressable getExpressionType() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCount(TypeConfiguration typeConfiguration) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JdbcMapping> getJdbcMappings(TypeConfiguration typeConfiguration) {
|
||||
return Collections.singletonList( jdbcMapping );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJdbcTypes(Consumer<JdbcMapping> action, Clause clause, TypeConfiguration typeConfiguration) {
|
||||
action.accept( jdbcMapping );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDisassembledJdbcValues(
|
||||
Object value,
|
||||
Clause clause,
|
||||
JdbcValuesConsumer valuesConsumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
valuesConsumer.consume( value, jdbcMapping );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJdbcValues(
|
||||
Object value,
|
||||
Clause clause,
|
||||
JdbcValuesConsumer valuesConsumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
valuesConsumer.consume( value, jdbcMapping );
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// DomainResultProducer
|
||||
|
||||
@Override
|
||||
public void visitJdbcTypes(Consumer<JdbcMapping> action, TypeConfiguration typeConfiguration) {
|
||||
action.accept( jdbcMapping );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<T> createDomainResult(String resultVariable, DomainResultCreationState creationState) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||
|
||||
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
|
||||
this,
|
||||
jdbcMapping.getJavaTypeDescriptor(),
|
||||
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
|
||||
);
|
||||
|
||||
//noinspection unchecked
|
||||
return new BasicResult( sqlSelection.getValuesArrayPosition(), resultVariable, jdbcMapping.getJavaTypeDescriptor() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySqlSelections(DomainResultCreationState creationState) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||
|
||||
sqlExpressionResolver.resolveSqlSelection(
|
||||
this,
|
||||
jdbcMapping.getJavaTypeDescriptor(),
|
||||
sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSelection createSqlSelection(
|
||||
int jdbcPosition,
|
||||
int valuesArrayPosition,
|
||||
JavaTypeDescriptor javaTypeDescriptor,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
return new SqlSelectionImpl(
|
||||
jdbcPosition,
|
||||
valuesArrayPosition,
|
||||
this,
|
||||
jdbcMapping
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.expression;
|
||||
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface Literal extends JdbcParameterBinder, Expression {
|
||||
Object getLiteralValue();
|
||||
JdbcMapping getJdbcMapping();
|
||||
}
|
|
@ -6,22 +6,117 @@
|
|||
*/
|
||||
package org.hibernate.sql.ast.tree.expression;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.sql.results.internal.domain.basic.BasicResult;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* A literal specified in the source query.
|
||||
* Represents a literal in the SQL AST. This form accepts a {@link BasicValuedMapping} is its MappingModelExpressable.
|
||||
*
|
||||
* @see JdbcLiteral
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class QueryLiteral<T> extends AbstractLiteral<T> {
|
||||
public QueryLiteral(Object value, BasicValuedMapping expressableType, Clause clause) {
|
||||
super( value, expressableType, clause );
|
||||
public class QueryLiteral<T> implements Literal, DomainResultProducer<T> {
|
||||
private final Object value;
|
||||
private final BasicValuedMapping type;
|
||||
|
||||
public QueryLiteral(Object value, BasicValuedMapping type) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLiteralValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMapping getJdbcMapping() {
|
||||
return type.getJdbcMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker walker) {
|
||||
walker.visitQueryLiteral( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicValuedMapping getExpressionType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<T> createDomainResult(
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
|
||||
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
|
||||
this,
|
||||
type.getMappedTypeDescriptor().getMappedJavaTypeDescriptor(),
|
||||
creationState.getSqlAstCreationState()
|
||||
.getCreationContext()
|
||||
.getSessionFactory()
|
||||
.getTypeConfiguration()
|
||||
);
|
||||
|
||||
//noinspection unchecked
|
||||
return new BasicResult<>(
|
||||
sqlSelection.getValuesArrayPosition(),
|
||||
resultVariable,
|
||||
type.getMappedTypeDescriptor().getMappedJavaTypeDescriptor()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSelection createSqlSelection(
|
||||
int jdbcPosition,
|
||||
int valuesArrayPosition,
|
||||
JavaTypeDescriptor javaTypeDescriptor,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
return new SqlSelectionImpl(
|
||||
jdbcPosition,
|
||||
valuesArrayPosition,
|
||||
this,
|
||||
type.getJdbcMapping()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJdbcTypes(
|
||||
Consumer<JdbcMapping> action,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
action.accept( type.getJdbcMapping() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindParameterValue(
|
||||
PreparedStatement statement,
|
||||
int startPosition,
|
||||
JdbcParameterBindings jdbcParameterBindings,
|
||||
ExecutionContext executionContext) throws SQLException {
|
||||
//noinspection unchecked
|
||||
( (BasicType) getExpressionType() ).getJdbcValueBinder().bind(
|
||||
statement,
|
||||
getLiteralValue(),
|
||||
startPosition,
|
||||
executionContext.getSession()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.exec.spi;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.resource.jdbc.LogicalConnection;
|
||||
|
||||
/**
|
||||
* Helper for creating various types of
|
||||
* NOTE :
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class StatementCreatorHelper {
|
||||
public static PreparedStatement prepareQueryStatement(
|
||||
String sql,
|
||||
SharedSessionContractImplementor session) {
|
||||
return session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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.orm.test.query.sqm.mutation.multitable;
|
||||
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.orm.test.metamodel.mapping.SecondaryTableTests;
|
||||
import org.hibernate.orm.test.metamodel.mapping.inheritance.joined.JoinedInheritanceTest;
|
||||
import org.hibernate.query.internal.ParameterMetadataImpl;
|
||||
import org.hibernate.query.internal.QueryParameterBindingsImpl;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.sql.exec.spi.Callback;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests for selecting matching ids related to SQM update/select statements.
|
||||
*
|
||||
* Matching-id-selection is used in CTE- and inline-based strategies.
|
||||
*
|
||||
* A "functional correctness" test for {@link MatchingIdSelectionHelper#selectMatchingIds}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@DomainModel(
|
||||
standardModels = StandardDomainModel.GAMBIT,
|
||||
annotatedClasses = {
|
||||
SecondaryTableTests.SimpleEntityWithSecondaryTables.class,
|
||||
JoinedInheritanceTest.Customer.class,
|
||||
JoinedInheritanceTest.DomesticCustomer.class,
|
||||
JoinedInheritanceTest.ForeignCustomer.class
|
||||
}
|
||||
)
|
||||
@ServiceRegistry
|
||||
@SessionFactory( exportSchema = true )
|
||||
public class IdSelectionTests {
|
||||
|
||||
@Test
|
||||
public void testSecondaryTableRestrictedOnRootTable(SessionFactoryScope scope) {
|
||||
final SqmDeleteStatement sqm = (SqmDeleteStatement) scope.getSessionFactory()
|
||||
.getQueryEngine()
|
||||
.getHqlTranslator()
|
||||
.translate( "delete SimpleEntityWithSecondaryTables where name = :n" );
|
||||
|
||||
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
|
||||
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
|
||||
|
||||
final QueryParameterBindingsImpl domainParamBindings = QueryParameterBindingsImpl.from(
|
||||
parameterMetadata,
|
||||
scope.getSessionFactory()
|
||||
);
|
||||
domainParamBindings.getBinding( "n" ).setBindValue( "abc" );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings );
|
||||
|
||||
MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondaryTableRestrictedOnNonRootTable(SessionFactoryScope scope) {
|
||||
final SqmDeleteStatement sqm = (SqmDeleteStatement) scope.getSessionFactory()
|
||||
.getQueryEngine()
|
||||
.getHqlTranslator()
|
||||
.translate( "delete SimpleEntityWithSecondaryTables where data = :d" );
|
||||
|
||||
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
|
||||
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
|
||||
|
||||
final QueryParameterBindingsImpl domainParamBindings = QueryParameterBindingsImpl.from(
|
||||
parameterMetadata,
|
||||
scope.getSessionFactory()
|
||||
);
|
||||
domainParamBindings.getBinding( "d" ).setBindValue( "123" );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings );
|
||||
|
||||
MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinedSubclassRestrictedOnRootTable(SessionFactoryScope scope) {
|
||||
final SqmDeleteStatement sqm = (SqmDeleteStatement) scope.getSessionFactory()
|
||||
.getQueryEngine()
|
||||
.getHqlTranslator()
|
||||
.translate( "delete Customer where name = :n" );
|
||||
|
||||
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
|
||||
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
|
||||
|
||||
final QueryParameterBindingsImpl domainParamBindings = QueryParameterBindingsImpl.from(
|
||||
parameterMetadata,
|
||||
scope.getSessionFactory()
|
||||
);
|
||||
domainParamBindings.getBinding( "n" ).setBindValue( "Acme" );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings );
|
||||
|
||||
MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinedSubclassRestrictedOnNonPrimaryRootTable(SessionFactoryScope scope) {
|
||||
final SqmDeleteStatement sqm = (SqmDeleteStatement) scope.getSessionFactory()
|
||||
.getQueryEngine()
|
||||
.getHqlTranslator()
|
||||
.translate( "delete ForeignCustomer where name = :n" );
|
||||
|
||||
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
|
||||
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
|
||||
|
||||
final QueryParameterBindingsImpl domainParamBindings = QueryParameterBindingsImpl.from(
|
||||
parameterMetadata,
|
||||
scope.getSessionFactory()
|
||||
);
|
||||
domainParamBindings.getBinding( "n" ).setBindValue( "Acme" );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings );
|
||||
|
||||
MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinedSubclassRestrictedOnPrimaryNonRootTable(SessionFactoryScope scope) {
|
||||
final SqmDeleteStatement sqm = (SqmDeleteStatement) scope.getSessionFactory()
|
||||
.getQueryEngine()
|
||||
.getHqlTranslator()
|
||||
.translate( "delete ForeignCustomer where vat = :v" );
|
||||
|
||||
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
|
||||
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
|
||||
|
||||
final QueryParameterBindingsImpl domainParamBindings = QueryParameterBindingsImpl.from(
|
||||
parameterMetadata,
|
||||
scope.getSessionFactory()
|
||||
);
|
||||
domainParamBindings.getBinding( "v" ).setBindValue( "123" );
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings );
|
||||
|
||||
MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static class TestExecutionContext implements ExecutionContext {
|
||||
private final SessionImplementor session;
|
||||
private final QueryParameterBindingsImpl domainParamBindings;
|
||||
|
||||
public TestExecutionContext(SessionImplementor session, QueryParameterBindingsImpl domainParamBindings) {
|
||||
this.session = session;
|
||||
this.domainParamBindings = domainParamBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return QueryOptions.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return domainParamBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return afterLoadAction -> {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue