From 264e71a916ff27a97bfefea144a20895f7fc75e7 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 23 Aug 2020 12:26:57 +0200 Subject: [PATCH] HHH-14153 clean up BasicExecutor and friends introduces InsertExecutor and UpdateExecutor since we're going to be introducing specific optimizations relating to single-table updates relates to HHH-14153 --- .../hql/internal/ast/QueryTranslatorImpl.java | 13 ++- .../hql/internal/ast/exec/BasicExecutor.java | 90 +++++++------------ .../hql/internal/ast/exec/DeleteExecutor.java | 43 +++++---- .../hql/internal/ast/exec/InsertExecutor.java | 34 +++++++ .../hql/internal/ast/exec/UpdateExecutor.java | 36 ++++++++ 5 files changed, 136 insertions(+), 80 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/InsertExecutor.java create mode 100644 hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/UpdateExecutor.java diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java index e2efb5ad4d..2313ed4d63 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/QueryTranslatorImpl.java @@ -29,18 +29,16 @@ import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; import org.hibernate.hql.internal.antlr.HqlTokenTypes; import org.hibernate.hql.internal.antlr.SqlTokenTypes; -import org.hibernate.hql.internal.ast.exec.BasicExecutor; import org.hibernate.hql.internal.ast.exec.DeleteExecutor; +import org.hibernate.hql.internal.ast.exec.InsertExecutor; import org.hibernate.hql.internal.ast.exec.MultiTableDeleteExecutor; import org.hibernate.hql.internal.ast.exec.MultiTableUpdateExecutor; import org.hibernate.hql.internal.ast.exec.StatementExecutor; +import org.hibernate.hql.internal.ast.exec.UpdateExecutor; import org.hibernate.hql.internal.ast.tree.AggregatedSelectExpression; import org.hibernate.hql.internal.ast.tree.FromElement; -import org.hibernate.hql.internal.ast.tree.InsertStatement; import org.hibernate.hql.internal.ast.tree.QueryNode; import org.hibernate.hql.internal.ast.tree.Statement; -import org.hibernate.hql.internal.ast.tree.UpdateStatement; -import org.hibernate.hql.internal.ast.util.ASTPrinter; import org.hibernate.hql.internal.ast.util.ASTUtil; import org.hibernate.hql.internal.ast.util.NodeTraverser; import org.hibernate.hql.internal.ast.util.TokenPrinters; @@ -597,7 +595,6 @@ public void validateScrollability() throws HibernateException { } private StatementExecutor buildAppropriateStatementExecutor(HqlSqlWalker walker) { - final Statement statement = (Statement) walker.getAST(); if ( walker.getStatementType() == HqlSqlTokenTypes.DELETE ) { final FromElement fromElement = walker.getFinalFromClause().getFromElement(); final Queryable persister = fromElement.getQueryable(); @@ -605,7 +602,7 @@ private StatementExecutor buildAppropriateStatementExecutor(HqlSqlWalker walker) return new MultiTableDeleteExecutor( walker ); } else { - return new DeleteExecutor( walker, persister ); + return new DeleteExecutor( walker ); } } else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) { @@ -618,11 +615,11 @@ else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) { return new MultiTableUpdateExecutor( walker ); } else { - return new BasicExecutor( walker, persister ); + return new UpdateExecutor( walker ); } } else if ( walker.getStatementType() == HqlSqlTokenTypes.INSERT ) { - return new BasicExecutor( walker, ( (InsertStatement) statement ).getIntoClause().getQueryable() ); + return new InsertExecutor( walker ); } else { throw new QueryException( "Unexpected statement type" ); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java index f178b9e61c..ab5681f123 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/BasicExecutor.java @@ -8,7 +8,6 @@ import java.sql.PreparedStatement; import java.sql.SQLException; -import java.util.Iterator; import java.util.List; import org.hibernate.HibernateException; @@ -17,46 +16,22 @@ import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; -import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; -import org.hibernate.hql.internal.ast.HqlSqlWalker; -import org.hibernate.hql.internal.ast.QuerySyntaxException; -import org.hibernate.hql.internal.ast.SqlGenerator; import org.hibernate.param.ParameterSpecification; import org.hibernate.persister.entity.Queryable; -import antlr.RecognitionException; - /** - * Implementation of BasicExecutor. + * Base implementation of {@link StatementExecutor}. * * @author Steve Ebersole */ -public class BasicExecutor implements StatementExecutor { - private final Queryable persister; - private final String sql; - private final List parameterSpecifications; +public abstract class BasicExecutor implements StatementExecutor { - public BasicExecutor(HqlSqlWalker walker, Queryable persister) { + final Queryable persister; + String sql; + List parameterSpecifications; + + public BasicExecutor(Queryable persister) { this.persister = persister; - try { - SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() ); - gen.statement( walker.getAST() ); - if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) { - // workaround for a problem where HqlSqlWalker actually generates - // broken SQL with undefined aliases in the where clause, because - // that is what MultiTableUpdateExecutor is expecting to get - String alias = walker.getFinalFromClause().getFromElement().getTableAlias(); - sql = gen.getSQL().replace( alias + ".", "" ); - } - else { - sql = gen.getSQL(); - } - gen.getParseErrorHandler().throwQueryException(); - parameterSpecifications = gen.getCollectedParameters(); - } - catch ( RecognitionException e ) { - throw QuerySyntaxException.convert( e ); - } } @Override @@ -65,42 +40,43 @@ public String[] getSqlStatements() { } @Override - public int execute(QueryParameters parameters, SharedSessionContractImplementor session) throws HibernateException { - return doExecute( - parameters, - session, - session.getJdbcServices().getDialect() - .addSqlHintOrComment( - sql, - parameters, - session.getFactory().getSessionFactoryOptions().isCommentsEnabled() - ), - parameterSpecifications - ); - } - - protected int doExecute(QueryParameters parameters, SharedSessionContractImplementor session, String sql, - List parameterSpecifications) throws HibernateException { + public int execute(QueryParameters parameters, SharedSessionContractImplementor session) + throws HibernateException { + BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, persister ); if ( session.isEventSource() ) { - ( (EventSource) session ).getActionQueue().addAction( action ); + ( (EventSource) session).getActionQueue().addAction( action ); } else { action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session ); } - PreparedStatement st = null; - RowSelection selection = parameters.getRowSelection(); + return doExecute( + session.getJdbcServices().getDialect() + .addSqlHintOrComment( + sql, + parameters, + session.getFactory().getSessionFactoryOptions().isCommentsEnabled() + ), + parameters, + parameterSpecifications, + session + ); + } + int doExecute(String sql, QueryParameters parameters, + List parameterSpecifications, + SharedSessionContractImplementor session) + throws HibernateException{ try { + PreparedStatement st = null; try { st = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false ); - Iterator paramSpecItr = parameterSpecifications.iterator(); int pos = 1; - while ( paramSpecItr.hasNext() ) { - final ParameterSpecification paramSpec = (ParameterSpecification) paramSpecItr.next(); - pos += paramSpec.bind( st, parameters, session, pos ); + for ( ParameterSpecification parameter: parameterSpecifications) { + pos += parameter.bind( st, parameters, session, pos ); } + RowSelection selection = parameters.getRowSelection(); if ( selection != null ) { if ( selection.getTimeout() != null ) { st.setQueryTimeout( selection.getTimeout() ); @@ -117,7 +93,9 @@ protected int doExecute(QueryParameters parameters, SharedSessionContractImpleme } } catch( SQLException sqle ) { - throw session.getJdbcServices().getSqlExceptionHelper().convert( sqle, "could not execute update query", sql ); + throw session.getJdbcServices().getSqlExceptionHelper() + .convert( sqle, "could not execute update query", sql); } } + } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java index 9226f6ba65..7638a4ed9a 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/DeleteExecutor.java @@ -17,13 +17,12 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.hql.internal.ast.HqlSqlWalker; +import org.hibernate.hql.internal.ast.QuerySyntaxException; import org.hibernate.hql.internal.ast.SqlGenerator; import org.hibernate.hql.internal.ast.tree.DeleteStatement; -import org.hibernate.hql.internal.ast.tree.FromElement; import org.hibernate.metamodel.spi.MetamodelImplementor; -import org.hibernate.param.ParameterSpecification; -import org.hibernate.persister.collection.AbstractCollectionPersister; -import org.hibernate.persister.entity.Queryable; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.Joinable; import org.hibernate.sql.Delete; import org.hibernate.type.CollectionType; import org.hibernate.type.ComponentType; @@ -36,8 +35,10 @@ /** - * Provides deletions in addition to the basic SQL delete statement being executed. Ex: cascading the delete into a - * many-to-many join table. + * Executes HQL bulk deletes against a single table. + * + * Provides deletions in addition to the basic SQL delete statement being executed. + * Ex: cascading the delete into a many-to-many join table. * * @author Brett Meyer */ @@ -45,14 +46,22 @@ public class DeleteExecutor extends BasicExecutor { private static final Logger LOG = Logger.getLogger( DeleteExecutor.class ); private final List deletes = new ArrayList<>(); - private List parameterSpecifications; - public DeleteExecutor(HqlSqlWalker walker, Queryable persister) { - super( walker, persister ); - + public DeleteExecutor(HqlSqlWalker walker) { + super( walker.getFinalFromClause().getFromElement().getQueryable() ); + final SessionFactoryImplementor factory = walker.getSessionFactoryHelper().getFactory(); - final Dialect dialect = factory.getJdbcServices().getJdbcEnvironment().getDialect(); - + + try { + SqlGenerator gen = new SqlGenerator( factory ); + gen.statement( walker.getAST() ); + sql = gen.getSQL(); + gen.getParseErrorHandler().throwQueryException(); + } + catch ( RecognitionException e ) { + throw QuerySyntaxException.convert( e ); + } + try { final DeleteStatement deleteStatement = (DeleteStatement) walker.getAST(); @@ -72,12 +81,13 @@ public DeleteExecutor(HqlSqlWalker walker, Queryable persister) { final boolean commentsEnabled = factory.getSessionFactoryOptions().isCommentsEnabled(); final MetamodelImplementor metamodel = factory.getMetamodel(); + final Dialect dialect = factory.getJdbcServices().getJdbcEnvironment().getDialect(); final boolean notSupportingTuplesInSubqueries = !dialect.supportsTuplesInSubqueries(); // If many-to-many, delete the FK row in the collection table. for ( Type type : persister.getPropertyTypes() ) { if ( type.isCollectionType() ) { final CollectionType cType = (CollectionType) type; - final AbstractCollectionPersister cPersister = (AbstractCollectionPersister) metamodel.collectionPersister( cType.getRole() ); + final CollectionPersister cPersister = metamodel.collectionPersister( cType.getRole() ); if ( cPersister.isManyToMany() ) { Type keyType = cPersister.getKeyType(); String[] columnNames; @@ -106,14 +116,15 @@ public DeleteExecutor(HqlSqlWalker walker, Queryable persister) { ); } else { + Joinable joinable = (Joinable) cPersister; StringBuilder whereBuilder = new StringBuilder(); whereBuilder.append( '(' ); - append( ", ", cPersister.getKeyColumnNames(), whereBuilder ); + append( ", ", joinable.getKeyColumnNames(), whereBuilder ); whereBuilder.append( ") in (select " ); append( ", ", columnNames, whereBuilder ); final String where = whereBuilder.append(" from ") .append( persister.getTableName() ).append( idSubselectWhere ).append( ")" ).toString(); - final Delete delete = new Delete().setTableName( cPersister.getTableName() ).setWhere( where ); + final Delete delete = new Delete().setTableName( joinable.getTableName() ).setWhere( where ); if ( commentsEnabled ) { delete.setComment( "delete FKs in join table" ); } @@ -139,7 +150,7 @@ private static void append(String delimiter, String[] parts, StringBuilder sb) { @Override public int execute(QueryParameters parameters, SharedSessionContractImplementor session) throws HibernateException { for (String delete : deletes) { - doExecute( parameters, session, delete, parameterSpecifications ); + doExecute( delete, parameters, parameterSpecifications, session ); } // finally, execute the original sql statement diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/InsertExecutor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/InsertExecutor.java new file mode 100644 index 0000000000..ae283a6288 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/InsertExecutor.java @@ -0,0 +1,34 @@ +package org.hibernate.hql.internal.ast.exec; + +import antlr.RecognitionException; +import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; +import org.hibernate.hql.internal.ast.HqlSqlWalker; +import org.hibernate.hql.internal.ast.QuerySyntaxException; +import org.hibernate.hql.internal.ast.SqlGenerator; +import org.hibernate.hql.internal.ast.tree.InsertStatement; +import org.hibernate.hql.internal.ast.tree.Statement; +import org.hibernate.param.ParameterSpecification; +import org.hibernate.persister.entity.Queryable; + +import java.util.List; + +/** + * Executes HQL insert statements. + * + * @author Gavin King + */ +public class InsertExecutor extends BasicExecutor { + public InsertExecutor(HqlSqlWalker walker) { + super( ( (InsertStatement) walker.getAST() ).getIntoClause().getQueryable() ); + try { + SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() ); + gen.statement( walker.getAST() ); + sql = gen.getSQL(); + gen.getParseErrorHandler().throwQueryException(); + parameterSpecifications = gen.getCollectedParameters(); + } + catch ( RecognitionException e ) { + throw QuerySyntaxException.convert( e ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/UpdateExecutor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/UpdateExecutor.java new file mode 100644 index 0000000000..2f80e94519 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/exec/UpdateExecutor.java @@ -0,0 +1,36 @@ +package org.hibernate.hql.internal.ast.exec; + +import antlr.RecognitionException; +import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; +import org.hibernate.hql.internal.ast.HqlSqlWalker; +import org.hibernate.hql.internal.ast.QuerySyntaxException; +import org.hibernate.hql.internal.ast.SqlGenerator; +import org.hibernate.param.ParameterSpecification; +import org.hibernate.persister.entity.Queryable; + +import java.util.List; + +/** + * Executes HQL bulk updates against a single table. + * + * @author Gavin King + */ +public class UpdateExecutor extends BasicExecutor { + public UpdateExecutor(HqlSqlWalker walker) { + super( walker.getFinalFromClause().getFromElement().getQueryable() ); + try { + SqlGenerator gen = new SqlGenerator( walker.getSessionFactoryHelper().getFactory() ); + gen.statement( walker.getAST() ); + // workaround for a problem where HqlSqlWalker actually generates + // broken SQL with undefined aliases in the where clause, because + // that is what MultiTableUpdateExecutor is expecting to get + String alias = walker.getFinalFromClause().getFromElement().getTableAlias(); + sql = gen.getSQL().replace( alias + ".", "" ); + gen.getParseErrorHandler().throwQueryException(); + parameterSpecifications = gen.getCollectedParameters(); + } + catch ( RecognitionException e ) { + throw QuerySyntaxException.convert( e ); + } + } +}