Working support for simple HQL DELETE -> SqmDeleteStatement translation

This commit is contained in:
Steve Ebersole 2019-11-08 15:58:54 -06:00
parent 8dd63c372d
commit 30ad3eabe5
44 changed files with 714 additions and 102 deletions

View File

@ -14,6 +14,8 @@ import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.service.Service;
import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl;
import org.hibernate.sql.exec.internal.StandardJdbcMutationExecutor;
import org.hibernate.sql.exec.spi.JdbcMutationExecutor;
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
/**
@ -84,4 +86,12 @@ public interface JdbcServices extends Service {
default JdbcSelectExecutor getJdbcSelectExecutor() {
return JdbcSelectExecutorStandardImpl.INSTANCE;
}
default JdbcMutationExecutor getJdbcDeleteExecutor() {
return StandardJdbcMutationExecutor.INSTANCE;
}
default JdbcMutationExecutor getJdbcUpdateExecutor() {
return StandardJdbcMutationExecutor.INSTANCE;
}
}

View File

@ -618,7 +618,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
queryString,
interpretationCache.resolveHqlInterpretation(
queryString,
s -> queryEngine.getHqlTranslator().interpret( queryString )
s -> queryEngine.getHqlTranslator().translate( queryString )
),
resultClass,
this

View File

@ -17,6 +17,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.loader.spi.Loadable;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
@ -109,6 +110,10 @@ public interface EntityMappingType extends ManagedMappingType, Loadable {
return false;
}
default SqmMultiTableMutationStrategy getSqmMultiTableMutationStrategy(){
return getEntityPersister().getSqmMultiTableMutationStrategy();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Special model parts - identifier, discriminator, etc

View File

@ -187,7 +187,6 @@ public class EmbeddedAttributeMapping
SqlAstCreationState sqlAstCreationState) {
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( attrColumnNames.length );
final TableReference tableReference = tableGroup.resolveTableReference( getContainingTableExpression() );
getEmbeddableTypeDescriptor().visitJdbcTypes(
new Consumer<JdbcMapping>() {
private int index = 0;

View File

@ -28,7 +28,7 @@ public interface HqlTranslator {
*
* @return The semantic representation of the incoming query.
*/
SqmStatement interpret(String hql);
SqmStatement translate(String hql);
/**
* Give the translator a chance to "shut down" if it needs to

View File

@ -124,7 +124,7 @@ public class NamedHqlQueryMementoImpl extends AbstractNamedQueryMemento implemen
@Override
public void validate(QueryEngine queryEngine) {
queryEngine.getHqlTranslator().interpret( getHqlString() );
queryEngine.getHqlTranslator().translate( getHqlString() );
}
@Override

View File

@ -343,12 +343,17 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
parameterCollector = deleteStatement;
processingStateStack.push( new SqmDmlCreationProcessingState( deleteStatement, this ) );
final SqmDmlCreationProcessingState sqmDeleteCreationState = new SqmDmlCreationProcessingState(
deleteStatement,
this
);
sqmDeleteCreationState.getPathRegistry().register( root );
processingStateStack.push( sqmDeleteCreationState );
try {
if ( ctx.whereClause() != null && ctx.whereClause().predicate() != null ) {
deleteStatement.getWhereClause().setPredicate(
(SqmPredicate) ctx.whereClause().predicate().accept( this )
);
deleteStatement.applyPredicate( (SqmPredicate) ctx.whereClause().predicate().accept( this ) );
}
return deleteStatement;
@ -792,6 +797,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
log.debugf( "Attempting to resolve path [%s] as entity reference...", entityName );
EntityDomainType reference = null;
try {
entityName = creationContext.getJpaMetamodel().qualifyImportableName( entityName );
reference = creationContext.getJpaMetamodel().entity( entityName );
}
catch (Exception ignore) {

View File

@ -38,7 +38,7 @@ public class StandardHqlTranslator implements HqlTranslator {
}
@Override
public SqmStatement interpret(String query) {
public SqmStatement translate(String query) {
final HqlParser.StatementContext hqlParseTree = parseHql( query );
// then we perform semantic analysis and build the semantic representation...

View File

@ -28,13 +28,15 @@ import org.hibernate.query.hql.spi.SqmCreationOptions;
import org.hibernate.query.internal.QueryInterpretationCacheDisabledImpl;
import org.hibernate.query.internal.QueryInterpretationCacheStandardImpl;
import org.hibernate.query.named.NamedQueryRepository;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.internal.SqmCreationOptionsStandard;
import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder;
import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
@ -160,6 +162,21 @@ public class QueryEngine {
creationContext
);
}
@Override
public SimpleSqmDeleteToSqlAstConverter createSimpleDeleteConverter(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext) {
return new StandardSqmDeleteToSqlAstConverter(
creationContext,
queryOptions,
domainParameterXref,
domainParameterBindings
);
}
};
}

View File

@ -27,7 +27,7 @@ import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.sql.internal.SqmSelectInterpretation;
import org.hibernate.query.sqm.sql.SqmSelectInterpretation;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;

View File

@ -8,7 +8,6 @@ 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.tree.delete.SqmDeleteStatement;
import org.hibernate.sql.exec.spi.ExecutionContext;
/**
@ -17,10 +16,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
public class MultiTableDeleteQueryPlan implements NonSelectQueryPlan {
private final DeleteHandler deleteHandler;
public MultiTableDeleteQueryPlan(
SqmDeleteStatement sqmDeleteStatement,
DeleteHandler deleteHandler,
ExecutionContext executionContext) {
public MultiTableDeleteQueryPlan(DeleteHandler deleteHandler) {
this.deleteHandler = deleteHandler;
}

View File

@ -46,7 +46,7 @@ import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmStatement;
@ -92,7 +92,7 @@ public class QuerySqmImpl<R>
final SessionFactoryImplementor factory = producer.getFactory();
this.sqmStatement = factory.getQueryEngine().getHqlTranslator().interpret( hqlString );
this.sqmStatement = factory.getQueryEngine().getHqlTranslator().translate( hqlString );
if ( resultType != null ) {
if ( sqmStatement instanceof SqmDmlStatement ) {
@ -532,7 +532,7 @@ public class QuerySqmImpl<R>
return buildUpdateQueryPlan();
}
throw new NotYetImplementedException( "Query#executeUpdate not yet implemented" );
throw new NotYetImplementedException( "Query#executeUpdate for Statements of type [" + getSqmStatement() + "not yet supported" );
}
private NonSelectQueryPlan buildDeleteQueryPlan() {
@ -541,13 +541,19 @@ public class QuerySqmImpl<R>
final String entityNameToDelete = sqmDelete.getTarget().getReferencedPathSource().getHibernateEntityName();
final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToDelete );
final DeleteHandler deleteHandler = entityDescriptor.getSqmMultiTableMutationStrategy().buildDeleteHandler(
sqmDelete,
domainParameterXref,
this::getSessionFactory
);
return new DeleteQueryPlanImpl( sqmDelete, deleteHandler, this );
final SqmMultiTableMutationStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableMutationStrategy();
if ( multiTableStrategy == null ) {
return new SimpleDeleteQueryPlan( sqmDelete, domainParameterXref );
}
else {
return new MultiTableDeleteQueryPlan(
multiTableStrategy.buildDeleteHandler(
sqmDelete,
domainParameterXref,
this::getSessionFactory
)
);
}
}
private NonSelectQueryPlan buildUpdateQueryPlan() {

View File

@ -6,57 +6,109 @@
*/
package org.hibernate.query.sqm.internal;
import org.hibernate.NotYetImplementedFor6Exception;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteInterpretation;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.exec.spi.ExecutionContext;
//import org.hibernate.sql.ast.consume.spi.SqlDeleteToJdbcDeleteConverter;
//import org.hibernate.sql.ast.produce.sqm.spi.SqmDeleteInterpretation;
//import org.hibernate.sql.ast.produce.sqm.spi.SqmDeleteToSqlAstConverterSimple;
//import org.hibernate.sql.exec.internal.JdbcMutationExecutorImpl;
//import org.hibernate.sql.exec.spi.ExecutionContext;
//import org.hibernate.sql.exec.spi.JdbcMutation;
//
//import static org.hibernate.query.internal.QueryHelper.buildJdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcDelete;
import org.hibernate.sql.exec.spi.JdbcParameter;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
/**
* @author Steve Ebersole
*/
public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
private final SqmDeleteStatement sqmStatement;
private final SqmDeleteStatement sqmDelete;
private final DomainParameterXref domainParameterXref;
public SimpleDeleteQueryPlan(SqmDeleteStatement sqmStatement) {
this.sqmStatement = sqmStatement;
domainParameterXref = DomainParameterXref.from( sqmStatement );
private JdbcDelete jdbcDelete;
private Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref;
// todo (6.0) : here is where we need to perform the conversion into SQL AST
public SimpleDeleteQueryPlan(
SqmDeleteStatement sqmDelete,
DomainParameterXref domainParameterXref) {
this.sqmDelete = sqmDelete;
this.domainParameterXref = domainParameterXref;
}
@Override
public int executeUpdate(ExecutionContext executionContext) {
throw new NotYetImplementedFor6Exception( getClass() );
// final SqmDeleteInterpretation sqmInterpretation = SqmDeleteToSqlAstConverterSimple.interpret(
// sqmStatement,
// executionContext.getQueryOptions(),
// domainParameterXref,
// executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
// executionContext.getSession()
// );
//
// // the converter should enforce this, simple assertion here
// assert sqmInterpretation.getSqlDeletes().size() == 1;
//
// final JdbcMutation jdbcDelete = SqlDeleteToJdbcDeleteConverter.interpret(
// sqmInterpretation.getSqlDeletes().get( 0 ),
// executionContext.getSession().getSessionFactory()
// );
//
//
// return JdbcMutationExecutorImpl.WITH_AFTER_STATEMENT_CALL.execute(
// jdbcDelete,
// buildJdbcParameterBindings( sqmStatement, sqmInterpretation, executionContext ),
// executionContext
// );
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final JdbcServices jdbcServices = factory.getJdbcServices();
if ( jdbcDelete == null ) {
final QueryEngine queryEngine = factory.getQueryEngine();
final SqmTranslatorFactory converterFactory = queryEngine.getSqmTranslatorFactory();
final SimpleSqmDeleteToSqlAstConverter converter = converterFactory.createSimpleDeleteConverter(
executionContext.getQueryOptions(),
domainParameterXref,
executionContext.getQueryParameterBindings(),
executionContext.getLoadQueryInfluencers(),
factory
);
final SimpleSqmDeleteInterpretation sqmInterpretation = converter.interpret( sqmDelete );
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteConverter( factory );
jdbcDelete = sqlAstTranslator.translate( sqmInterpretation.getSqlAst() );
this.jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
domainParameterXref,
sqmInterpretation::getJdbcParamsBySqmParam
);
}
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
executionContext.getQueryParameterBindings(),
domainParameterXref,
jdbcParamsXref,
// todo (6.0) : ugh. this one is important
null,
executionContext.getSession()
);
final LogicalConnectionImplementor logicalConnection = executionContext.getSession()
.getJdbcCoordinator()
.getLogicalConnection();
return jdbcServices.getJdbcDeleteExecutor().execute(
jdbcDelete,
jdbcParameterBindings,
sql -> {
try {
return logicalConnection.getPhysicalConnection().prepareStatement( sql );
}
catch (SQLException e) {
throw jdbcServices.getSqlExceptionHelper().convert(
e,
"Error performing DELETE",
sql
);
}
},
(integer, preparedStatement) -> {},
executionContext
);
}
}

View File

@ -21,7 +21,7 @@
* == From HQL/JPQL
*
* `SemanticQueryProducer` defines just a single method for producing SQM based on HQL:
* {@link org.hibernate.query.hql.HqlTranslator#interpret}.
* {@link org.hibernate.query.hql.HqlTranslator#translate}.
* See {@link org.hibernate.query.hql.internal} for details
*
*

View File

@ -414,7 +414,7 @@ public abstract class BaseSqmToSqlAstConverter
return null;
}
Predicate additionalRestrictions;
protected Predicate additionalRestrictions;
@SuppressWarnings("WeakerAccess")
protected void consumeFromClauseRoot(SqmRoot<?> sqmRoot) {

View File

@ -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.sql;
import java.util.List;
import java.util.Map;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.exec.spi.JdbcParameter;
/**
* @author Steve Ebersole
*/
public class SimpleSqmDeleteInterpretation implements SqmInterpretation {
private final DeleteStatement sqlAst;
private final Map<SqmParameter, List<JdbcParameter>> jdbcParamMap;
public SimpleSqmDeleteInterpretation(
DeleteStatement sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamMap) {
this.sqlAst = sqlAst;
this.jdbcParamMap = jdbcParamMap;
}
@Override
public DeleteStatement getSqlAst() {
return sqlAst;
}
@Override
public Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam() {
return jdbcParamMap;
}
}

View File

@ -0,0 +1,17 @@
/*
* 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.sql;
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
/**
* @author Steve Ebersole
*/
public interface SimpleSqmDeleteToSqlAstConverter extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess {
SimpleSqmDeleteInterpretation interpret(SqmDeleteStatement statement);
}

View File

@ -0,0 +1,24 @@
/*
* 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.sql;
import java.util.List;
import java.util.Map;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcParameter;
/**
* Information obtained from the interpretation of an SqmStatement
*
* @author Steve Ebersole
*/
public interface SqmInterpretation {
Statement getSqlAst();
Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam();
}

View File

@ -4,12 +4,12 @@
* 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.sql.internal;
package org.hibernate.query.sqm.sql;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcParameter;
@ -21,7 +21,7 @@ import org.hibernate.sql.exec.spi.JdbcParameter;
*
* @author Steve Ebersole
*/
public class SqmSelectInterpretation {
public class SqmSelectInterpretation implements SqmInterpretation {
private final SelectStatement sqlAst;
private final Map<SqmParameter,List<JdbcParameter>> jdbcParamsBySqmParam;
@ -32,10 +32,12 @@ public class SqmSelectInterpretation {
this.jdbcParamsBySqmParam = jdbcParamsBySqmParam;
}
@Override
public SelectStatement getSqlAst() {
return sqlAst;
}
@Override
public Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam() {
return jdbcParamsBySqmParam;
}

View File

@ -7,7 +7,6 @@
package org.hibernate.query.sqm.sql;
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.sql.internal.SqmSelectInterpretation;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
/**

View File

@ -10,10 +10,10 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
/**
* Factory for various
* @author Steve Ebersole
*/
public interface SqmTranslatorFactory {
@ -24,5 +24,12 @@ public interface SqmTranslatorFactory {
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext);
SimpleSqmDeleteToSqlAstConverter createSimpleDeleteConverter(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext);
// todo (6.0) : update, delete, etc converters...
}

View File

@ -45,6 +45,7 @@ public class EmbeddableValuedPathInterpretation<T> implements AssignableSqmPathI
return new EmbeddableValuedPathInterpretation<>(
mapping.toSqlExpression(
tableGroup,
converter.getCurrentClauseStack().getCurrent(),
converter,
converter

View File

@ -0,0 +1,106 @@
/*
* 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.sql.internal;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteInterpretation;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstTreeHelper;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.predicate.Predicate;
/**
* @author Steve Ebersole
*/
public class StandardSqmDeleteToSqlAstConverter
extends BaseSqmToSqlAstConverter
implements SimpleSqmDeleteToSqlAstConverter {
public StandardSqmDeleteToSqlAstConverter(
SqlAstCreationContext creationContext,
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings) {
super( creationContext, queryOptions, domainParameterXref, domainParameterBindings );
}
@Override
public SimpleSqmDeleteInterpretation interpret(SqmDeleteStatement statement) {
final DeleteStatement deleteStatement = visitDeleteStatement( statement );
return new SimpleSqmDeleteInterpretation(
deleteStatement,
getJdbcParamsBySqmParam()
);
}
@Override
public DeleteStatement visitDeleteStatement(SqmDeleteStatement statement) {
final String entityName = statement.getTarget().getEntityName();
final EntityPersister entityDescriptor = getCreationContext().getDomainModel().getEntityDescriptor( entityName );
assert entityDescriptor != null;
getProcessingStateStack().push(
new SqlAstProcessingStateImpl(
null,
this,
getCurrentClauseStack()::getCurrent
)
);
try {
final NavigablePath rootPath = new NavigablePath( entityName );
final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup(
rootPath,
null,
JoinType.LEFT,
LockMode.WRITE,
stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ),
getSqlExpressionResolver(),
() -> predicate -> additionalRestrictions = predicate,
getCreationContext()
);
getFromClauseIndex().registerTableGroup( rootPath, rootTableGroup );
if ( ! rootTableGroup.getTableReferenceJoins().isEmpty() ) {
throw new HibernateException( "Not expecting multiple table references for an SQM DELETE" );
}
Predicate suppliedPredicate = null;
final SqmWhereClause whereClause = statement.getWhereClause();
if ( whereClause != null && whereClause.getPredicate() != null ) {
getCurrentClauseStack().push( Clause.WHERE );
try {
suppliedPredicate = (Predicate) whereClause.getPredicate().accept( this );
}
finally {
getCurrentClauseStack().pop();
}
}
return new DeleteStatement(
rootTableGroup.getPrimaryTableReference(),
SqlAstTreeHelper.combinePredicates( suppliedPredicate, additionalRestrictions )
);
}
finally {
getProcessingStateStack().pop();
}
}
}

View File

@ -30,6 +30,7 @@ import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmSelectInterpretation;
import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;

View File

@ -22,6 +22,7 @@ import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
import org.hibernate.sql.ast.spi.SqlAstTreeHelper;
/**
* @author Steve Ebersole
@ -61,6 +62,18 @@ public class SqmDeleteStatement<T>
return whereClause;
}
@Override
public void applyPredicate(SqmPredicate predicate) {
if ( predicate == null ) {
return;
}
if ( whereClause == null ) {
whereClause = new SqmWhereClause( nodeBuilder() );
}
whereClause.applyPredicate( predicate );
}
public void setWhereClause(SqmWhereClause whereClause) {
this.whereClause = whereClause;
}

View File

@ -13,4 +13,6 @@ package org.hibernate.query.sqm.tree.predicate;
*/
public interface SqmWhereClauseContainer {
SqmWhereClause getWhereClause();
void applyPredicate(SqmPredicate accept);
}

View File

@ -86,6 +86,19 @@ public class SqmQuerySpec<T> implements SqmNode, SqmFromClauseContainer, SqmWher
this.whereClause = whereClause;
}
@Override
public void applyPredicate(SqmPredicate predicate) {
if ( predicate == null ) {
return;
}
if ( whereClause == null ) {
whereClause = new SqmWhereClause( nodeBuilder() );
}
whereClause.applyPredicate( predicate );
}
public SqmGroupByClause getGroupByClause() {
return groupByClause;
}

View File

@ -77,6 +77,19 @@ public class SqmUpdateStatement<T>
return whereClause;
}
@Override
public void applyPredicate(SqmPredicate predicate) {
if ( predicate == null ) {
return;
}
if ( whereClause == null ) {
whereClause = new SqmWhereClause( nodeBuilder() );
}
whereClause.applyPredicate( predicate );
}
public void setWhereClause(SqmWhereClause whereClause) {
this.whereClause = whereClause;
}

View File

@ -0,0 +1,19 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.ast;
import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter;
import org.hibernate.sql.ast.spi.SqlSelectAstWalker;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.exec.spi.JdbcDelete;
/**
* @author Steve Ebersole
*/
public interface SqlAstDeleteTranslator extends SqlSelectAstWalker, SqlAstToJdbcOperationConverter {
JdbcDelete translate(DeleteStatement sqlAst);
}

View File

@ -13,6 +13,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
*/
public interface SqlAstTranslatorFactory {
SqlAstSelectTranslator buildSelectConverter(SessionFactoryImplementor sessionFactory);
SqlAstDeleteTranslator buildDeleteConverter(SessionFactoryImplementor sessionFactory);
// todo (6.0) : update, delete, etc
}

View File

@ -27,6 +27,10 @@ public class SqlAstTreeHelper {
return incomingRestriction;
}
if ( incomingRestriction == null ) {
return baseRestriction;
}
final Junction combinedPredicate;
if ( baseRestriction instanceof Junction ) {

View File

@ -6,12 +6,9 @@
*/
package org.hibernate.sql.ast.spi;
import org.hibernate.sql.ast.tree.select.SelectStatement;
/**
* @author Steve Ebersole
* @author Andrea Boriero
*/
public interface SqlSelectAstWalker extends SqlAstWalker {
void visitSelectQuery(SelectStatement selectQuery);
}

View File

@ -0,0 +1,60 @@
/*
* 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.spi;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.exec.spi.JdbcDelete;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
/**
* @author Steve Ebersole
*/
public class StandardSqlAstDeleteTranslator
extends AbstractSqlAstToJdbcOperationConverter
implements SqlAstDeleteTranslator {
public StandardSqlAstDeleteTranslator(SessionFactoryImplementor sessionFactory) {
super( sessionFactory );
}
@Override
public JdbcDelete translate(DeleteStatement sqlAst) {
appendSql( "delete from " );
appendSql( sqlAst.getTargetTable().getTableExpression() );
sqlAst.getRestriction().accept( this );
return new JdbcDelete() {
@Override
public String getSql() {
return StandardSqlAstDeleteTranslator.this.getSql();
}
@Override
public List<JdbcParameterBinder> getParameterBinders() {
return StandardSqlAstDeleteTranslator.this.getParameterBinders();
}
@Override
public Set<String> getAffectedTableNames() {
return getAffectedTableExpressions();
}
};
}
@Override
public void visitColumnReference(ColumnReference columnReference) {
// generally we do not want to render the qualifier
appendSql( columnReference.getColumnExpression() );
}
}

View File

@ -67,8 +67,4 @@ public class StandardSqlAstSelectTranslator
);
}
@Override
public void visitSelectQuery(SelectStatement selectQuery) {
visitQuerySpec( selectQuery.getQuerySpec() );
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.sql.ast.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.SqlAstSelectTranslator;
@ -18,4 +19,9 @@ public class StandardSqlAstTranslatorFactory implements SqlAstTranslatorFactory
public SqlAstSelectTranslator buildSelectConverter(SessionFactoryImplementor sessionFactory) {
return new StandardSqlAstSelectTranslator( sessionFactory);
}
@Override
public SqlAstDeleteTranslator buildDeleteConverter(SessionFactoryImplementor sessionFactory) {
return new StandardSqlAstDeleteTranslator( sessionFactory );
}
}

View File

@ -26,6 +26,8 @@ import org.hibernate.type.spi.TypeConfiguration;
* @author Steve Ebersole
*/
public class ColumnReference implements Expression {
private final String qualifier;
private final String columnExpression;
private final String referenceExpression;
private final JdbcMapping jdbcMapping;
@ -34,20 +36,11 @@ public class ColumnReference implements Expression {
String columnExpression,
JdbcMapping jdbcMapping,
SessionFactoryImplementor sessionFactory) {
this(
qualifier == null
? columnExpression
: qualifier + "." + columnExpression,
jdbcMapping,
sessionFactory
);
}
public ColumnReference(
String referenceExpression,
JdbcMapping jdbcMapping,
SessionFactoryImplementor sessionFactory) {
this.referenceExpression = referenceExpression;
this.qualifier = qualifier;
this.columnExpression = columnExpression;
this.referenceExpression = qualifier == null
? columnExpression
: qualifier + "." + columnExpression;
this.jdbcMapping = jdbcMapping;
}
@ -59,6 +52,14 @@ public class ColumnReference implements Expression {
this( tableReference.getIdentificationVariable(), columnExpression, jdbcMapping, sessionFactory );
}
public String getQualifier() {
return qualifier;
}
public String getColumnExpression() {
return columnExpression;
}
public String getExpressionText() {
return referenceExpression;
}

View File

@ -0,0 +1,85 @@
/*
* 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.internal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcDelete;
import org.hibernate.sql.exec.spi.JdbcMutation;
import org.hibernate.sql.exec.spi.JdbcMutationExecutor;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
/**
* @author Steve Ebersole
*/
public class StandardJdbcMutationExecutor implements JdbcMutationExecutor {
/**
* Singleton access
*/
public static final StandardJdbcMutationExecutor INSTANCE = new StandardJdbcMutationExecutor();
@Override
public int execute(
JdbcMutation jdbcMutation,
JdbcParameterBindings jdbcParameterBindings,
Function<String, PreparedStatement> statementCreator,
BiConsumer<Integer, PreparedStatement> expectationCheck,
ExecutionContext executionContext) {
final LogicalConnectionImplementor logicalConnection = executionContext.getSession()
.getJdbcCoordinator()
.getLogicalConnection();
final JdbcServices jdbcServices = executionContext.getSession().getFactory().getServiceRegistry().getService(
JdbcServices.class );
final String sql = jdbcMutation.getSql();
try {
// prepare the query
final PreparedStatement preparedStatement = statementCreator.apply( sql );
try {
if ( executionContext.getQueryOptions().getTimeout() != null ) {
preparedStatement.setQueryTimeout( executionContext.getQueryOptions().getTimeout() );
}
// bind parameters
// todo : validate that all query parameters were bound?
int paramBindingPosition = 1;
for ( JdbcParameterBinder parameterBinder : jdbcMutation.getParameterBinders() ) {
parameterBinder.bindParameterValue(
preparedStatement,
paramBindingPosition++,
jdbcParameterBindings,
executionContext
);
}
int rows = preparedStatement.executeUpdate();
expectationCheck.accept( rows, preparedStatement );
return rows;
}
finally {
logicalConnection.getResourceRegistry().release( preparedStatement );
}
}
catch (SQLException e) {
throw jdbcServices.getSqlExceptionHelper().convert(
e,
"JDBC exception executing SQL [" + sql + "]"
);
}
finally {
executionContext.afterStatement( logicalConnection );
}
}
}

View File

@ -7,24 +7,16 @@
package org.hibernate.sql.exec.spi;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
/**
* @author Steve Ebersole
*/
public interface ExecutionContext {
default Object resolveEntityInstance(EntityKey entityKey, boolean eager) {
return StandardEntityInstanceResolver.resolveEntityInstance(
entityKey,
eager,
getSession()
);
}
SharedSessionContractImplementor getSession();
QueryOptions getQueryOptions();
@ -43,4 +35,12 @@ public interface ExecutionContext {
default CollectionKey getCollectionKey() {
return null;
}
/**
* Hook to allow delaying calls to {@link LogicalConnectionImplementor#afterStatement()}.
* Mainly used in the case of batching and multi-table mutations
*/
default void afterStatement(LogicalConnectionImplementor logicalConnection) {
logicalConnection.afterStatement();
}
}

View File

@ -0,0 +1,13 @@
/*
* 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;
/**
* @author Steve Ebersole
*/
public interface JdbcDelete extends JdbcMutation {
}

View File

@ -0,0 +1,16 @@
/*
* 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;
/**
* Classification of JdbcDelete and JdbcUpdate operations
*
* @author Steve Ebersole
*/
public interface JdbcMutation extends JdbcOperation {
}

View File

@ -0,0 +1,28 @@
/*
* 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 java.util.function.BiConsumer;
import java.util.function.Function;
/**
* Executor for JdbcDelete and JdbcUpdate operations
*
* @author Steve Ebersole
*/
public interface JdbcMutationExecutor {
/**
* Perform the execution
*/
int execute(
JdbcMutation jdbcMutation,
JdbcParameterBindings jdbcParameterBindings,
Function<String, PreparedStatement> statementCreator,
BiConsumer<Integer, PreparedStatement> expectationCheck,
ExecutionContext executionContext);
}

View File

@ -0,0 +1,58 @@
/*
* 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.hql;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
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;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
@DomainModel( standardModels = StandardDomainModel.GAMBIT )
@ServiceRegistry
@SessionFactory( exportSchema = true )
public class MutationTests {
@Test
public void testSimpleDeleteTranslation(SessionFactoryScope scope) {
final SqmDeleteStatement sqmDelete = (SqmDeleteStatement) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete BasicEntity" );
assertThat( sqmDelete, notNullValue() );
assertThat( sqmDelete.getTarget().getEntityName(), is( BasicEntity.class.getName() ) );
assertThat( sqmDelete.getRestriction(), nullValue() );
}
@Test
public void testSimpleRestrictedDeleteTranslation(SessionFactoryScope scope) {
final SqmDeleteStatement sqmDelete = (SqmDeleteStatement) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete BasicEntity where data = 'abc'" );
assertThat( sqmDelete, notNullValue() );
assertThat( sqmDelete.getTarget().getEntityName(), is( BasicEntity.class.getName() ) );
assertThat( sqmDelete.getRestriction(), notNullValue() );
assertThat( sqmDelete.getRestriction(), instanceOf( SqmComparisonPredicate.class ) );
}
}

View File

@ -53,7 +53,7 @@ public abstract class BaseSqmUnitTest
}
public static SqmSelectStatement interpretSelect(String hql, SessionFactoryImplementor sessionFactory) {
return (SqmSelectStatement) sessionFactory.getQueryEngine().getHqlTranslator().interpret( hql );
return (SqmSelectStatement) sessionFactory.getQueryEngine().getHqlTranslator().translate( hql );
}
@Override

View File

@ -16,7 +16,7 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.query.hql.spi.HqlQueryImplementor;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.sqm.internal.QuerySqmImpl;
import org.hibernate.query.sqm.sql.internal.SqmSelectInterpretation;
import org.hibernate.query.sqm.sql.SqmSelectInterpretation;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.sql.ast.spi.SqlSelection;