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
This commit is contained in:
Gavin King 2020-08-23 12:26:57 +02:00 committed by Andrea Boriero
parent d1119d320a
commit 264e71a916
5 changed files with 136 additions and 80 deletions

View File

@ -29,18 +29,16 @@ import org.hibernate.hql.internal.QueryExecutionRequestException;
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 class QueryTranslatorImpl implements FilterTranslator {
}
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 @@ public class QueryTranslatorImpl implements FilterTranslator {
return new MultiTableDeleteExecutor( walker );
}
else {
return new DeleteExecutor( walker, persister );
return new DeleteExecutor( walker );
}
}
else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) {
@ -618,11 +615,11 @@ public class QueryTranslatorImpl implements FilterTranslator {
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" );

View File

@ -8,7 +8,6 @@ package org.hibernate.hql.internal.ast.exec;
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.QueryParameters;
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<ParameterSpecification> 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 class BasicExecutor implements StatementExecutor {
}
@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<ParameterSpecification> 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 @@ public class BasicExecutor implements StatementExecutor {
}
}
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);
}
}
}

View File

@ -17,13 +17,12 @@ import org.hibernate.engine.spi.QueryParameters;
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 @@ import antlr.collections.AST;
/**
* 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<String> deletes = new ArrayList<>();
private List<ParameterSpecification> 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 class DeleteExecutor extends BasicExecutor {
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 class DeleteExecutor extends BasicExecutor {
);
}
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 @@ public class DeleteExecutor extends BasicExecutor {
@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

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}