NativeQuery support

- initial working support - simple scalar queries
This commit is contained in:
Steve Ebersole 2020-07-23 12:26:01 -05:00
parent 25fc3e2dce
commit 5dded5de7c
42 changed files with 1186 additions and 463 deletions

View File

@ -6,9 +6,9 @@
*/ */
package org.hibernate.engine.query.internal; package org.hibernate.engine.query.internal;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.query.spi.NativeQueryInterpreter; import org.hibernate.engine.query.spi.NativeQueryInterpreter;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.sql.internal.NativeSelectQueryPlanImpl;
import org.hibernate.query.sql.internal.ParameterParser; import org.hibernate.query.sql.internal.ParameterParser;
import org.hibernate.query.sql.spi.NativeSelectQueryDefinition; import org.hibernate.query.sql.spi.NativeSelectQueryDefinition;
import org.hibernate.query.sql.spi.NativeSelectQueryPlan; import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
@ -32,15 +32,12 @@ public class NativeQueryInterpreterStandardImpl implements NativeQueryInterprete
public <R> NativeSelectQueryPlan<R> createQueryPlan( public <R> NativeSelectQueryPlan<R> createQueryPlan(
NativeSelectQueryDefinition<R> queryDefinition, NativeSelectQueryDefinition<R> queryDefinition,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
throw new NotYetImplementedFor6Exception( getClass() ); return new NativeSelectQueryPlanImpl<>(
queryDefinition.getSqlString(),
// CustomQuery customQuery = new SQLCustomQuery( queryDefinition.getAffectedTableNames(),
// specification.getQueryString(), queryDefinition.getQueryParameterList(),
// specification.getQueryReturns(), queryDefinition.getJdbcValuesMappingProducer(),
// specification.getQuerySpaces(), queryDefinition.getRowTransformer()
// sessionFactory );
// );
//
// return new NativeSQLQueryPlan( specification.getQueryString(), customQuery );
} }
} }

View File

@ -13,6 +13,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
@ -468,4 +469,15 @@ public final class ArrayHelper {
public static boolean isEmpty(Object[] array) { public static boolean isEmpty(Object[] array) {
return array == null || array.length == 0; return array == null || array.length == 0;
} }
public static <T> void forEach(T[] array, Consumer<T> consumer) {
if ( array == null ) {
return;
}
//noinspection ForLoopReplaceableByForEach
for ( int i = 0; i < array.length; i++ ) {
consumer.accept( array[ i ] );
}
}
} }

View File

@ -49,11 +49,12 @@ import org.hibernate.query.Query;
import org.hibernate.query.QueryParameter; import org.hibernate.query.QueryParameter;
import org.hibernate.query.internal.QueryOptionsImpl; import org.hibernate.query.internal.QueryOptionsImpl;
import org.hibernate.query.procedure.ProcedureParameter; import org.hibernate.query.procedure.ProcedureParameter;
import org.hibernate.query.results.ResultSetMapping;
import org.hibernate.query.results.ResultSetMappingImpl;
import org.hibernate.query.spi.AbstractQuery; import org.hibernate.query.spi.AbstractQuery;
import org.hibernate.query.spi.MutableQueryOptions; import org.hibernate.query.spi.MutableQueryOptions;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.result.NoMoreReturnsException; import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Output; import org.hibernate.result.Output;
import org.hibernate.result.ResultSetOutput; import org.hibernate.result.ResultSetOutput;
@ -83,7 +84,7 @@ public class ProcedureCallImpl<R>
private final ProcedureParameterMetadataImpl parameterMetadata; private final ProcedureParameterMetadataImpl parameterMetadata;
private final ProcedureParamBindings paramBindings; private final ProcedureParamBindings paramBindings;
private final List<DomainResultProducer<?>> domainResultProducers; private final ResultSetMapping resultSetMapping = new ResultSetMappingImpl();
private Set<String> synchronizedQuerySpaces; private Set<String> synchronizedQuerySpaces;
@ -106,7 +107,6 @@ public class ProcedureCallImpl<R>
this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() ); this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() );
this.synchronizedQuerySpaces = null; this.synchronizedQuerySpaces = null;
this.domainResultProducers = null;
} }
/** /**
* The result Class(es) return form * The result Class(es) return form
@ -125,12 +125,11 @@ public class ProcedureCallImpl<R>
this.parameterMetadata = new ProcedureParameterMetadataImpl(); this.parameterMetadata = new ProcedureParameterMetadataImpl();
this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() ); this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() );
this.domainResultProducers = CollectionHelper.arrayList( resultClasses.length );
this.synchronizedQuerySpaces = new HashSet<>(); this.synchronizedQuerySpaces = new HashSet<>();
Util.resolveResultSetMappingClasses( Util.resolveResultSetMappingClasses(
resultClasses, resultClasses,
domainResultProducers::add, resultSetMapping,
synchronizedQuerySpaces::add, synchronizedQuerySpaces::add,
getSession().getFactory() getSession().getFactory()
); );
@ -156,12 +155,11 @@ public class ProcedureCallImpl<R>
this.parameterMetadata = new ProcedureParameterMetadataImpl(); this.parameterMetadata = new ProcedureParameterMetadataImpl();
this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() ); this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() );
this.domainResultProducers = CollectionHelper.arrayList( resultSetMappingNames.length );
this.synchronizedQuerySpaces = new HashSet<>(); this.synchronizedQuerySpaces = new HashSet<>();
Util.resolveResultSetMappingNames( Util.resolveResultSetMappingNames(
resultSetMappingNames, resultSetMappingNames,
domainResultProducers::add, resultSetMapping,
synchronizedQuerySpaces::add, synchronizedQuerySpaces::add,
getSession().getFactory() getSession().getFactory()
); );
@ -180,13 +178,12 @@ public class ProcedureCallImpl<R>
this.parameterMetadata = new ProcedureParameterMetadataImpl( memento, session ); this.parameterMetadata = new ProcedureParameterMetadataImpl( memento, session );
this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() ); this.paramBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() );
this.domainResultProducers = new ArrayList<>();
this.synchronizedQuerySpaces = CollectionHelper.makeCopy( memento.getQuerySpaces() ); this.synchronizedQuerySpaces = CollectionHelper.makeCopy( memento.getQuerySpaces() );
Util.resolveResultSetMappings( Util.resolveResultSetMappings(
memento.getResultSetMappingNames(), memento.getResultSetMappingNames(),
memento.getResultSetMappingClasses(), memento.getResultSetMappingClasses(),
domainResultProducers::add, resultSetMapping,
synchronizedQuerySpaces::add, synchronizedQuerySpaces::add,
getSession().getFactory() getSession().getFactory()
); );

View File

@ -13,7 +13,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.query.named.NamedQueryRepository; import org.hibernate.query.named.NamedQueryRepository;
import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.query.spi.ResultSetMapping; import org.hibernate.query.results.ResultSetMapping;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -32,7 +32,7 @@ public class Util {
public static void resolveResultSetMappings( public static void resolveResultSetMappings(
String[] resultSetMappingNames, String[] resultSetMappingNames,
Class[] resultSetMappingClasses, Class[] resultSetMappingClasses,
Consumer<DomainResultProducer> resultProducerConsumer, ResultSetMapping resultSetMapping,
Consumer<String> querySpaceConsumer, Consumer<String> querySpaceConsumer,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
if ( ! ArrayHelper.isEmpty( resultSetMappingNames ) ) { if ( ! ArrayHelper.isEmpty( resultSetMappingNames ) ) {
@ -40,10 +40,10 @@ public class Util {
if ( ! ArrayHelper.isEmpty( resultSetMappingClasses ) ) { if ( ! ArrayHelper.isEmpty( resultSetMappingClasses ) ) {
throw new IllegalArgumentException( "Cannot specify both result-set mapping names and classes" ); throw new IllegalArgumentException( "Cannot specify both result-set mapping names and classes" );
} }
resolveResultSetMappingNames( resultSetMappingNames, resultProducerConsumer, querySpaceConsumer, sessionFactory ); resolveResultSetMappingNames( resultSetMappingNames, resultSetMapping, querySpaceConsumer, sessionFactory );
} }
else if ( ! ArrayHelper.isEmpty( resultSetMappingClasses ) ) { else if ( ! ArrayHelper.isEmpty( resultSetMappingClasses ) ) {
resolveResultSetMappingClasses( resultSetMappingClasses, resultProducerConsumer, querySpaceConsumer, sessionFactory ); resolveResultSetMappingClasses( resultSetMappingClasses, resultSetMapping, querySpaceConsumer, sessionFactory );
} }
// otherwise, nothing to resolve // otherwise, nothing to resolve
@ -51,22 +51,24 @@ public class Util {
public static void resolveResultSetMappingNames( public static void resolveResultSetMappingNames(
String[] resultSetMappingNames, String[] resultSetMappingNames,
Consumer<DomainResultProducer> resultProducerConsumer, ResultSetMapping resultSetMapping,
Consumer<String> querySpaceConsumer, Consumer<String> querySpaceConsumer,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
final NamedQueryRepository namedQueryRepository = sessionFactory.getQueryEngine().getNamedQueryRepository(); final NamedQueryRepository namedQueryRepository = sessionFactory.getQueryEngine().getNamedQueryRepository();
for ( String resultSetMappingName : resultSetMappingNames ) { for ( String resultSetMappingName : resultSetMappingNames ) {
final NamedResultSetMappingMemento memento = namedQueryRepository.getResultSetMappingMemento( resultSetMappingName ); final NamedResultSetMappingMemento memento = namedQueryRepository.getResultSetMappingMemento( resultSetMappingName );
final ResultSetMapping resultSetMapping = memento.toResultSetMapping(); memento.resolve(
resultProducerConsumer.accept( resultSetMapping ); resultSetMapping,
// todo (6.0) : determine query spaces - maybe passing the consumer to `NamedResultSetMappingMemento#toResultSetMapping`? querySpaceConsumer,
sessionFactory
);
} }
} }
public static void resolveResultSetMappingClasses( public static void resolveResultSetMappingClasses(
Class[] resultSetMappingClasses, Class[] resultSetMappingClasses,
Consumer<DomainResultProducer> resultProducerConsumer, ResultSetMapping resultSetMapping,
Consumer<String> querySpaceConsumer, Consumer<String> querySpaceConsumer,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
throw new NotYetImplementedFor6Exception( Util.class ); throw new NotYetImplementedFor6Exception( Util.class );

View File

@ -25,7 +25,7 @@ public abstract class AbstractQueryParameter<T> implements QueryParameterImpleme
@Override @Override
public void disallowMultiValuedBinding() { public void disallowMultiValuedBinding() {
QueryLogger.QUERY_LOGGER.debugf( "QueryParameter#disallowMultiValuedBinding() called : %s", this ); QueryLogger.QUERY_MESSAGE_LOGGER.debugf( "QueryParameter#disallowMultiValuedBinding() called : %s", this );
this.allowMultiValuedBinding = true; this.allowMultiValuedBinding = true;
} }

View File

@ -26,7 +26,8 @@ import static org.jboss.logging.Logger.Level.ERROR;
public interface QueryLogger extends BasicLogger { public interface QueryLogger extends BasicLogger {
String LOGGER_NAME = "org.hibernate.orm.query"; String LOGGER_NAME = "org.hibernate.orm.query";
QueryLogger QUERY_LOGGER = Logger.getMessageLogger( QueryLogger.class, LOGGER_NAME ); Logger QUERY_LOGGER = Logger.getLogger( LOGGER_NAME );
QueryLogger QUERY_MESSAGE_LOGGER = Logger.getMessageLogger( QueryLogger.class, LOGGER_NAME );
boolean TRACE_ENABLED = QUERY_LOGGER.isTraceEnabled(); boolean TRACE_ENABLED = QUERY_LOGGER.isTraceEnabled();
boolean DEBUG_ENABLED = QUERY_LOGGER.isDebugEnabled(); boolean DEBUG_ENABLED = QUERY_LOGGER.isDebugEnabled();

View File

@ -123,6 +123,10 @@ public class NamedQueryRepositoryImpl implements NamedQueryRepository {
resultSetMappingMementoMap.put( name, memento ); resultSetMappingMementoMap.put( name, memento );
} }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Prepare repository for use
@Override @Override
public void prepare( public void prepare(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,

View File

@ -6,10 +6,12 @@
*/ */
package org.hibernate.query.internal; package org.hibernate.query.internal;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.query.spi.ResultSetMapping; import org.hibernate.query.results.ResultSetMapping;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -27,7 +29,10 @@ public class NamedResultSetMappingMementoImpl implements NamedResultSetMappingMe
} }
@Override @Override
public ResultSetMapping toResultSetMapping() { public void resolve(
ResultSetMapping resultSetMapping,
Consumer<String> querySpaceConsumer,
SessionFactoryImplementor sessionFactory) {
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
} }
} }

View File

@ -1,16 +0,0 @@
/*
* 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.internal;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
/**
* @author Steve Ebersole
*/
public interface NativeQueryReturnBuilder {
NativeSQLQueryReturn buildReturn();
}

View File

@ -1,74 +0,0 @@
/*
* 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.internal;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.LockMode;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryJoinReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.query.NativeQuery;
/**
* @author Steve Ebersole
*/
public class NativeQueryReturnBuilderFetchImpl implements NativeQuery.FetchReturn, NativeQueryReturnBuilder {
private final String alias;
private String ownerTableAlias;
private final String joinedPropertyName;
private LockMode lockMode = LockMode.READ;
private Map<String, String[]> propertyMappings;
public NativeQueryReturnBuilderFetchImpl(String alias, String ownerTableAlias, String joinedPropertyName) {
this.alias = alias;
this.ownerTableAlias = ownerTableAlias;
this.joinedPropertyName = joinedPropertyName;
}
public NativeQuery.FetchReturn setLockMode(LockMode lockMode) {
this.lockMode = lockMode;
return this;
}
public NativeQuery.FetchReturn addProperty(String propertyName, String columnAlias) {
addProperty( propertyName ).addColumnAlias( columnAlias );
return this;
}
public NativeQuery.ReturnProperty addProperty(final String propertyName) {
if ( propertyMappings == null ) {
propertyMappings = new HashMap<>();
}
return new NativeQuery.ReturnProperty() {
public NativeQuery.ReturnProperty addColumnAlias(String columnAlias) {
String[] columnAliases = propertyMappings.get( propertyName );
if ( columnAliases == null ) {
columnAliases = new String[] {columnAlias};
}
else {
String[] newColumnAliases = new String[columnAliases.length + 1];
System.arraycopy( columnAliases, 0, newColumnAliases, 0, columnAliases.length );
newColumnAliases[columnAliases.length] = columnAlias;
columnAliases = newColumnAliases;
}
propertyMappings.put( propertyName, columnAliases );
return this;
}
};
}
public NativeSQLQueryReturn buildReturn() {
return new NativeSQLQueryJoinReturn(
alias,
ownerTableAlias,
joinedPropertyName,
propertyMappings,
lockMode
);
}
}

View File

@ -1,71 +0,0 @@
/*
* 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.internal;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.LockMode;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
import org.hibernate.query.NativeQuery;
/**
* @author Steve Ebersole
*/
public class NativeQueryReturnBuilderRootImpl implements NativeQuery.RootReturn, NativeQueryReturnBuilder {
private final String alias;
private final String entityName;
private LockMode lockMode = LockMode.READ;
private Map<String, String[]> propertyMappings;
public NativeQueryReturnBuilderRootImpl(String alias, String entityName) {
this.alias = alias;
this.entityName = entityName;
}
public NativeQuery.RootReturn setLockMode(LockMode lockMode) {
this.lockMode = lockMode;
return this;
}
public NativeQuery.RootReturn setDiscriminatorAlias(String alias) {
addProperty( "class", alias );
return this;
}
public NativeQuery.RootReturn addProperty(String propertyName, String columnAlias) {
addProperty( propertyName ).addColumnAlias( columnAlias );
return this;
}
public NativeQuery.ReturnProperty addProperty(final String propertyName) {
if ( propertyMappings == null ) {
propertyMappings = new HashMap<>();
}
return new NativeQuery.ReturnProperty() {
public NativeQuery.ReturnProperty addColumnAlias(String columnAlias) {
String[] columnAliases = propertyMappings.get( propertyName );
if ( columnAliases == null ) {
columnAliases = new String[] {columnAlias};
}
else {
String[] newColumnAliases = new String[columnAliases.length + 1];
System.arraycopy( columnAliases, 0, newColumnAliases, 0, columnAliases.length );
newColumnAliases[columnAliases.length] = columnAlias;
columnAliases = newColumnAliases;
}
propertyMappings.put( propertyName, columnAliases );
return this;
}
};
}
public NativeSQLQueryReturn buildReturn() {
return new NativeSQLQueryRootReturn( alias, entityName, propertyMappings, lockMode );
}
}

View File

@ -6,8 +6,11 @@
*/ */
package org.hibernate.query.named; package org.hibernate.query.named;
import java.util.function.Consumer;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.query.spi.ResultSetMapping; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.results.ResultSetMapping;
/** /**
* Used to keep information about named result mappings defined by the * Used to keep information about named result mappings defined by the
@ -27,8 +30,5 @@ import org.hibernate.query.spi.ResultSetMapping;
public interface NamedResultSetMappingMemento { public interface NamedResultSetMappingMemento {
String getName(); String getName();
/** void resolve(ResultSetMapping resultSetMapping, Consumer<String> querySpaceConsumer, SessionFactoryImplementor sessionFactory);
* todo (6.0) : determine the proper arguments. depends on how we port JdbcValues, etc from the original 6.0 work
*/
ResultSetMapping toResultSetMapping();
} }

View File

@ -0,0 +1,64 @@
/*
* 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.results;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.hibernate.internal.util.collections.ArrayHelper;
/**
* @author Steve Ebersole
*/
public abstract class AbstractPropertyContainer<T extends AbstractPropertyContainer<T>> implements PropertyContainer {
private Map<String,FetchBuilder> fetchBuilderMap;
protected abstract String getPropertyBase();
@Override
public T addProperty(String propertyName, String columnAlias) {
final FetchBuilder fetchBuilder = addProperty( propertyName );
fetchBuilder.addColumnAlias( columnAlias );
return (T) this;
}
@Override
public T addProperty(String propertyName, String... columnAliases) {
final FetchBuilder fetchBuilder = addProperty( propertyName );
ArrayHelper.forEach( columnAliases, fetchBuilder::addColumnAlias );
return (T) this;
}
@Override
public FetchBuilder addProperty(String propertyName) {
if ( fetchBuilderMap == null ) {
fetchBuilderMap = new HashMap<>();
}
else {
final FetchBuilder existing = fetchBuilderMap.get( propertyName );
if ( existing != null ) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Fetch was already defined for %s.%s : %s",
getPropertyBase(),
propertyName,
existing
)
);
}
}
final FetchBuilder fetchBuilder = new StandardFetchBuilderImpl();
fetchBuilderMap.put( propertyName, fetchBuilder );
return fetchBuilder;
}
}

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.query.results;
/**
* @author Steve Ebersole
*/
public interface BuilderGraphNode {
}

View File

@ -0,0 +1,63 @@
/*
* 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.results;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @author Steve Ebersole
*/
public class Builders {
public static ScalarResultBuilder scalar(
String columnAlias,
BasicType<?> type) {
return new ScalarResultBuilder( columnAlias, type );
}
public static ScalarResultBuilder scalar(
String columnAlias,
Class<?> javaType,
SessionFactoryImplementor factory) {
return new ScalarResultBuilder(
columnAlias,
factory.getTypeConfiguration().getBasicTypeForJavaType( javaType )
);
}
public static DomainResult<?> implicitScalarDomainResult(
int colIndex,
String columnName,
JdbcValuesMetadata jdbcResultsMetadata,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( colIndex );
final BasicJavaDescriptor<?> javaTypeDescriptor = sqlTypeDescriptor.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
final BasicType<?> jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( javaTypeDescriptor, sqlTypeDescriptor );
sqlSelectionConsumer.accept( new SqlSelectionImpl( colIndex, jdbcMapping ) );
return new BasicResult<>( colIndex, columnName, javaTypeDescriptor );
}
public static EntityResultBuilder entity(String tableAlias, String entityName) {
throw new NotYetImplementedFor6Exception( );
}
public static LegacyFetchBuilder fetch(String tableAlias, String ownerTableAlias, String joinPropertyName) {
throw new NotYetImplementedFor6Exception( );
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.results;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.NativeQuery;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
/**
* @author Steve Ebersole
*/
public class EntityResultBuilder
extends AbstractPropertyContainer<EntityResultBuilder>
implements ResultBuilder, NativeQuery.RootReturn {
private final String entityName;
private final String tableAlias;
private LockMode lockMode;
private String discriminatorColumnAlias;
public EntityResultBuilder(String entityName, String tableAlias) {
this.entityName = entityName;
this.tableAlias = tableAlias;
}
@Override
protected String getPropertyBase() {
return entityName;
}
@Override
public DomainResult<?> buildReturn(
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, LegacyFetchBuilder> legacyFetchResolver,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public EntityResultBuilder setLockMode(LockMode lockMode) {
this.lockMode = lockMode;
return this;
}
@Override
public EntityResultBuilder setDiscriminatorAlias(String columnAlias) {
this.discriminatorColumnAlias = columnAlias;
return this;
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.results;
import java.util.function.Consumer;
import org.hibernate.Incubating;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.NativeQuery;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
/**
* Responsible for building a single {@link DomainResult} instance as part of
* the overall mapping of native / procedure query results.
*
* @author Steve Ebersole
*/
@Incubating
public interface FetchBuilder extends NativeQuery.ReturnProperty {
Fetch buildFetch(
FetchParent parent,
JdbcValuesMetadata jdbcResultsMetadata,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory);
}

View File

@ -0,0 +1,55 @@
/*
* 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.results;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping;
/**
* Implementation of JdbcValuesMapping for native / procedure queries
*
* @author Steve Ebersole
*/
public class JdbcValuesMappingImpl implements JdbcValuesMapping {
private final List<SqlSelection> sqlSelections;
private final List<DomainResult<?>> domainResults;
public JdbcValuesMappingImpl(
List<SqlSelection> sqlSelections,
List<DomainResult<?>> domainResults) {
this.sqlSelections = sqlSelections;
this.domainResults = domainResults;
}
@Override
public List<SqlSelection> getSqlSelections() {
return sqlSelections;
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public List<DomainResult> getDomainResults() {
return (List) domainResults;
}
@Override
@SuppressWarnings("rawtypes")
public List<DomainResultAssembler> resolveAssemblers(AssemblerCreationState creationState) {
final List<DomainResultAssembler> assemblers = new ArrayList<>( domainResults.size() );
domainResults.forEach(
domainResult -> assemblers.add( domainResult.createResultAssembler( creationState ) )
);
return assemblers;
}
}

View File

@ -0,0 +1,18 @@
/*
* 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.results;
import org.hibernate.query.NativeQuery;
/**
* @author Steve Ebersole
*/
public interface LegacyFetchBuilder extends FetchBuilder, NativeQuery.FetchReturn {
String getOwnerAlias();
String getFetchAlias();
String getFetchedAttributeName();
}

View File

@ -0,0 +1,27 @@
/*
* 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.results;
/**
* @author Steve Ebersole
*/
public interface PropertyContainer {
/**
* Add a simple property-to-one-column mapping.
*/
PropertyContainer addProperty(String propertyName, String columnAlias);
/**
* Add a property mapped to multiple columns
*/
PropertyContainer addProperty(String propertyName, String... columnAliases);
/**
* Add a property whose columns can later be defined using {@link FetchBuilder#addColumnAlias}
*/
FetchBuilder addProperty(String propertyName);
}

View File

@ -0,0 +1,31 @@
/*
* 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.results;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.hibernate.Incubating;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
/**
* Responsible for building a single {@link DomainResult} instance as part of
* the overall mapping of native / procedure query results.
*
* @author Steve Ebersole
*/
@Incubating
public interface ResultBuilder {
DomainResult<?> buildReturn(
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String,String,LegacyFetchBuilder> legacyFetchResolver,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory);
}

View File

@ -0,0 +1,40 @@
/*
* 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.results;
import java.util.function.BiConsumer;
import org.hibernate.Incubating;
import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
/**
* Acts as the {@link JdbcValuesMappingProducer} for {@link org.hibernate.query.NativeQuery}
* or {@link org.hibernate.procedure.ProcedureCall} / {@link javax.persistence.StoredProcedureQuery}
* instances.
*
* Collects builders for results and fetches and manages resolving them via JdbcValuesMappingProducer
*
* @see org.hibernate.query.NativeQuery#addScalar
* @see org.hibernate.query.NativeQuery#addEntity
* @see org.hibernate.query.NativeQuery#addJoin
* @see org.hibernate.query.NativeQuery#addFetch
* @see org.hibernate.query.NativeQuery#addRoot
*
* @author Steve Ebersole
*/
@Incubating
public interface ResultSetMapping extends JdbcValuesMappingProducer {
int getNumberOfResultBuilders();
void visitResultBuilders(BiConsumer<Integer, ResultBuilder> resultBuilderConsumer);
void addResultBuilder(ResultBuilder resultBuilder);
void addLegacyFetchBuilder(LegacyFetchBuilder fetchBuilder);
NamedResultSetMappingMemento toMemento(String name);
}

View File

@ -0,0 +1,177 @@
/*
* 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.results;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.Incubating;
import org.hibernate.Internal;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @author Steve Ebersole
*/
@Incubating
@Internal
public class ResultSetMappingImpl implements ResultSetMapping {
private List<ResultBuilder> resultBuilders;
private Map<String, Map<String,LegacyFetchBuilder>> legacyFetchBuilders;
@Override
public int getNumberOfResultBuilders() {
return resultBuilders == null ? 0 : resultBuilders.size();
}
@Override
public void visitResultBuilders(BiConsumer<Integer, ResultBuilder> resultBuilderConsumer) {
if ( resultBuilders == null ) {
return;
}
for ( int i = 0; i < resultBuilders.size(); i++ ) {
resultBuilderConsumer.accept( i, resultBuilders.get( i ) );
}
}
@Override
public void addResultBuilder(ResultBuilder resultBuilder) {
if ( resultBuilders == null ) {
resultBuilders = new ArrayList<>();
}
resultBuilders.add( resultBuilder );
}
@Override
public void addLegacyFetchBuilder(LegacyFetchBuilder fetchBuilder) {
final Map<String, LegacyFetchBuilder> existingFetchBuildersByOwner;
if ( legacyFetchBuilders == null ) {
legacyFetchBuilders = new HashMap<>();
existingFetchBuildersByOwner = null;
}
else {
existingFetchBuildersByOwner = legacyFetchBuilders.get( fetchBuilder.getOwnerAlias() );
}
final Map<String, LegacyFetchBuilder> fetchBuildersByOwner;
if ( existingFetchBuildersByOwner == null ) {
fetchBuildersByOwner = new HashMap<>();
legacyFetchBuilders.put( fetchBuilder.getOwnerAlias(), fetchBuildersByOwner );
}
else {
fetchBuildersByOwner = existingFetchBuildersByOwner;
}
final LegacyFetchBuilder previousBuilder = fetchBuildersByOwner.put( fetchBuilder.getFetchedAttributeName(), fetchBuilder );
if ( previousBuilder != null ) {
// todo (6.0) : error? log? nothing?
}
}
@Override
public JdbcValuesMapping resolve(
JdbcValuesMetadata jdbcResultsMetadata,
SessionFactoryImplementor sessionFactory) {
final List<SqlSelection> sqlSelections = new ArrayList<>( jdbcResultsMetadata.getColumnCount() );
final int numberOfResults;
if ( resultBuilders == null ) {
numberOfResults = jdbcResultsMetadata.getColumnCount();
}
else {
numberOfResults = resultBuilders.size();
}
final List<DomainResult<?>> domainResults = new ArrayList<>( numberOfResults );
for ( int i = 0; i < numberOfResults; i++ ) {
final ResultBuilder resultBuilder = resultBuilders != null
? resultBuilders.get( i )
: null;
final DomainResult<?> domainResult;
if ( resultBuilder == null ) {
domainResult = makeImplicitDomainResult(
i,
sqlSelections::add,
jdbcResultsMetadata,
sessionFactory
);
}
else {
domainResult = resultBuilder.buildReturn(
jdbcResultsMetadata,
(ownerAlias, fetchName) -> {
if ( legacyFetchBuilders == null ) {
return null;
}
final Map<String, LegacyFetchBuilder> fetchBuildersForOwner = legacyFetchBuilders.get(
ownerAlias );
if ( fetchBuildersForOwner == null ) {
return null;
}
return fetchBuildersForOwner.get( fetchName );
},
sqlSelections::add,
sessionFactory
);
}
domainResults.add( domainResult );
}
return new JdbcValuesMappingImpl( sqlSelections, domainResults );
}
private DomainResult<?> makeImplicitDomainResult(
int valuesArrayPosition,
Consumer<SqlSelection> sqlSelectionConsumer,
JdbcValuesMetadata jdbcResultsMetadata,
SessionFactoryImplementor sessionFactory) {
final int jdbcPosition = valuesArrayPosition + 1;
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( jdbcPosition );
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JavaTypeDescriptor<?> javaTypeDescriptor = sqlTypeDescriptor.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
final BasicType<?> jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve(
javaTypeDescriptor,
sqlTypeDescriptor
);
final String name = jdbcResultsMetadata.resolveColumnName( jdbcPosition );
final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( valuesArrayPosition, jdbcMapping );
sqlSelectionConsumer.accept( sqlSelection );
return new BasicResult( valuesArrayPosition, name, jdbcMapping.getJavaTypeDescriptor() );
}
@Override
public NamedResultSetMappingMemento toMemento(String name) {
throw new NotYetImplementedFor6Exception( getClass() );
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.results;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @see javax.persistence.ColumnResult
*
* @author Steve Ebersole
*/
public class ScalarResultBuilder implements ResultBuilder {
private final String explicitName;
private final BasicType<?> explicitType;
ScalarResultBuilder(String explicitName, BasicType<?> explicitType) {
assert explicitName != null;
this.explicitName = explicitName;
this.explicitType = explicitType;
}
public String getExplicitName() {
return explicitName;
}
@Override
public DomainResult<?> buildReturn(
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, LegacyFetchBuilder> legacyFetchResolver,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( explicitName );
final int valuesArrayPosition = jdbcPosition - 1;
final BasicType<?> jdbcMapping;
if ( explicitType != null ) {
jdbcMapping = explicitType;
}
else {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( jdbcPosition );
final JavaTypeDescriptor<?> javaTypeDescriptor = sqlTypeDescriptor.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( javaTypeDescriptor, sqlTypeDescriptor );
}
final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( valuesArrayPosition, jdbcMapping );
sqlSelectionConsumer.accept( sqlSelection );
return new BasicResult( valuesArrayPosition, explicitName, jdbcMapping.getJavaTypeDescriptor() );
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.results;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.type.descriptor.ValueExtractor;
/**
* SqlSelection for NativeQuery
*
* @author Steve Ebersole
*/
public class SqlSelectionImpl implements SqlSelection {
private final int valuesArrayPosition;
private final BasicValuedMapping valueMapping;
public SqlSelectionImpl(int valuesArrayPosition, BasicValuedMapping valueMapping) {
this.valuesArrayPosition = valuesArrayPosition;
this.valueMapping = valueMapping;
}
@Override
public ValueExtractor getJdbcValueExtractor() {
return valueMapping.getJdbcMapping().getJdbcValueExtractor();
}
@Override
public int getValuesArrayPosition() {
return valuesArrayPosition;
}
@Override
public MappingModelExpressable getExpressionType() {
return valueMapping;
}
@Override
public void accept(SqlAstWalker sqlAstWalker) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.results;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.NativeQuery;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
/**
* @author Steve Ebersole
*/
public class StandardFetchBuilderImpl implements FetchBuilder {
@Override
public Fetch buildFetch(
FetchParent parent,
JdbcValuesMetadata jdbcResultsMetadata,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
return null;
}
@Override
public NativeQuery.ReturnProperty addColumnAlias(String columnAlias) {
return null;
}
}

View File

@ -0,0 +1,18 @@
/*
* 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
*/
/**
* Support for defining result-set mappings used in {@link org.hibernate.query.NativeQuery}
* and {@link org.hibernate.procedure.ProcedureCall} / {@link javax.persistence.StoredProcedureQuery}.
* These result-set mappings are used to map the values in the JDBC {@link java.sql.ResultSet}
* into "domain results" (see {@link org.hibernate.sql.results.graph.DomainResult}).
*
* @see org.hibernate.query.results.ResultSetMapping
*
* @author Steve Ebersole
*/
package org.hibernate.query.results;

View File

@ -304,7 +304,7 @@ public class QueryEngine {
StringBuilder failingQueries = new StringBuilder( "Errors in named queries: " ); StringBuilder failingQueries = new StringBuilder( "Errors in named queries: " );
String sep = ""; String sep = "";
for ( Map.Entry<String, HibernateException> entry : errors.entrySet() ) { for ( Map.Entry<String, HibernateException> entry : errors.entrySet() ) {
QueryLogger.QUERY_LOGGER.namedQueryError( entry.getKey(), entry.getValue() ); QueryLogger.QUERY_MESSAGE_LOGGER.namedQueryError( entry.getKey(), entry.getValue() );
failingQueries.append( sep ).append( entry.getKey() ); failingQueries.append( sep ).append( entry.getKey() );
sep = ", "; sep = ", ";
} }

View File

@ -1,40 +0,0 @@
/*
* 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.spi;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* A builder for {@link DomainResult} instances related to native SQL query results.
*
* todo (6.0) : Perhaps this should be a builder for QueryResultProducer instances instead?
*
* @author Steve Ebersole
*/
public interface QueryResultBuilder {
// todo (6.0) : need to add the notion of "builders" that nest inside other builders.
// Nesting can happen as:
//
// For a scalar, it might represent:
// 1) a top-level QueryResult
// 2) a column within the attribute mapping for entity (or composite?)
// 3) an argument to a dynamic-instantiation
//
// For an attribute, it always represents a non-QueryResult. At least I
// think that is accurate - validate this, can an attribute be defined
// as a top-level QueryResult? dynamic-instantiation argument? - JPA at least does not support that
//
// For dynamic-instantiation, it might represent:
// 1) a top-level QueryResult
// 2) dynamic-instantiation argument (non-JPA)
JavaTypeDescriptor getResultType();
DomainResult buildReturn(DomainResultCreationState creationState);
}

View File

@ -1,31 +0,0 @@
/*
* 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.spi;
import org.hibernate.Incubating;
import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
/**
* Describes a ResultSet mapping applied to either a {@link org.hibernate.query.NativeQuery}
* or a {@link org.hibernate.procedure.ProcedureCall} / {@link javax.persistence.StoredProcedureQuery}.
*
* It is either generated from a {@link NamedResultSetMappingMemento} or
* on-the-fly via Hibernate's {@link org.hibernate.query.NativeQuery} contract. Acts
* as the {@link DomainResultProducer} for these uses
*
* @see org.hibernate.query.NativeQuery#addScalar
* @see org.hibernate.query.NativeQuery#addEntity
* @see org.hibernate.query.NativeQuery#addJoin
* @see org.hibernate.query.NativeQuery#addFetch
* @see org.hibernate.query.NativeQuery#addRoot
*
* @author Steve Ebersole
*/
@Incubating
public interface ResultSetMapping extends DomainResultProducer {
}

View File

@ -11,7 +11,6 @@ import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -38,9 +37,6 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.ScrollMode; import org.hibernate.ScrollMode;
import org.hibernate.engine.query.spi.NativeQueryInterpreter; import org.hibernate.engine.query.spi.NativeQueryInterpreter;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryConstructorReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.GraphSemantic;
@ -56,12 +52,13 @@ import org.hibernate.query.Query;
import org.hibernate.query.QueryParameter; import org.hibernate.query.QueryParameter;
import org.hibernate.query.ResultListTransformer; import org.hibernate.query.ResultListTransformer;
import org.hibernate.query.TupleTransformer; import org.hibernate.query.TupleTransformer;
import org.hibernate.query.internal.NativeQueryReturnBuilder;
import org.hibernate.query.internal.NativeQueryReturnBuilderFetchImpl;
import org.hibernate.query.internal.NativeQueryReturnBuilderRootImpl;
import org.hibernate.query.internal.ParameterMetadataImpl; import org.hibernate.query.internal.ParameterMetadataImpl;
import org.hibernate.query.internal.QueryOptionsImpl; import org.hibernate.query.internal.QueryOptionsImpl;
import org.hibernate.query.internal.QueryParameterBindingsImpl; import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.query.results.Builders;
import org.hibernate.query.results.EntityResultBuilder;
import org.hibernate.query.results.LegacyFetchBuilder;
import org.hibernate.query.results.ResultSetMappingImpl;
import org.hibernate.query.spi.AbstractQuery; import org.hibernate.query.spi.AbstractQuery;
import org.hibernate.query.spi.MutableQueryOptions; import org.hibernate.query.spi.MutableQueryOptions;
import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.NonSelectQueryPlan;
@ -84,6 +81,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
import org.hibernate.sql.results.spi.RowTransformer; import org.hibernate.sql.results.spi.RowTransformer;
import org.hibernate.transform.ResultTransformer; import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.BasicType;
import static org.hibernate.jpa.QueryHints.HINT_NATIVE_LOCKMODE; import static org.hibernate.jpa.QueryHints.HINT_NATIVE_LOCKMODE;
@ -104,11 +102,7 @@ public class NativeQueryImpl<R>
private Set<String> querySpaces; private Set<String> querySpaces;
private ResultSetMappingImpl resultSetMapping = new ResultSetMappingImpl();
private List<NativeSQLQueryReturn> queryReturns;
private List<NativeQueryReturnBuilder> queryReturnBuilders;
private boolean autoDiscoverTypes;
private Object collectionKey; private Object collectionKey;
private NativeQueryInterpreter nativeQueryInterpreter; private NativeQueryInterpreter nativeQueryInterpreter;
@ -141,7 +135,10 @@ public class NativeQueryImpl<R>
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
this( memento, session ); this( memento, session );
// todo (6.0) : validate `resultJavaType` against specified result-set mapping // todo (6.0) : need to add handling for `javax.persistence.NamedNativeQuery#resultSetMapping`
// and `javax.persistence.NamedNativeQuery#resultClass`
// todo (6.0) : relatedly, does `resultJavaType` come from `NamedNativeQuery#resultClass`?
} }
/** /**
@ -152,6 +149,11 @@ public class NativeQueryImpl<R>
String resultSetMappingName, String resultSetMappingName,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
this( memento, session ); this( memento, session );
// todo (6.0) : need to add handling for `javax.persistence.NamedNativeQuery#resultSetMapping`
// and `javax.persistence.NamedNativeQuery#resultClass`
// todo (6.0) : relatedly, does `resultSetMappingName` come from `NamedNativeQuery#resultSetMapping`?
} }
private ParameterInterpretation resolveParameterInterpretation(SharedSessionContractImplementor session) { private ParameterInterpretation resolveParameterInterpretation(SharedSessionContractImplementor session) {
@ -199,7 +201,6 @@ public class NativeQueryImpl<R>
this.sqlString = sqlString; this.sqlString = sqlString;
this.queryReturns = new ArrayList<>();
this.querySpaces = new HashSet<>(); this.querySpaces = new HashSet<>();
final ParameterInterpretation parameterInterpretation = resolveParameterInterpretation( session ); final ParameterInterpretation parameterInterpretation = resolveParameterInterpretation( session );
@ -332,44 +333,56 @@ public class NativeQueryImpl<R>
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private SelectQueryPlan<R> resolveSelectQueryPlan() { private SelectQueryPlan<R> resolveSelectQueryPlan() {
SelectQueryPlan<R> queryPlan = null;
final JdbcValuesMappingProducer resultSetMapping = getJdbcValuesMappingProducer();
final QueryInterpretationCache.Key cacheKey = generateSelectInterpretationsKey( resultSetMapping ); final QueryInterpretationCache.Key cacheKey = generateSelectInterpretationsKey( resultSetMapping );
if ( cacheKey != null ) { if ( cacheKey != null ) {
return getSession().getFactory().getQueryEngine().getInterpretationCache().resolveSelectQueryPlan( return getSession().getFactory().getQueryEngine().getInterpretationCache().resolveSelectQueryPlan(
cacheKey, cacheKey,
this::createQueryPlan () -> createQueryPlan( resultSetMapping )
); );
} }
else { else {
return createQueryPlan(); return createQueryPlan( resultSetMapping );
} }
} }
private NativeSelectQueryPlan<R> createQueryPlan() { private NativeSelectQueryPlan<R> createQueryPlan(JdbcValuesMappingProducer jdbcValuesMappingProducer) {
final RowTransformer rowTransformer = resolveRowTransformer(); final RowTransformer<?> rowTransformer = null;
return getSessionFactory().getQueryEngine().getNativeQueryInterpreter().createQueryPlan( final NativeSelectQueryDefinition queryDefinition = new NativeSelectQueryDefinition() {
generateSelectQueryDefinition(), @Override
getSessionFactory() public String getSqlString() {
); return NativeQueryImpl.this.getQueryString();
} }
private JdbcValuesMappingProducer getJdbcValuesMappingProducer() { @Override
// todo (6.0) - need to resolve SqlSelections as well as resolving ResultBuilders and FetchBuilders into QueryResult trees public boolean isCallable() {
// also need to account for the edge case where the user passed just the return false;
// query string and no mappings (see ResultSetMappingUndefinedImpl)
throw new NotYetImplementedFor6Exception( );
} }
private RowTransformer resolveRowTransformer() { @Override
// todo (6.0) - need to resolve the RowTransformer to use, if one. public List<QueryParameterImplementor<?>> getQueryParameterList() {
// todo (6.0) - what about ResultListTransformer? return NativeQueryImpl.this.occurrenceOrderedParamList;
throw new NotYetImplementedFor6Exception( ); }
@Override
public JdbcValuesMappingProducer getJdbcValuesMappingProducer() {
return jdbcValuesMappingProducer;
}
@Override
public RowTransformer getRowTransformer() {
return rowTransformer;
}
@Override
public Set<String> getAffectedTableNames() {
return querySpaces;
}
};
return getSessionFactory().getQueryEngine()
.getNativeQueryInterpreter()
.createQueryPlan( queryDefinition, getSessionFactory() );
} }
private SelectInterpretationsKey generateSelectInterpretationsKey(JdbcValuesMappingProducer resultSetMapping) { private SelectInterpretationsKey generateSelectInterpretationsKey(JdbcValuesMappingProducer resultSetMapping) {
@ -398,57 +411,6 @@ public class NativeQueryImpl<R>
return limit.getFirstRow() != null || limit.getMaxRows() != null; return limit.getFirstRow() != null || limit.getMaxRows() != null;
} }
private NativeSelectQueryDefinition<R> generateSelectQueryDefinition() {
return new NativeSelectQueryDefinition() {
@Override
public String getSqlString() {
return NativeQueryImpl.this.getQueryString();
}
@Override
public boolean isCallable() {
return false;
}
@Override
public List<QueryParameterImplementor<?>> getQueryParameterList() {
return NativeQueryImpl.this.occurrenceOrderedParamList;
}
@Override
public JdbcValuesMappingProducer getJdbcValuesMappingProducer() {
return NativeQueryImpl.this.getJdbcValuesMappingProducer();
}
@Override
public RowTransformer getRowTransformer() {
return NativeQueryImpl.this.resolveRowTransformer();
}
@Override
public Set<String> getAffectedTableNames() {
return querySpaces;
}
};
}
private void prepareQueryReturnsIfNecessary() {
if ( queryReturnBuilders != null ) {
if ( !queryReturnBuilders.isEmpty() ) {
if ( queryReturns != null ) {
queryReturns.clear();
queryReturns = null;
}
queryReturns = new ArrayList<>();
for ( NativeQueryReturnBuilder builder : queryReturnBuilders ) {
queryReturns.add( builder.buildReturn() );
}
queryReturnBuilders.clear();
}
queryReturnBuilders = null;
}
}
@Override @Override
protected ScrollableResultsImplementor doScroll(ScrollMode scrollMode) { protected ScrollableResultsImplementor doScroll(ScrollMode scrollMode) {
return resolveSelectQueryPlan().performScroll( scrollMode, this ); return resolveSelectQueryPlan().performScroll( scrollMode, this );
@ -456,27 +418,6 @@ public class NativeQueryImpl<R>
@Override @Override
protected void beforeQuery(boolean txnRequired) { protected void beforeQuery(boolean txnRequired) {
prepareQueryReturnsIfNecessary();
boolean noReturns = queryReturns == null || queryReturns.isEmpty();
if ( noReturns ) {
this.autoDiscoverTypes = true;
}
else {
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
if ( queryReturn instanceof NativeSQLQueryScalarReturn ) {
NativeSQLQueryScalarReturn scalar = (NativeSQLQueryScalarReturn) queryReturn;
if ( scalar.getType() == null ) {
autoDiscoverTypes = true;
break;
}
}
else if ( NativeSQLQueryConstructorReturn.class.isInstance( queryReturn ) ) {
autoDiscoverTypes = true;
break;
}
}
}
super.beforeQuery( txnRequired ); super.beforeQuery( txnRequired );
if ( getSynchronizedQuerySpaces() != null && !getSynchronizedQuerySpaces().isEmpty() ) { if ( getSynchronizedQuerySpaces() != null && !getSynchronizedQuerySpaces().isEmpty() ) {
@ -537,36 +478,24 @@ public class NativeQueryImpl<R>
return addScalar( columnAlias, null ); return addScalar( columnAlias, null );
} }
// @Override
// public NativeQueryImplementor<R> addScalar(String columnAlias, BasicDomainType type) {
// return null;
// }
@Override @Override
public NativeQueryImplementor<R> addScalar(String columnAlias, BasicDomainType type) { public NativeQueryImplementor<R> addScalar(String columnAlias, BasicDomainType type) {
addReturnBuilder( resultSetMapping.addResultBuilder( Builders.scalar( columnAlias, (BasicType<?>) type ) );
() -> new NativeSQLQueryScalarReturn( columnAlias, type )
);
return this; return this;
} }
protected void addReturnBuilder(NativeQueryReturnBuilder builder) { @Override
if ( queryReturnBuilders == null ) { public EntityResultBuilder addRoot(String tableAlias, String entityName) {
queryReturnBuilders = new ArrayList<>(); final EntityResultBuilder resultBuilder = Builders.entity(
} tableAlias,
entityName
queryReturnBuilders.add( builder ); );
resultSetMapping.addResultBuilder( resultBuilder );
return resultBuilder;
} }
@Override @Override
public RootReturn addRoot(String tableAlias, String entityName) { public EntityResultBuilder addRoot(String tableAlias, Class entityType) {
NativeQueryReturnBuilderRootImpl builder = new NativeQueryReturnBuilderRootImpl( tableAlias, entityName );
addReturnBuilder( builder );
return builder;
}
@Override
public RootReturn addRoot(String tableAlias, Class entityType) {
return addRoot( tableAlias, entityType.getName() ); return addRoot( tableAlias, entityType.getName() );
} }
@ -604,9 +533,9 @@ public class NativeQueryImpl<R>
@Override @Override
public FetchReturn addFetch(String tableAlias, String ownerTableAlias, String joinPropertyName) { public FetchReturn addFetch(String tableAlias, String ownerTableAlias, String joinPropertyName) {
NativeQueryReturnBuilderFetchImpl builder = new NativeQueryReturnBuilderFetchImpl( tableAlias, ownerTableAlias, joinPropertyName ); final LegacyFetchBuilder fetchBuilder = Builders.fetch( tableAlias, ownerTableAlias, joinPropertyName );
addReturnBuilder( builder ); resultSetMapping.addLegacyFetchBuilder( fetchBuilder );
return builder; return fetchBuilder;
} }
@Override @Override

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.query.sql.internal; package org.hibernate.query.sql.internal;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -14,7 +15,13 @@ import org.hibernate.ScrollMode;
import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.sql.spi.NativeSelectQueryPlan; import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl;
import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.ExecutionContext;
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.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
import org.hibernate.sql.results.spi.RowTransformer; import org.hibernate.sql.results.spi.RowTransformer;
@ -40,28 +47,53 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
this.affectedTableNames = affectedTableNames; this.affectedTableNames = affectedTableNames;
this.parameterList = parameterList; this.parameterList = parameterList;
this.resultSetMapping = resultSetMapping; this.resultSetMapping = resultSetMapping;
this.rowTransformer = rowTransformer; this.rowTransformer = rowTransformer != null
? rowTransformer
: RowTransformerPassThruImpl.instance();
} }
@Override @Override
public List<R> performList(ExecutionContext executionContext) { public List<R> performList(ExecutionContext executionContext) {
throw new NotYetImplementedFor6Exception( getClass() ); final List<JdbcParameterBinder> jdbcParameterBinders = resolveJdbcParamBinders( executionContext );
final JdbcParameterBindings jdbcParameterBindings = resolveJdbcParamBindings( executionContext, jdbcParameterBinders );
// final List<JdbcParameterBinder> jdbcParameterBinders = resolveJdbcParameterBinders( executionContext ); final JdbcSelect jdbcSelect = new JdbcSelect(
// sql,
// final JdbcSelect jdbcSelect = new JdbcSelectImpl( jdbcParameterBinders,
// sql, resultSetMapping,
// jdbcParameterBinders, affectedTableNames,
// resultSetMapping, Collections.emptySet()
// affectedTableNames );
// );
// final JdbcSelectExecutor executor = JdbcSelectExecutorStandardImpl.INSTANCE;
// // todo (6.0) : need to make this swappable (see note in executor class)
// final JdbcSelectExecutor executor = JdbcSelectExecutorStandardImpl.INSTANCE; return executor.list(
// jdbcSelect,
// return executor.list( jdbcSelect, JdbcParameterBindings.NO_BINDINGS, executionContext, rowTransformer ); jdbcParameterBindings,
executionContext,
rowTransformer,
false
);
} }
//
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) { // private List<JdbcParameterBinder> resolveJdbcParameterBinders(ExecutionContext executionContext) {
// final List<JdbcParameterBinder> jdbcParameterBinders = CollectionHelper.arrayList( parameterList.size() ); // final List<JdbcParameterBinder> jdbcParameterBinders = CollectionHelper.arrayList( parameterList.size() );
// //
@ -98,27 +130,25 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
@Override @Override
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext) { public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext) {
throw new NotYetImplementedFor6Exception( getClass() ); final List<JdbcParameterBinder> jdbcParameterBinders = resolveJdbcParamBinders( executionContext );
// final JdbcParameterBindings jdbcParameterBindings = resolveJdbcParamBindings( executionContext, jdbcParameterBinders );
// // todo (6.0) : see notes above in `#performList`
// final JdbcSelect jdbcSelect = new JdbcSelect(
// final List<JdbcParameterBinder> jdbcParameterBinders = resolveJdbcParameterBinders( executionContext ); sql,
// jdbcParameterBinders,
// final JdbcSelect jdbcSelect = new JdbcSelectImpl( resultSetMapping,
// sql, affectedTableNames,
// jdbcParameterBinders, Collections.emptySet()
// resultSetMapping, );
// affectedTableNames
// ); final JdbcSelectExecutor executor = JdbcSelectExecutorStandardImpl.INSTANCE;
// final JdbcSelectExecutor executor = JdbcSelectExecutorStandardImpl.INSTANCE;
// return executor.scroll(
// return executor.scroll( jdbcSelect,
// jdbcSelect, scrollMode,
// scrollMode, jdbcParameterBindings,
// // the binders created here encapsulate their bind value executionContext,
// JdbcParameterBindings.NO_BINDINGS, rowTransformer
// executionContext, );
// rowTransformer
// );
} }
} }

View File

@ -32,6 +32,7 @@ import org.hibernate.query.QueryParameter;
import org.hibernate.query.ResultListTransformer; import org.hibernate.query.ResultListTransformer;
import org.hibernate.query.TupleTransformer; import org.hibernate.query.TupleTransformer;
import org.hibernate.query.named.NameableQuery; import org.hibernate.query.named.NameableQuery;
import org.hibernate.query.results.EntityResultBuilder;
import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryImplementor;
/** /**
@ -55,7 +56,7 @@ public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQu
NativeQueryImplementor<R> addScalar(String columnAlias, BasicDomainType type); NativeQueryImplementor<R> addScalar(String columnAlias, BasicDomainType type);
@Override @Override
RootReturn addRoot(String tableAlias, String entityName); EntityResultBuilder addRoot(String tableAlias, String entityName);
@Override @Override
NativeQueryImplementor<R> addEntity(String entityName); NativeQueryImplementor<R> addEntity(String entityName);

View File

@ -707,7 +707,7 @@ public abstract class BaseSqmToSqlAstConverter
// add any additional join restrictions // add any additional join restrictions
if ( sqmJoin.getJoinPredicate() != null ) { if ( sqmJoin.getJoinPredicate() != null ) {
if ( sqmJoin.isFetched() ) { if ( sqmJoin.isFetched() ) {
QueryLogger.QUERY_LOGGER.debugf( "Join fetch [" + sqmJoin.getNavigablePath() + "] is restricted" ); QueryLogger.QUERY_MESSAGE_LOGGER.debugf( "Join fetch [" + sqmJoin.getNavigablePath() + "] is restricted" );
} }
if ( joinedTableGroupJoin == null ) { if ( joinedTableGroupJoin == null ) {

View File

@ -6,8 +6,9 @@
*/ */
package org.hibernate.sql.results.jdbc.spi; package org.hibernate.sql.results.jdbc.spi;
import org.hibernate.Incubating;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.ResultSetMapping; import org.hibernate.query.results.ResultSetMapping;
/** /**
* Producer for JdbcValuesMapping references. * Producer for JdbcValuesMapping references.
@ -20,13 +21,12 @@ import org.hibernate.query.spi.ResultSetMapping;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Incubating
public interface JdbcValuesMappingProducer { public interface JdbcValuesMappingProducer {
/** /**
* Resolve the selections (both at the JDBC and object level) for this * Resolve the JdbcValuesMapping. This involves resolving the
* mapping. Acts as delayed access to this resolution process to support * {@link org.hibernate.sql.results.graph.DomainResult} and
* "auto discovery" as needed for "undefined scalar" results as defined by * {@link org.hibernate.sql.results.graph.Fetch}
* native-sql and procedure call queries.
*/ */
JdbcValuesMapping resolve(JdbcValuesMetadata jdbcResultsMetadata, SessionFactoryImplementor sessionFactory); JdbcValuesMapping resolve(JdbcValuesMetadata jdbcResultsMetadata, SessionFactoryImplementor sessionFactory);
} }

View File

@ -17,10 +17,12 @@ import javax.persistence.Id;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import org.hibernate.graph.GraphParser;
import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.GraphSemantic;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -81,6 +83,7 @@ public class EntityGraphNativeQueryTest {
} }
@Test @Test
@FailureExpected( reason = "Uses an implicit entity/root return, which is not yet implemented" )
void testNativeQueryLoadGraph(SessionFactoryScope scope) { void testNativeQueryLoadGraph(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
@ -98,14 +101,21 @@ public class EntityGraphNativeQueryTest {
.setHint( GraphSemantic.LOAD.getJpaHintName(), fooGraph ) .setHint( GraphSemantic.LOAD.getJpaHintName(), fooGraph )
.getSingleResult(); .getSingleResult();
fail("Should throw exception"); fail("Should throw exception");
} catch (Exception e) { }
assertThat( e.getMessage(), is( "A native SQL query cannot use EntityGraphs" ) ); catch (Exception e) {
if ( e.getMessage().equals( "A native SQL query cannot use EntityGraphs" ) ) {
// success
}
else {
throw new RuntimeException( "Unexpected exception", e );
}
} }
} }
); );
} }
@Test @Test
@FailureExpected( reason = "Uses an implicit entity/root return, which is not yet implemented" )
void testNativeQueryFetchGraph(SessionFactoryScope scope) { void testNativeQueryFetchGraph(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
@ -123,8 +133,14 @@ public class EntityGraphNativeQueryTest {
.setHint( GraphSemantic.FETCH.getJpaHintName(), fooGraph ) .setHint( GraphSemantic.FETCH.getJpaHintName(), fooGraph )
.getSingleResult(); .getSingleResult();
fail( "Should throw exception" ); fail( "Should throw exception" );
} catch (Exception e) { }
assertThat( e.getMessage(), is( "A native SQL query cannot use EntityGraphs" ) ); catch (Exception e) {
if ( e.getMessage().equals( "A native SQL query cannot use EntityGraphs" ) ) {
// success
}
else {
throw new RuntimeException( "Unexpected exception", e );
}
} }
} }
); );

View File

@ -8,6 +8,8 @@ package org.hibernate.orm.test.query.named.resultmapping;
import org.hibernate.query.named.NamedQueryRepository; import org.hibernate.query.named.NamedQueryRepository;
import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.query.results.ResultSetMapping;
import org.hibernate.query.results.ResultSetMappingImpl;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
@ -16,7 +18,7 @@ import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
/** /**
@ -31,8 +33,10 @@ public class SimpleNamedMappingTests {
final QueryEngine queryEngine = sessionFactoryScope.getSessionFactory().getQueryEngine(); final QueryEngine queryEngine = sessionFactoryScope.getSessionFactory().getQueryEngine();
final NamedQueryRepository namedQueryRepository = queryEngine.getNamedQueryRepository(); final NamedQueryRepository namedQueryRepository = queryEngine.getNamedQueryRepository();
final NamedResultSetMappingMemento mappingMemento = namedQueryRepository.getResultSetMappingMemento( "name" ); final NamedResultSetMappingMemento mappingMemento = namedQueryRepository.getResultSetMappingMemento( "name" );
assertThat( mappingMemento.toResultSetMapping(), notNullValue() );
}
// todo (6.0) : atm that ^^ is as far as we can test until we implement native-query support to test applying a mapping final ResultSetMapping mapping = new ResultSetMappingImpl();
mappingMemento.resolve( mapping, querySpace -> {}, sessionFactoryScope.getSessionFactory() );
assertThat( mapping.getNumberOfResultBuilders(), is( 1 ) );
}
} }

View File

@ -0,0 +1,141 @@
/*
* 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.LocalDate;
import java.util.List;
import javax.persistence.Entity;
import org.hibernate.query.sql.spi.NativeQueryImplementor;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.contacts.Contact;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.hamcrest.CoreMatchers;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author Steve Ebersole
*/
@DomainModel(
standardModels = StandardDomainModel.CONTACTS
)
@SessionFactory
public class NativeQueryScalarTests {
@Test
public void fullyImplicitTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final String sql = "select gender, first, last, id from contacts";
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( Object[].class ) );
final Object[] values = (Object[]) result;
assertThat( values.length, is(4 ) );
assertThat( ( (Number) values[0] ).intValue(), is( Contact.Gender.OTHER.ordinal() ) );
assertThat( values[1], is( "My First" ) );
assertThat( values[2], is( "Contact" ) );
assertThat( values[3], is( 1 ) );
}
);
}
@Test
public void explicitOrderTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final String sql = "select gender, first, last, id from contacts";
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
// notice the reverse order from the select clause
query.addScalar( "id" );
query.addScalar( "last" );
query.addScalar( "first" );
query.addScalar( "gender" );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( Object[].class ) );
final Object[] values = (Object[]) result;
assertThat( values.length, is(4 ) );
assertThat( values[0], is( 1 ) );
assertThat( values[1], is( "Contact" ) );
assertThat( values[2], is( "My First" ) );
assertThat( ( (Number) values[3] ).intValue(), is( Contact.Gender.OTHER.ordinal() ) );
}
);
}
@Test
@FailureExpected( reason = "Explicit type support not working atm" )
public void explicitEnumTypeTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final String sql = "select gender, first, last, id from contacts";
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
// notice the reverse order from the select clause
query.addScalar( "id" );
query.addScalar( "last" );
query.addScalar( "first" );
query.addScalar( "gender", scope.getSessionFactory().getTypeConfiguration().getBasicTypeForJavaType( Contact.Gender.class ) );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( Object[].class ) );
final Object[] values = (Object[]) result;
assertThat( values.length, is(4 ) );
assertThat( values[0], is( 1 ) );
assertThat( values[1], is( "Contact" ) );
assertThat( values[2], is( "My First" ) );
assertThat( values[3], is( Contact.Gender.OTHER ) );
}
);
}
@BeforeEach
public void prepareData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist(
new Contact(
1,
new Contact.Name( "My First", "Contact"),
Contact.Gender.OTHER,
LocalDate.EPOCH
)
);
}
);
}
@AfterEach
public void cleanUpData(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.createQuery( "delete Contact" ).executeUpdate()
);
}
}

View File

@ -0,0 +1,11 @@
/*
* 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
*/
/**
* Tests for {@link org.hibernate.query.NativeQuery}
*/
package org.hibernate.orm.test.query.sql;

View File

@ -10,13 +10,17 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.ColumnResult;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.Table; import javax.persistence.Table;
import org.hibernate.query.NativeQuery;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test; import org.junit.Test;
@ -117,8 +121,43 @@ public class SetElementNullBasicTest extends BaseCoreFunctionalTestCase {
} }
); );
} }
//
// @SqlResultSetMapping(
// columns = {
// @ColumnResult( name = "a_id", type = long.class ),
// @ColumnResult( name = "a_name" )
// }
// )
private List<?> getCollectionElementRows(int id) { private List<?> getCollectionElementRows(int id) {
doInHibernate(
this::sessionFactory,
session -> {
final String qry = "SELECT a.id as a_id, a.name as a_name FROM AnEntity a where a.id = " + id;
final NativeQuery nativeQuery = session.createNativeQuery( qry );
nativeQuery.list();
nativeQuery.addRoot( "a", AnEntity.class )
.addIdColumnAliases( "a_id" )
.addProperty( "name", "a_name" );
nativeQuery.addFetch( "c", "a", "aCollection" );
}
);
doInHibernate(
this::sessionFactory,
session -> {
final String qry = "SELECT a.id as a_id, a.name as a_name, c.aCollection FROM AnEntity a join AnEntity_aCollection c on a.id = c.id and a.id = " + id;
final NativeQuery nativeQuery = session.createNativeQuery( qry );
nativeQuery.addRoot( "a", AnEntity.class )
.addIdColumnAliases( "a_id" )
.addProperty( "name", "a_name" );
nativeQuery.addFetch( "c", "a", "aCollection" );
nativeQuery.list();
}
);
return doInHibernate( return doInHibernate(
this::sessionFactory, session -> { this::sessionFactory, session -> {
return session.createNativeQuery( return session.createNativeQuery(

View File

@ -16,6 +16,7 @@ import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.OrderColumn; import javax.persistence.OrderColumn;
import javax.persistence.SecondaryTable; import javax.persistence.SecondaryTable;
import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
@ -23,6 +24,7 @@ import javax.persistence.TemporalType;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Entity @Entity
@Table( name = "contacts" )
@SecondaryTable( name="contact_supp" ) @SecondaryTable( name="contact_supp" )
public class Contact { public class Contact {
private Integer id; private Integer id;

View File

@ -41,6 +41,15 @@ public class SimpleEntity {
this.someString = someString; this.someString = someString;
} }
public SimpleEntity(
Integer id,
String someString,
Long someLong) {
this.id = id;
this.someString = someString;
this.someLong = someLong;
}
public SimpleEntity( public SimpleEntity(
Integer id, Integer id,
Date someDate, Date someDate,