NativeQuery support

- parameter handling
This commit is contained in:
Steve Ebersole 2020-07-28 14:09:54 -05:00
parent 5f7c139f7e
commit bbe3a6b0ad
4 changed files with 201 additions and 67 deletions

View File

@ -6,24 +6,32 @@
*/
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.ScrollMode;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.model.domain.AllowableParameterType;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
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.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
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.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
import org.hibernate.sql.results.spi.RowTransformer;
import org.hibernate.type.StandardBasicTypes;
/**
* @author Steve Ebersole
@ -54,8 +62,51 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
@Override
public List<R> performList(ExecutionContext executionContext) {
final List<JdbcParameterBinder> jdbcParameterBinders = resolveJdbcParamBinders( executionContext );
final JdbcParameterBindings jdbcParameterBindings = resolveJdbcParamBindings( executionContext, jdbcParameterBinders );
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() );
queryParameterBindings.visitBindings(
(param, binding) -> {
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 JdbcParameterBinding() {
@Override
public JdbcMapping getBindType() {
return jdbcMapping;
}
@Override
public Object getBindValue() {
return binding.getBindValue();
}
}
);
}
);
}
final JdbcSelect jdbcSelect = new JdbcSelect(
sql,
@ -76,62 +127,53 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
);
}
private List<JdbcParameterBinder> resolveJdbcParamBinders(ExecutionContext executionContext) {
if ( parameterList == null || parameterList.isEmpty() ) {
return Collections.emptyList();
}
throw new NotYetImplementedFor6Exception( getClass() );
}
private JdbcParameterBindings resolveJdbcParamBindings(
ExecutionContext executionContext,
List<JdbcParameterBinder> jdbcParameterBinders) {
if ( jdbcParameterBinders.isEmpty() ) {
return JdbcParameterBindings.NO_BINDINGS;
}
throw new NotYetImplementedFor6Exception( getClass() );
}
// private List<JdbcParameterBinder> resolveJdbcParameterBinders(ExecutionContext executionContext) {
// final List<JdbcParameterBinder> jdbcParameterBinders = CollectionHelper.arrayList( parameterList.size() );
//
// for ( QueryParameterImplementor parameter : parameterList ) {
// final QueryParameterBinding parameterBinding = executionContext.getDomainParameterBindingContext()
// .getQueryParameterBindings()
// .getBinding( parameter );
// AllowableParameterType type = parameterBinding.getBindType();
// if ( type == null ) {
// type = parameter.getHibernateType();
// }
//
// type.dehydrate(
// type.unresolve( parameterBinding.getBindValue(), executionContext.getSession() ),
// (jdbcValue, sqlExpressableType, boundColumn) -> jdbcParameterBinders.add(
// (statement, startPosition, jdbcParameterBindings, executionContext1) -> {
// //noinspection unchecked
// sqlExpressableType.getJdbcValueBinder().bind(
// statement,
// startPosition,
// jdbcValue,
// executionContext1
// );
// return 1;
// }
// ),
// Clause.IRRELEVANT,
// executionContext.getSession()
// );
// }
//
// return jdbcParameterBinders;
// }
@Override
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext) {
final List<JdbcParameterBinder> jdbcParameterBinders = resolveJdbcParamBinders( executionContext );
final JdbcParameterBindings jdbcParameterBindings = resolveJdbcParamBindings( executionContext, jdbcParameterBinders );
final List<JdbcParameterBinder> jdbcParameterBinders;
final JdbcParameterBindings jdbcParameterBindings;
final QueryParameterBindings queryParameterBindings = executionContext.getQueryParameterBindings();
if ( parameterList.isEmpty() ) {
jdbcParameterBinders = Collections.emptyList();
jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS;
}
else {
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;
}
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping );
jdbcParameterBinders.add( jdbcParameter );
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBinding() {
@Override
public JdbcMapping getBindType() {
return jdbcMapping;
}
@Override
public Object getBindValue() {
return binding.getBindValue();
}
}
);
}
);
}
final JdbcSelect jdbcSelect = new JdbcSelect(
sql,

View File

@ -29,8 +29,6 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
NAMED
}
private final int ordinalParameterBase;
private ParameterStyle parameterStyle;
private Map<String, QueryParameterImplementor<?>> namedQueryParameters;
@ -42,12 +40,6 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
@SuppressWarnings("WeakerAccess")
public ParameterRecognizerImpl(SessionFactoryImplementor factory) {
if ( factory.getSessionFactoryOptions().isJpaBootstrap() ) {
ordinalParameterBase = 1;
}
else {
ordinalParameterBase = 1;
}
ordinalParameterImplicitPosition = 1;
}
@ -63,7 +55,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
for ( Integer position : positionsArray ) {
if ( position != previous + 1 ) {
if ( first ) {
throw new QueryException( "Positional parameters did not start with base [" + ordinalParameterBase + "] : " + position );
throw new QueryException( "Positional parameters did not start with base 1 : " + position );
}
else {
throw new QueryException( "Gap in positional parameter positions; skipped " + (previous+1) );

View File

@ -0,0 +1,99 @@
/*
* 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.sql;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import javax.persistence.TemporalType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
/**
* @author Steve Ebersole
*/
@DomainModel( standardModels = StandardDomainModel.HELPDESK )
@SessionFactory
public class NativeQueryParameterTests {
@Test
public void testBasicParameterBinding(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createNativeQuery( "select t.id, t.key, t.subject from ticket t where t.key = ?" )
.setParameter( 1, "ABC-123" )
.list();
}
);
}
@Test
public void testTypedParameterBinding(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createNativeQuery( "select t.id, t.key, t.subject from ticket t where t.key = ?" )
.setParameter( 1, "ABC-123", StandardBasicTypes.STRING )
.list();
}
);
}
@Test
public void testTemporalParameterBinding(SessionFactoryScope scope) {
final String qryString = "select i.id, i.effectiveStart, i.effectiveEnd " +
" from incident i" +
" where i.reported BETWEEN ? AND ?";
scope.inTransaction(
session -> {
{
final Instant now = Instant.now();
final Instant startPeriod = now.minus( 30, ChronoUnit.DAYS );
session.createNativeQuery( qryString )
.setParameter( 1, startPeriod )
.setParameter( 2, now )
.list();
}
{
final Instant now = Instant.now();
final Instant startPeriod = now.minus( 30, ChronoUnit.DAYS );
session.createNativeQuery( qryString )
.setParameter( 1, startPeriod, TemporalType.DATE )
.setParameter( 2, now, TemporalType.DATE )
.list();
}
{
final Instant now = Instant.now();
final Instant startPeriod = now.minus( 30, ChronoUnit.DAYS );
session.createNativeQuery( qryString )
.setParameter( 1, startPeriod, TemporalType.TIMESTAMP )
.setParameter( 2, now, TemporalType.TIMESTAMP )
.list();
}
{
final Instant now = Instant.now();
final Instant startPeriod = now.minus( 30, ChronoUnit.DAYS );
session.createNativeQuery( qryString )
.setParameter( 1, startPeriod, TemporalType.TIME )
.setParameter( 2, now, TemporalType.TIME )
.list();
}
}
);
}
}

View File

@ -18,7 +18,8 @@ public class HelpDeskDomainModel extends AbstractDomainModelDescriptor {
super(
Status.class,
Account.class,
Ticket.class
Ticket.class,
Incident.class
);
}
}