Implement non-select native query support and fix parameter binding ordering issues for select native queries

This commit is contained in:
Christian Beikov 2021-03-08 16:19:17 +01:00
parent ce41447875
commit 56d90a0aa9
6 changed files with 208 additions and 25 deletions

View File

@ -179,6 +179,17 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
}
}
public static void schedule(ExecutionContext executionContext, Set<String> affectedQueryables) {
final SharedSessionContractImplementor session = executionContext.getSession();
final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, affectedQueryables );
if ( session.isEventSource() ) {
( (EventSource) session ).getActionQueue().addAction( action );
}
else {
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
}
}
/**
* Check whether we should consider an entity as affected by the query. This

View File

@ -166,7 +166,7 @@ public class DomainResultCreationStateImpl
@Override
public boolean forceIdentifierSelection() {
return false;
return true;
}
@Override

View File

@ -6,24 +6,133 @@
*/
package org.hibernate.query.sql.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.action.internal.BulkOperationCleanupAction;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelHelper;
import org.hibernate.metamodel.model.domain.AllowableParameterType;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.SqlOmittingQueryOptions;
import org.hibernate.query.sql.spi.NativeQueryImplementor;
import org.hibernate.query.sqm.internal.SqmUtil;
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl;
import org.hibernate.sql.exec.internal.StandardJdbcMutationExecutor;
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;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
import org.hibernate.sql.exec.spi.NativeJdbcMutation;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
import org.hibernate.sql.results.spi.RowTransformer;
import org.hibernate.type.StandardBasicTypes;
/**
* @author Steve Ebersole
*/
public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan {
private final NativeQueryImplementor nativeQuery;
private final String sql;
private final Set<String> affectedTableNames;
public NativeNonSelectQueryPlanImpl(NativeQueryImplementor nativeQuery) {
this.nativeQuery = nativeQuery;
private final List<QueryParameterImplementor<?>> parameterList;
public NativeNonSelectQueryPlanImpl(
String sql,
Set<String> affectedTableNames,
List<QueryParameterImplementor<?>> parameterList) {
this.sql = sql;
this.affectedTableNames = affectedTableNames;
this.parameterList = parameterList;
}
@Override
public int executeUpdate(ExecutionContext executionContext) {
throw new NotYetImplementedFor6Exception();
executionContext.getSession().autoFlushIfRequired( affectedTableNames );
BulkOperationCleanupAction.schedule( executionContext, affectedTableNames );
final List<JdbcParameterBinder> jdbcParameterBinders;
final JdbcParameterBindings jdbcParameterBindings;
final QueryParameterBindings queryParameterBindings = executionContext.getQueryParameterBindings();
if ( parameterList == null || parameterList.isEmpty() ) {
jdbcParameterBinders = Collections.emptyList();
jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS;
}
else {
jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() );
for ( QueryParameterImplementor<?> param : parameterList ) {
QueryParameterBinding<?> binding = queryParameterBindings.getBinding( param );
AllowableParameterType<?> type = binding.getBindType();
if ( type == null ) {
type = param.getHibernateType();
}
if ( type == null ) {
type = StandardBasicTypes.OBJECT_TYPE;
}
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping );
jdbcParameterBinders.add( jdbcParameter );
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() )
);
}
}
final JdbcMutation jdbcMutation = new NativeJdbcMutation(
sql,
jdbcParameterBinders,
affectedTableNames
);
final JdbcMutationExecutor executor = StandardJdbcMutationExecutor.INSTANCE;
final SharedSessionContractImplementor session = executionContext.getSession();
// TODO: use configurable executor instead?
// final SessionFactoryImplementor factory = session.getFactory();
// final JdbcServices jdbcServices = factory.getJdbcServices();
// return jdbcServices.getJdbcMutationExecutor().execute(
return executor.execute(
jdbcMutation,
jdbcParameterBindings,
sql -> session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {},
executionContext
);
}
}

View File

@ -266,6 +266,7 @@ public class NativeQueryImpl<R>
this.parameterMetadata = parameterInterpretation.toParameterMetadata( session );
this.occurrenceOrderedParamList = parameterInterpretation.getOccurrenceOrderedParameters();
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
this.querySpaces = new HashSet<>();
this.resultSetMapping = new ResultSetMappingImpl( resultSetMappingMemento.getName() );
resultSetMappingMemento.resolve(
@ -301,7 +302,10 @@ public class NativeQueryImpl<R>
protected void applyOptions(NamedNativeQueryMemento memento) {
super.applyOptions( memento );
this.querySpaces = CollectionHelper.makeCopy( memento.getQuerySpaces() );
final Set<String> copy = CollectionHelper.makeCopy( memento.getQuerySpaces() );
if ( copy != null ) {
this.querySpaces = copy;
}
// todo (6.0) : query returns
}
@ -579,7 +583,7 @@ public class NativeQueryImpl<R>
}
if ( queryPlan == null ) {
queryPlan = new NativeNonSelectQueryPlanImpl( this );
queryPlan = new NativeNonSelectQueryPlanImpl( sqlString, querySpaces, occurrenceOrderedParamList );
if ( cacheKey != null ) {
getSession().getFactory().getQueryEngine().getInterpretationCache().cacheNonSelectQueryPlan( cacheKey, queryPlan );
}

View File

@ -18,6 +18,7 @@ import org.hibernate.internal.EmptyScrollableResults;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.model.domain.AllowableParameterType;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor;
@ -87,27 +88,26 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() );
queryParameterBindings.visitBindings(
(param, binding) -> {
AllowableParameterType<?> type = binding.getBindType();
if ( type == null ) {
type = param.getHibernateType();
}
if ( type == null ) {
type = StandardBasicTypes.OBJECT_TYPE;
}
for ( QueryParameterImplementor<?> param : parameterList ) {
QueryParameterBinding<?> binding = queryParameterBindings.getBinding( param );
AllowableParameterType<?> type = binding.getBindType();
if ( type == null ) {
type = param.getHibernateType();
}
if ( type == null ) {
type = StandardBasicTypes.OBJECT_TYPE;
}
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping );
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping );
jdbcParameterBinders.add( jdbcParameter );
jdbcParameterBinders.add( jdbcParameter );
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() )
);
}
);
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() )
);
}
}
executionContext.getSession().autoFlushIfRequired( affectedTableNames );
@ -122,6 +122,11 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
final JdbcSelectExecutor executor = JdbcSelectExecutorStandardImpl.INSTANCE;
// TODO: use configurable executor instead?
// final SharedSessionContractImplementor session = executionContext.getSession();
// final SessionFactoryImplementor factory = session.getFactory();
// final JdbcServices jdbcServices = factory.getJdbcServices();
// return jdbcServices.getJdbcMutationExecutor().execute(
return executor.list(
jdbcSelect,
jdbcParameterBindings,

View File

@ -0,0 +1,54 @@
/*
* 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.util.Collections;
import java.util.List;
import java.util.Set;
import org.hibernate.internal.FilterJdbcParameter;
/**
* Executable JDBC command
*
* @author Christian Beikov
*/
public class NativeJdbcMutation implements JdbcMutation {
private final String sql;
private final List<JdbcParameterBinder> parameterBinders;
private final Set<String> affectedTableNames;
public NativeJdbcMutation(
String sql,
List<JdbcParameterBinder> parameterBinders,
Set<String> affectedTableNames) {
this.sql = sql;
this.parameterBinders = parameterBinders;
this.affectedTableNames = affectedTableNames;
}
@Override
public String getSql() {
return sql;
}
@Override
public List<JdbcParameterBinder> getParameterBinders() {
return parameterBinders;
}
@Override
public Set<String> getAffectedTableNames() {
return affectedTableNames;
}
@Override
public Set<FilterJdbcParameter> getFilterJdbcParameters() {
return Collections.EMPTY_SET;
}
}