Fix some stored procedure and native query issues

This commit is contained in:
Christian Beikov 2022-02-16 14:13:50 +01:00
parent 8ed1ed5159
commit 439788198f
37 changed files with 609 additions and 213 deletions

View File

@ -7,13 +7,15 @@ import jakarta.persistence.Entity;
import jakarta.persistence.EntityResult; import jakarta.persistence.EntityResult;
import jakarta.persistence.FieldResult; import jakarta.persistence.FieldResult;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.NamedStoredProcedureQuery;
import jakarta.persistence.QueryHint;
import jakarta.persistence.SqlResultSetMapping; import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.SqlResultSetMappings; import jakarta.persistence.SqlResultSetMappings;
import jakarta.persistence.StoredProcedureParameter;
import org.hibernate.annotations.NamedNativeQuery;
import org.hibernate.c3p0.internal.C3P0ConnectionProvider; import org.hibernate.c3p0.internal.C3P0ConnectionProvider;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.dialect.OracleDialect;
import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
@ -27,7 +29,7 @@ import static org.junit.Assert.assertEquals;
/** /**
* @author Vlad Mihalcea * @author Vlad Mihalcea
*/ */
@RequiresDialect(Oracle8iDialect.class) @RequiresDialect(OracleDialect.class)
@TestForIssue( jiraKey = "HHH-10256" ) @TestForIssue( jiraKey = "HHH-10256" )
public class OracleSQLCallableStatementProxyTest extends public class OracleSQLCallableStatementProxyTest extends
BaseCoreFunctionalTestCase { BaseCoreFunctionalTestCase {
@ -95,19 +97,19 @@ public class OracleSQLCallableStatementProxyTest extends
public void testStoredProcedureOutParameter() { public void testStoredProcedureOutParameter() {
doInHibernate( this::sessionFactory, session -> { doInHibernate( this::sessionFactory, session -> {
List<Object[]> persons = session List<Object[]> persons = session
.createNamedQuery( .createNamedStoredProcedureQuery( "getPerson" )
"getPerson")
.setParameter(1, 1L) .setParameter(1, 1L)
.getResultList(); .getResultList();
assertEquals(1, persons.size()); assertEquals(1, persons.size());
} ); } );
} }
@NamedNativeQuery( @NamedStoredProcedureQuery(
name = "getPerson", name = "getPerson",
query = "{ ? = call fn_person( ? ) }", procedureName = "fn_person",
callable = true, resultSetMappings = "person",
resultSetMapping = "person" hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true"),
parameters = @StoredProcedureParameter(type = Long.class)
) )
@SqlResultSetMappings({ @SqlResultSetMappings({
@SqlResultSetMapping( @SqlResultSetMapping(

View File

@ -153,7 +153,7 @@ public class Identifier implements Comparable<Identifier> {
public static String unQuote(String name) { public static String unQuote(String name) {
assert isQuoted( name ); assert isQuoted( name );
return name.substring( 1, name.length() - 2 ); return name.substring( 1, name.length() - 1 );
} }
/** /**

View File

@ -288,9 +288,6 @@ public class MetadataBuildingProcess {
processor.postProcessEntityHierarchies(); processor.postProcessEntityHierarchies();
processor.processResultSetMappings(); processor.processResultSetMappings();
processor.processNamedQueries();
processor.finishUp();
for ( MetadataContributor contributor : classLoaderService.loadJavaServices( MetadataContributor.class ) ) { for ( MetadataContributor contributor : classLoaderService.loadJavaServices( MetadataContributor.class ) ) {
log.tracef( "Calling MetadataContributor : %s", contributor ); log.tracef( "Calling MetadataContributor : %s", contributor );
@ -299,6 +296,11 @@ public class MetadataBuildingProcess {
metadataCollector.processSecondPasses( rootMetadataBuildingContext ); metadataCollector.processSecondPasses( rootMetadataBuildingContext );
// Make sure collections are fully bound before processing named queries as hbm result set mappings require it
processor.processNamedQueries();
processor.finishUp();
if ( options.isXmlMappingEnabled() ) { if ( options.isXmlMappingEnabled() ) {
final Iterable<AdditionalJaxbMappingProducer> producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class ); final Iterable<AdditionalJaxbMappingProducer> producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class );
if ( producers != null ) { if ( producers != null ) {

View File

@ -13,6 +13,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.LockMode; import org.hibernate.LockMode;
@ -27,11 +28,23 @@ import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmResultSetMappingType;
import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.internal.FetchMementoEmbeddableStandard;
import org.hibernate.query.internal.FetchMementoEntityStandard;
import org.hibernate.query.spi.NavigablePath; import org.hibernate.query.spi.NavigablePath;
import org.hibernate.query.internal.FetchMementoBasicStandard; import org.hibernate.query.internal.FetchMementoBasicStandard;
import org.hibernate.query.internal.FetchMementoHbmStandard; import org.hibernate.query.internal.FetchMementoHbmStandard;
@ -47,6 +60,7 @@ import org.hibernate.query.named.NamedResultSetMappingMemento;
import org.hibernate.query.named.ResultMemento; import org.hibernate.query.named.ResultMemento;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer; import org.hibernate.sql.results.graph.FetchableContainer;
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
/** /**
@ -498,6 +512,21 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
this.propertyPathParts = propertyPath.split( "\\." ); this.propertyPathParts = propertyPath.split( "\\." );
this.columnAliases = extractColumnAliases( hbmPropertyMapping, context ); this.columnAliases = extractColumnAliases( hbmPropertyMapping, context );
if ( columnAliases.size() > 1 ) {
// We have to reorder the columns according to the property reordering
final Value value = getValue( parent, propertyPath, context );
assert value instanceof Component;
final Component component = (Component) value;
int[] originalPropertyOrder = component.sortProperties();
if ( originalPropertyOrder != null ) {
final String[] originalColumns = columnAliases.toArray( new String[0] );
for ( int i = 0; i < originalPropertyOrder.length; i++ ) {
final int originalIndex = originalPropertyOrder[i];
columnAliases.set( i, originalColumns[originalIndex] );
}
}
}
BootQueryLogging.LOGGER.debugf( BootQueryLogging.LOGGER.debugf(
"Creating PropertyFetchDescriptor (%s : %s) for ResultSet mapping - %s", "Creating PropertyFetchDescriptor (%s : %s) for ResultSet mapping - %s",
parent, parent,
@ -506,11 +535,100 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
); );
} }
private static Value getValue(HbmFetchParent parent, String propertyPath, MetadataBuildingContext context) {
if ( parent instanceof EntityResultDescriptor ) {
final PersistentClass entityBinding = context.getMetadataCollector()
.getEntityBinding( ( (EntityResultDescriptor) parent ).entityName );
Value value = null;
StringTokenizer st = new StringTokenizer( propertyPath, ".", false );
try {
while ( st.hasMoreElements() ) {
final String element = (String) st.nextElement();
if ( value == null ) {
Property identifierProperty = entityBinding.getIdentifierProperty();
if ( identifierProperty != null && identifierProperty.getName().equals( element ) ) {
// we have a mapped identifier property and the root of
// the incoming property path matched that identifier
// property
value = identifierProperty.getValue();
}
else if ( identifierProperty == null && entityBinding.getIdentifierMapper() != null ) {
// we have an embedded composite identifier
try {
identifierProperty = entityBinding.getIdentifierMapper().getProperty( element );
// the root of the incoming property path matched one
// of the embedded composite identifier properties
value = identifierProperty.getValue();
}
catch (MappingException ignore) {
// ignore it...
}
}
if ( value == null ) {
value = entityBinding.getProperty( element ).getValue();
}
}
else if ( value instanceof Component ) {
value = ( (Component) value ).getProperty( element ).getValue();
}
else if ( value instanceof ToOne ) {
value = context.getMetadataCollector()
.getEntityBinding( ( (ToOne) value ).getReferencedEntityName() )
.getProperty( element )
.getValue();
}
else if ( value instanceof OneToMany ) {
value = ( (OneToMany) value ).getAssociatedClass().getProperty( element ).getValue();
}
else {
final Collection collection = (Collection) value;
switch ( element ) {
case "key":
value = collection.getKey();
break;
case "element":
value = collection.getElement();
break;
case "index":
if ( collection instanceof IndexedCollection ) {
value = ( (IndexedCollection) collection ).getIndex();
break;
}
default:
throw new MappingException( "property [" + element + "] not found on collection [" + collection.getRole() + "]" );
}
}
}
return value;
}
catch (MappingException e) {
throw new MappingException( "property [" + propertyPath + "] not found on entity [" + entityBinding.getEntityName() + "]" );
}
}
else if ( parent instanceof CollectionResultDescriptor ) {
final Collection collectionBinding = context.getMetadataCollector()
.getCollectionBinding( ( (CollectionResultDescriptor) parent ).collectionPath.getFullPath() );
return collectionBinding.getElement();
}
else {
assert parent instanceof JoinDescriptor;
final JoinDescriptor joinDescriptor = (JoinDescriptor) parent;
final HbmFetchParent joinParent = joinDescriptor.fetchParentByAliasAccess.get()
.get( joinDescriptor.ownerTableAlias );
return getValue( joinParent, joinDescriptor.propertyPath + "." + propertyPath, context );
}
}
@Override @Override
public String getFetchablePath() { public String getFetchablePath() {
return propertyPath; return propertyPath;
} }
public List<String> getColumnAliases() {
return columnAliases;
}
private static List<String> extractColumnAliases( private static List<String> extractColumnAliases(
JaxbHbmNativeQueryPropertyReturnType hbmPropertyMapping, JaxbHbmNativeQueryPropertyReturnType hbmPropertyMapping,
MetadataBuildingContext context) { MetadataBuildingContext context) {
@ -520,7 +638,7 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
final List<String> columnAliases = new ArrayList<>( hbmPropertyMapping.getReturnColumn().size() ); final List<String> columnAliases = new ArrayList<>( hbmPropertyMapping.getReturnColumn().size() );
hbmPropertyMapping.getReturnColumn().forEach( hbmPropertyMapping.getReturnColumn().forEach(
(column) -> columnAliases.add( column.getName() ) column -> columnAliases.add( column.getName() )
); );
return columnAliases; return columnAliases;
} }
@ -552,7 +670,28 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
fetchable = (Fetchable) ( (FetchableContainer) fetchable ).findSubPart( propertyPathParts[i], null ); fetchable = (Fetchable) ( (FetchableContainer) fetchable ).findSubPart( propertyPathParts[i], null );
} }
return new FetchMementoBasicStandard( navigablePath, (BasicValuedModelPart) fetchable, columnAliases.get( 0 ) ); if ( fetchable instanceof BasicValuedModelPart ) {
return new FetchMementoBasicStandard(
navigablePath,
(BasicValuedModelPart) fetchable,
columnAliases.get( 0 )
);
}
else if ( fetchable instanceof EntityValuedFetchable ) {
return new FetchMementoEntityStandard(
navigablePath,
(EntityValuedFetchable) fetchable,
columnAliases
);
}
else {
assert fetchable instanceof EmbeddableValuedModelPart;
return new FetchMementoEmbeddableStandard(
navigablePath,
(EmbeddableValuedModelPart) fetchable,
columnAliases
);
}
} }
@Override @Override
@ -632,16 +771,26 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
applyFetchJoins( joinDescriptorsAccess, tableAlias, propertyFetchDescriptors ); applyFetchJoins( joinDescriptorsAccess, tableAlias, propertyFetchDescriptors );
final Map<String, FetchMemento> fetchDescriptorMap = new HashMap<>(); final Map<String, FetchMemento> fetchDescriptorMap = new HashMap<>();
final List<String> keyColumnNames = new ArrayList<>();
final boolean isPlural = thisAsParentMemento.getFetchableContainer() instanceof PluralAttributeMapping;
propertyFetchDescriptors.forEach( propertyFetchDescriptors.forEach(
hbmFetchDescriptor -> fetchDescriptorMap.put( hbmFetchDescriptor -> {
hbmFetchDescriptor.getFetchablePath(), if ( isPlural && "key".equals( hbmFetchDescriptor.getFetchablePath() ) ) {
hbmFetchDescriptor.resolve( resolutionContext ) keyColumnNames.addAll( ( (PropertyFetchDescriptor) hbmFetchDescriptor ).getColumnAliases() );
) }
else {
fetchDescriptorMap.put(
hbmFetchDescriptor.getFetchablePath(),
hbmFetchDescriptor.resolve( resolutionContext )
);
}
}
); );
memento = new FetchMementoHbmStandard( memento = new FetchMementoHbmStandard(
thisAsParentMemento.getNavigablePath(), thisAsParentMemento.getNavigablePath(),
ownerTableAlias, ownerTableAlias,
tableAlias, tableAlias,
keyColumnNames,
lockMode, lockMode,
thisAsParentMemento, thisAsParentMemento,
fetchDescriptorMap, fetchDescriptorMap,
@ -665,7 +814,14 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
final FetchParentMemento ownerMemento = hbmFetchParent.resolveParentMemento( resolutionContext ); final FetchParentMemento ownerMemento = hbmFetchParent.resolveParentMemento( resolutionContext );
final String[] parts = propertyPath.split( "\\." ); final String[] parts = propertyPath.split( "\\." );
NavigablePath navigablePath = ownerMemento.getNavigablePath().append( parts[ 0 ] ); NavigablePath navigablePath;
if ( ownerMemento.getFetchableContainer() instanceof PluralAttributeMapping ) {
navigablePath = ownerMemento.getNavigablePath().append( CollectionPart.Nature.ELEMENT.getName() );
}
else {
navigablePath = ownerMemento.getNavigablePath();
}
navigablePath = navigablePath.append( parts[ 0 ] );
FetchableContainer fetchable = (FetchableContainer) ownerMemento.getFetchableContainer().findSubPart( parts[ 0 ], null ); FetchableContainer fetchable = (FetchableContainer) ownerMemento.getFetchableContainer().findSubPart( parts[ 0 ], null );
for ( int i = 1; i < parts.length; i++ ) { for ( int i = 1; i < parts.length; i++ ) {

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.boot.query; package org.hibernate.boot.query;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -76,10 +77,26 @@ public class NamedNativeQueryDefinitionBuilder extends AbstractNamedQueryBuilder
return this; return this;
} }
public String getSqlString() {
return sqlString;
}
public Set<String> getQuerySpaces() { public Set<String> getQuerySpaces() {
return querySpaces; return querySpaces;
} }
public Map<String, String> getParameterTypes() {
return parameterTypes == null ? Collections.emptyMap() : parameterTypes;
}
public String getResultSetMappingName() {
return resultSetMappingName;
}
public String getResultSetMappingClassName() {
return resultSetMappingClassName;
}
public NamedNativeQueryDefinitionBuilder addSynchronizedQuerySpaces(Set<String> querySpaces) { public NamedNativeQueryDefinitionBuilder addSynchronizedQuerySpaces(Set<String> querySpaces) {
if ( querySpaces == null || querySpaces.isEmpty() ) { if ( querySpaces == null || querySpaces.isEmpty() ) {
return this; return this;

View File

@ -1271,6 +1271,12 @@ public class OracleDialect extends Dialect {
return 1; return 1;
} }
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
// Not sure if it's a JDBC driver issue, but it doesn't work
return false;
}
@Override @Override
public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException { public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException {
return (ResultSet) statement.getObject( name ); return (ResultSet) statement.getObject( name );

View File

@ -884,7 +884,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
return exporter; return exporter;
} }
private class SqlServerSequenceExporter extends StandardSequenceExporter { private static class SqlServerSequenceExporter extends StandardSequenceExporter {
public SqlServerSequenceExporter(Dialect dialect) { public SqlServerSequenceExporter(Dialect dialect) {
super( dialect ); super( dialect );
@ -900,6 +900,12 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
} }
} }
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
// Not sure if it's a JDBC driver issue, but it doesn't work
return false;
}
@Override @Override
public String generatedAs(String generatedAs) { public String generatedAs(String generatedAs) {
return " as (" + generatedAs + ") persisted"; return " as (" + generatedAs + ") persisted";

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.loader.ast.internal; package org.hibernate.loader.ast.internal;
import org.hibernate.FlushMode;
import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.loader.ast.spi.CollectionLoader; import org.hibernate.loader.ast.spi.CollectionLoader;
@ -14,6 +15,8 @@ import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.named.NamedQueryMemento; import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryImplementor;
import jakarta.persistence.Parameter;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -34,7 +37,9 @@ public class CollectionLoaderNamedQuery implements CollectionLoader {
@Override @Override
public PersistentCollection<?> load(Object key, SharedSessionContractImplementor session) { public PersistentCollection<?> load(Object key, SharedSessionContractImplementor session) {
final QueryImplementor<PersistentCollection<?>> query = namedQueryMemento.toQuery( session ); final QueryImplementor<PersistentCollection<?>> query = namedQueryMemento.toQuery( session );
query.setParameter( 1, key ); //noinspection unchecked
query.setParameter( (Parameter<Object>) query.getParameters().iterator().next(), key );
query.setHibernateFlushMode( FlushMode.MANUAL );
return query.getResultList().get( 0 ); return query.getResultList().get( 0 );
} }
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.loader.ast.internal; package org.hibernate.loader.ast.internal;
import org.hibernate.FlushMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
@ -14,6 +15,8 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.named.NamedQueryMemento; import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryImplementor;
import jakarta.persistence.Parameter;
/** /**
* Implementation of SingleIdEntityLoader for cases where the application has * Implementation of SingleIdEntityLoader for cases where the application has
* provided the select load query * provided the select load query
@ -44,7 +47,9 @@ public class SingleIdEntityLoaderProvidedQueryImpl<T> implements SingleIdEntityL
entityDescriptor.getMappedJavaType().getJavaTypeClass() entityDescriptor.getMappedJavaType().getJavaTypeClass()
); );
query.setParameter( 1, pkValue ); //noinspection unchecked
query.setParameter( (Parameter<Object>) query.getParameters().iterator().next(), pkValue );
query.setHibernateFlushMode( FlushMode.MANUAL );
return query.uniqueResult(); return query.uniqueResult();
} }

View File

@ -2678,20 +2678,19 @@ public abstract class AbstractEntityPersister
Component component = (Component) property.getValue(); Component component = (Component) property.getValue();
internalInitSubclassPropertyAliasesMap( name, component.getProperties() ); internalInitSubclassPropertyAliasesMap( name, component.getProperties() );
} }
else {
String[] aliases = new String[property.getColumnSpan()];
String[] cols = new String[property.getColumnSpan()];
int l = 0;
for ( Selectable selectable: property.getSelectables() ) {
Dialect dialect = getFactory().getJdbcServices().getDialect();
aliases[l] = selectable.getAlias( dialect, property.getValue().getTable() );
cols[l] = selectable.getText(dialect); // TODO: skip formulas?
l++;
}
subclassPropertyAliases.put( name, aliases ); String[] aliases = new String[property.getColumnSpan()];
subclassPropertyColumnNames.put( name, cols ); String[] cols = new String[property.getColumnSpan()];
int l = 0;
for ( Selectable selectable: property.getSelectables() ) {
Dialect dialect = getFactory().getJdbcServices().getDialect();
aliases[l] = selectable.getAlias( dialect, property.getValue().getTable() );
cols[l] = selectable.getText(dialect); // TODO: skip formulas?
l++;
} }
subclassPropertyAliases.put( name, aliases );
subclassPropertyColumnNames.put( name, cols );
} }
} }

View File

@ -10,7 +10,6 @@ import java.sql.CallableStatement;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.procedure.spi.CallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport;
import org.hibernate.procedure.spi.ParameterStrategy;
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor; import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
import org.hibernate.sql.exec.spi.JdbcCall; import org.hibernate.sql.exec.spi.JdbcCall;
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration; import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
@ -22,7 +21,6 @@ public abstract class AbstractStandardCallableStatementSupport implements Callab
String procedureName, String procedureName,
JdbcCall procedureCall, JdbcCall procedureCall,
CallableStatement statement, CallableStatement statement,
ParameterStrategy parameterStrategy,
ProcedureParameterMetadataImplementor parameterMetadata, ProcedureParameterMetadataImplementor parameterMetadata,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
if ( procedureCall.getFunctionReturn() != null ) { if ( procedureCall.getFunctionReturn() != null ) {

View File

@ -17,6 +17,7 @@ import org.hibernate.procedure.spi.ProcedureParameterImplementor;
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor; import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
import org.hibernate.sql.exec.internal.JdbcCallImpl; import org.hibernate.sql.exec.internal.JdbcCallImpl;
import org.hibernate.sql.exec.spi.JdbcCall; import org.hibernate.sql.exec.spi.JdbcCall;
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
import jakarta.persistence.ParameterMode; import jakarta.persistence.ParameterMode;
@ -38,7 +39,7 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
final boolean firstParamIsRefCursor = parameterMetadata.getParameterCount() != 0 final boolean firstParamIsRefCursor = parameterMetadata.getParameterCount() != 0
&& isFirstParameterModeRefCursor( parameterMetadata ); && isFirstParameterModeRefCursor( parameterMetadata );
if ( firstParamIsRefCursor ) { if ( firstParamIsRefCursor || functionReturn != null ) {
// validate that the parameter strategy is positional (cannot mix, and REF_CURSOR is inherently positional) // validate that the parameter strategy is positional (cannot mix, and REF_CURSOR is inherently positional)
if ( parameterMetadata.hasNamedParameters() ) { if ( parameterMetadata.hasNamedParameters() ) {
throw new HibernateException( "Cannot mix named parameters and REF_CURSOR parameter on PostgreSQL" ); throw new HibernateException( "Cannot mix named parameters and REF_CURSOR parameter on PostgreSQL" );
@ -46,11 +47,10 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
} }
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList(); final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder( final ParameterStrategy parameterStrategy = parameterMetadata.hasNamedParameters() ?
parameterMetadata.hasNamedParameters() ? ParameterStrategy.NAMED :
ParameterStrategy.NAMED : ParameterStrategy.POSITIONAL;
ParameterStrategy.POSITIONAL final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder( parameterStrategy );
);
final StringBuilder buffer; final StringBuilder buffer;
final int offset; final int offset;
@ -82,9 +82,19 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
throw new HibernateException( throw new HibernateException(
"PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered" ); "PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered" );
} }
buffer.append( sep ).append( "?" ); buffer.append( sep );
final JdbcCallParameterRegistration registration = parameter.toJdbcParameterRegistration(
i + offset,
procedureCall
);
if ( registration.getName() != null ) {
buffer.append( ':' ).append( registration.getName() );
}
else {
buffer.append( "?" );
}
sep = ","; sep = ",";
builder.addParameterRegistration( parameter.toJdbcParameterRegistration( i + offset, procedureCall ) ); builder.addParameterRegistration( registration );
} }
buffer.append( ")}" ); buffer.append( ")}" );

View File

@ -33,7 +33,6 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.procedure.NoSuchParameterException; import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterStrategyException; import org.hibernate.procedure.ParameterStrategyException;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
@ -591,7 +590,7 @@ public class ProcedureCallImpl<R>
final Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations = new IdentityHashMap<>(); final Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations = new IdentityHashMap<>();
final List<JdbcCallRefCursorExtractor> refCursorExtractors = new ArrayList<>(); final List<JdbcCallRefCursorExtractor> refCursorExtractors = new ArrayList<>();
if ( functionReturn != null ) { if ( call.getFunctionReturn() != null ) {
parameterRegistrations.put( functionReturn, call.getFunctionReturn() ); parameterRegistrations.put( functionReturn, call.getFunctionReturn() );
final JdbcCallRefCursorExtractorImpl refCursorExtractor = call.getFunctionReturn().getRefCursorExtractor(); final JdbcCallRefCursorExtractorImpl refCursorExtractor = call.getFunctionReturn().getRefCursorExtractor();
if ( refCursorExtractor != null ) { if ( refCursorExtractor != null ) {
@ -620,7 +619,6 @@ public class ProcedureCallImpl<R>
procedureName, procedureName,
call, call,
statement, statement,
parameterMetadata.getParameterStrategy(),
parameterMetadata, parameterMetadata,
getSession() getSession()
); );
@ -690,7 +688,7 @@ public class ProcedureCallImpl<R>
// Note that this should actually happen in an executor // Note that this should actually happen in an executor
try { try {
int paramBindingPosition = functionReturn == null ? 1 : 2; int paramBindingPosition = call.getFunctionReturn() == null ? 1 : 2;
for ( JdbcParameterBinder parameterBinder : call.getParameterBinders() ) { for ( JdbcParameterBinder parameterBinder : call.getParameterBinders() ) {
parameterBinder.bindParameterValue( parameterBinder.bindParameterValue(
statement, statement,

View File

@ -201,10 +201,10 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
.getJdbcSessionContext() .getJdbcSessionContext()
.getServiceRegistry().getService( JdbcEnvironment.class ) .getServiceRegistry().getService( JdbcEnvironment.class )
.getExtractedDatabaseMetaData(); .getExtractedDatabaseMetaData();
return return procedureCall.getFunctionReturn() == null
databaseMetaData.supportsNamedParameters() && databaseMetaData.supportsNamedParameters()
&& hibernateType instanceof ProcedureParameterNamedBinder && hibernateType instanceof ProcedureParameterNamedBinder
&& ( (ProcedureParameterNamedBinder<?>) hibernateType ).canDoSetting(); && ( (ProcedureParameterNamedBinder<?>) hibernateType ).canDoSetting();
} }
@Override @Override

View File

@ -18,6 +18,7 @@ import org.hibernate.procedure.spi.ProcedureParameterImplementor;
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor; import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
import org.hibernate.sql.exec.internal.JdbcCallImpl; import org.hibernate.sql.exec.internal.JdbcCallImpl;
import org.hibernate.sql.exec.spi.JdbcCall; import org.hibernate.sql.exec.spi.JdbcCall;
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
import jakarta.persistence.ParameterMode; import jakarta.persistence.ParameterMode;
@ -38,9 +39,11 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
public static final StandardCallableStatementSupport REF_CURSOR_INSTANCE = new StandardCallableStatementSupport( true ); public static final StandardCallableStatementSupport REF_CURSOR_INSTANCE = new StandardCallableStatementSupport( true );
private final boolean supportsRefCursors; private final boolean supportsRefCursors;
private final boolean implicitReturn;
public StandardCallableStatementSupport(boolean supportsRefCursors) { public StandardCallableStatementSupport(boolean supportsRefCursors) {
this.supportsRefCursors = supportsRefCursors; this.supportsRefCursors = supportsRefCursors;
this.implicitReturn = !supportsRefCursors;
} }
@Override @Override
@ -50,14 +53,13 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata(); final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata();
final SharedSessionContractImplementor session = procedureCall.getSession(); final SharedSessionContractImplementor session = procedureCall.getSession();
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList(); final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder( final ParameterStrategy parameterStrategy = functionReturn == null && parameterMetadata.hasNamedParameters() ?
parameterMetadata.hasNamedParameters() ? ParameterStrategy.NAMED :
ParameterStrategy.NAMED : ParameterStrategy.POSITIONAL;
ParameterStrategy.POSITIONAL final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder( parameterStrategy );
);
final StringBuilder buffer; final StringBuilder buffer;
final int offset; final int offset;
if ( functionReturn != null ) { if ( functionReturn != null && !implicitReturn ) {
offset = 2; offset = 2;
buffer = new StringBuilder( 11 + procedureName.length() + registrations.size() * 2 ).append( "{?=call " ); buffer = new StringBuilder( 11 + procedureName.length() + registrations.size() * 2 ).append( "{?=call " );
builder.setFunctionReturn( functionReturn.toJdbcFunctionReturn( session ) ); builder.setFunctionReturn( functionReturn.toJdbcFunctionReturn( session ) );
@ -75,9 +77,19 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) { if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
verifyRefCursorSupport( session.getJdbcServices().getJdbcEnvironment().getDialect() ); verifyRefCursorSupport( session.getJdbcServices().getJdbcEnvironment().getDialect() );
} }
buffer.append( sep ).append( "?" ); buffer.append( sep );
final JdbcCallParameterRegistration registration = parameter.toJdbcParameterRegistration(
i + offset,
procedureCall
);
if ( registration.getName() != null ) {
buffer.append( ':' ).append( registration.getName() );
}
else {
buffer.append( "?" );
}
sep = ","; sep = ",";
builder.addParameterRegistration( parameter.toJdbcParameterRegistration( i + offset, procedureCall ) ); builder.addParameterRegistration( registration );
} }
buffer.append( ")}" ); buffer.append( ")}" );

View File

@ -22,7 +22,6 @@ public interface CallableStatementSupport {
String procedureName, String procedureName,
JdbcCall procedureCall, JdbcCall procedureCall,
CallableStatement statement, CallableStatement statement,
ParameterStrategy parameterStrategy,
ProcedureParameterMetadataImplementor parameterMetadata, ProcedureParameterMetadataImplementor parameterMetadata,
SharedSessionContractImplementor session); SharedSessionContractImplementor session);
} }

View File

@ -404,6 +404,8 @@ public interface NativeQuery<T> extends Query<T>, SynchronizeableQuery {
NavigablePath getNavigablePath(); NavigablePath getNavigablePath();
LockMode getLockMode();
/** /**
* Set the lock mode for this return. * Set the lock mode for this return.
* *

View File

@ -39,6 +39,14 @@ public class FetchMementoBasicStandard implements FetchMementoBasic {
return navigablePath; return navigablePath;
} }
public BasicValuedModelPart getFetchedAttribute() {
return fetchedAttribute;
}
public String getColumnAlias() {
return columnAlias;
}
@Override @Override
public FetchBuilder resolve( public FetchBuilder resolve(
Parent parent, Parent parent,

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.internal;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.query.named.FetchMemento;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.complete.CompleteFetchBuilderEmbeddableValuedModelPart;
import org.hibernate.query.spi.NavigablePath;
/**
* @author Christian Beikov
*/
public class FetchMementoEmbeddableStandard implements FetchMemento {
private final NavigablePath navigablePath;
private final EmbeddableValuedModelPart attributeMapping;
private final List<String> columnNames;
public FetchMementoEmbeddableStandard(
NavigablePath navigablePath,
EmbeddableValuedModelPart attributeMapping,
List<String> columnNames) {
this.navigablePath = navigablePath;
this.attributeMapping = attributeMapping;
this.columnNames = columnNames;
}
@Override
public FetchBuilder resolve(
Parent parent,
Consumer<String> querySpaceConsumer,
ResultSetMappingResolutionContext context) {
return new CompleteFetchBuilderEmbeddableValuedModelPart( navigablePath, attributeMapping, columnNames );
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
public EmbeddableValuedModelPart getAttributeMapping() {
return attributeMapping;
}
public List<String> getColumnNames() {
return columnNames;
}
}

View File

@ -6,12 +6,16 @@
*/ */
package org.hibernate.query.internal; package org.hibernate.query.internal;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.results.dynamic.DynamicResultBuilderEntityStandard;
import org.hibernate.query.spi.NavigablePath; import org.hibernate.query.spi.NavigablePath;
import org.hibernate.query.named.FetchMemento; import org.hibernate.query.named.FetchMemento;
import org.hibernate.query.results.FetchBuilder; import org.hibernate.query.results.FetchBuilder;
@ -31,6 +35,7 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final String ownerTableAlias; private final String ownerTableAlias;
private final String tableAlias; private final String tableAlias;
private final List<String> keyColumnNames;
private final LockMode lockMode; private final LockMode lockMode;
private final FetchParentMemento parent; private final FetchParentMemento parent;
private final Map<String, FetchMemento> fetchMementoMap; private final Map<String, FetchMemento> fetchMementoMap;
@ -40,6 +45,7 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
NavigablePath navigablePath, NavigablePath navigablePath,
String ownerTableAlias, String ownerTableAlias,
String tableAlias, String tableAlias,
List<String> keyColumnNames,
LockMode lockMode, LockMode lockMode,
FetchParentMemento parent, FetchParentMemento parent,
Map<String, FetchMemento> fetchMementoMap, Map<String, FetchMemento> fetchMementoMap,
@ -47,6 +53,7 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
this.navigablePath = navigablePath; this.navigablePath = navigablePath;
this.ownerTableAlias = ownerTableAlias; this.ownerTableAlias = ownerTableAlias;
this.tableAlias = tableAlias; this.tableAlias = tableAlias;
this.keyColumnNames = keyColumnNames;
this.lockMode = lockMode; this.lockMode = lockMode;
this.parent = parent; this.parent = parent;
this.fetchMementoMap = fetchMementoMap; this.fetchMementoMap = fetchMementoMap;
@ -64,19 +71,34 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
Consumer<String> querySpaceConsumer, Consumer<String> querySpaceConsumer,
ResultSetMappingResolutionContext context) { ResultSetMappingResolutionContext context) {
final Map<String, FetchBuilder> fetchBuilderMap = new HashMap<>(); final Map<String, FetchBuilder> fetchBuilderMap = new HashMap<>();
fetchMementoMap.forEach( fetchMementoMap.forEach(
(attrName, fetchMemento) -> fetchBuilderMap.put( (attrName, fetchMemento) -> fetchBuilderMap.put(
attrName, attrName,
fetchMemento.resolve(this, querySpaceConsumer, context ) fetchMemento.resolve(this, querySpaceConsumer, context )
) )
); );
final DynamicResultBuilderEntityStandard resultBuilder;
if ( fetchable instanceof PluralAttributeMapping ) {
resultBuilder = new DynamicResultBuilderEntityStandard(
(EntityMappingType) ( (PluralAttributeMapping) fetchable ).getElementDescriptor().getPartMappingType(),
tableAlias,
navigablePath
);
}
else {
resultBuilder = new DynamicResultBuilderEntityStandard(
( (ToOneAttributeMapping) fetchable ).getEntityMappingType(),
tableAlias,
navigablePath
);
}
return new DynamicFetchBuilderLegacy( return new DynamicFetchBuilderLegacy(
tableAlias, tableAlias,
ownerTableAlias, ownerTableAlias,
fetchable.getFetchableName(), fetchable.getFetchableName(),
new ArrayList<>(), keyColumnNames,
fetchBuilderMap fetchBuilderMap,
resultBuilder
); );
} }
} }

View File

@ -9,7 +9,6 @@ package org.hibernate.query.named;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.query.internal.ResultSetMappingResolutionContext; import org.hibernate.query.internal.ResultSetMappingResolutionContext;
import org.hibernate.query.named.FetchMemento;
import org.hibernate.query.results.FetchBuilder; import org.hibernate.query.results.FetchBuilder;
/** /**

View File

@ -0,0 +1,133 @@
/*
* 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.complete;
import java.util.List;
import java.util.function.BiFunction;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.ResultSetMappingSqlSelection;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.query.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import static org.hibernate.query.results.ResultsHelper.impl;
import static org.hibernate.query.results.ResultsHelper.jdbcPositionToValuesArrayPosition;
/**
* CompleteFetchBuilder for embeddable-valued ModelParts
*
* @author Christian Beikov
*/
public class CompleteFetchBuilderEmbeddableValuedModelPart
implements CompleteFetchBuilder, ModelPartReferenceEmbeddable {
private final NavigablePath navigablePath;
private final EmbeddableValuedModelPart modelPart;
private final List<String> columnAliases;
public CompleteFetchBuilderEmbeddableValuedModelPart(
NavigablePath navigablePath,
EmbeddableValuedModelPart modelPart,
List<String> columnAliases) {
this.navigablePath = navigablePath;
this.modelPart = modelPart;
this.columnAliases = columnAliases;
}
@Override
public FetchBuilder cacheKeyInstance() {
return new CompleteFetchBuilderEmbeddableValuedModelPart(
navigablePath,
modelPart,
List.copyOf( columnAliases )
);
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public EmbeddableValuedModelPart getReferencedPart() {
return modelPart;
}
@Override
public Fetch buildFetch(
FetchParent parent,
NavigablePath fetchPath,
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
DomainResultCreationState domainResultCreationState) {
assert fetchPath.equals( navigablePath );
final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState );
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() );
modelPart.forEachSelectable(
(selectionIndex, selectableMapping) -> {
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, selectableMapping.getContainingTableExpression() );
final String mappedColumn = selectableMapping.getSelectionExpression();
final String columnAlias = columnAliases.get( selectionIndex );
creationStateImpl.resolveSqlSelection(
creationStateImpl.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableReference, mappedColumn ),
processingState -> {
final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( columnAlias );
final int valuesArrayPosition = jdbcPositionToValuesArrayPosition( jdbcPosition );
return new ResultSetMappingSqlSelection( valuesArrayPosition, selectableMapping.getJdbcMapping() );
}
),
modelPart.getJavaType(),
creationStateImpl.getSessionFactory().getTypeConfiguration()
);
}
);
return parent.generateFetchableFetch(
modelPart,
fetchPath,
FetchTiming.IMMEDIATE,
true,
null,
domainResultCreationState
);
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final CompleteFetchBuilderEmbeddableValuedModelPart that = (CompleteFetchBuilderEmbeddableValuedModelPart) o;
return navigablePath.equals( that.navigablePath )
&& modelPart.equals( that.modelPart )
&& columnAliases.equals( that.columnAliases );
}
@Override
public int hashCode() {
int result = navigablePath.hashCode();
result = 31 * result + modelPart.hashCode();
result = 31 * result + columnAliases.hashCode();
return result;
}
}

View File

@ -24,6 +24,7 @@ import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper; import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.ResultSetMappingSqlSelection; import org.hibernate.query.results.ResultSetMappingSqlSelection;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.spi.SqlAliasBaseConstant;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -105,7 +106,9 @@ public class CompleteResultBuilderCollectionStandard implements CompleteResultBu
navigablePath, navigablePath,
tableAlias, tableAlias,
null, null,
creationStateImpl, new SqlAliasBaseConstant( tableAlias ),
creationStateImpl.getSqlExpressionResolver(),
creationStateImpl.getFromClauseAccess(),
sessionFactory sessionFactory
); );
fromClauseAccess.registerTableGroup( navigablePath, rootTableGroup ); fromClauseAccess.registerTableGroup( navigablePath, rootTableGroup );

View File

@ -22,6 +22,7 @@ import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.ResultBuilder; import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultsHelper; import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.spi.SqlAliasBaseConstant;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.entity.EntityResult; import org.hibernate.sql.results.graph.entity.EntityResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
@ -82,6 +83,11 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
return entityDescriptor; return entityDescriptor;
} }
@Override
public LockMode getLockMode() {
return lockMode;
}
@Override @Override
public NativeQuery.RootReturn setLockMode(LockMode lockMode) { public NativeQuery.RootReturn setLockMode(LockMode lockMode) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -131,9 +137,11 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
// since this is only used for result set mappings, the canUseInnerJoins value is irrelevant. // since this is only used for result set mappings, the canUseInnerJoins value is irrelevant.
true, true,
navigablePath, navigablePath,
tableAlias,
null, null,
null, new SqlAliasBaseConstant( tableAlias ),
impl, impl.getSqlExpressionResolver(),
impl.getFromClauseAccess(),
impl.getCreationContext() impl.getCreationContext()
) )
); );
@ -141,7 +149,7 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
return new EntityResultImpl( return new EntityResultImpl(
navigablePath, navigablePath,
entityDescriptor, entityDescriptor,
null, tableAlias,
lockMode, lockMode,
(entityResult) -> { (entityResult) -> {
if ( discriminatorFetchBuilder == null ) { if ( discriminatorFetchBuilder == null ) {

View File

@ -85,7 +85,6 @@ public abstract class AbstractFetchBuilderContainer<T extends AbstractFetchBuild
} }
final DynamicFetchBuilderStandard fetchBuilder = new DynamicFetchBuilderStandard( final DynamicFetchBuilderStandard fetchBuilder = new DynamicFetchBuilderStandard(
this,
propertyName propertyName
); );

View File

@ -55,6 +55,8 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
private final Map<String, FetchBuilder> fetchBuilderMap; private final Map<String, FetchBuilder> fetchBuilderMap;
private final DynamicResultBuilderEntityStandard resultBuilderEntity; private final DynamicResultBuilderEntityStandard resultBuilderEntity;
private LockMode lockMode;
public DynamicFetchBuilderLegacy( public DynamicFetchBuilderLegacy(
String tableAlias, String tableAlias,
String ownerTableAlias, String ownerTableAlias,
@ -164,41 +166,41 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
keyDescriptor = toOneAttributeMapping.getForeignKeyDescriptor(); keyDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
} }
keyDescriptor.forEachSelectable( if ( !columnNames.isEmpty() ) {
(selectionIndex, selectableMapping) -> { keyDescriptor.forEachSelectable(
resolveSqlSelection( (selectionIndex, selectableMapping) -> {
columnNames.get( selectionIndex ), resolveSqlSelection(
createColumnReferenceKey( columnNames.get( selectionIndex ),
tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ), createColumnReferenceKey(
selectableMapping.getSelectionExpression() tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ),
), selectableMapping.getSelectionExpression()
selectableMapping.getJdbcMapping(), ),
jdbcResultsMetadata, selectableMapping.getJdbcMapping(),
domainResultCreationState jdbcResultsMetadata,
); domainResultCreationState
} );
); }
);
}
// We process the fetch builder such that it contains a resultBuilderEntity before calling this method in ResultSetMappingProcessor // We process the fetch builder such that it contains a resultBuilderEntity before calling this method in ResultSetMappingProcessor
assert resultBuilderEntity != null; if ( resultBuilderEntity != null ) {
return resultBuilderEntity.buildFetch(
return resultBuilderEntity.buildFetch( parent,
parent, attributeMapping,
attributeMapping, jdbcResultsMetadata,
jdbcResultsMetadata, creationState
creationState );
); }
}
else {
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.IMMEDIATE,
true,
null,
domainResultCreationState
);
} }
return parent.generateFetchableFetch(
attributeMapping,
parent.resolveNavigablePath( attributeMapping ),
FetchTiming.IMMEDIATE,
true,
null,
domainResultCreationState
);
} }
private void resolveSqlSelection( private void resolveSqlSelection(
@ -235,17 +237,21 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
@Override @Override
public NativeQuery.FetchReturn setLockMode(LockMode lockMode) { public NativeQuery.FetchReturn setLockMode(LockMode lockMode) {
return null; this.lockMode = lockMode;
return this;
} }
@Override @Override
public NativeQuery.FetchReturn addProperty(String propertyName, String columnAlias) { public NativeQuery.FetchReturn addProperty(String propertyName, String columnAlias) {
return null; addProperty( propertyName ).addColumnAlias( columnAlias );
return this;
} }
@Override @Override
public NativeQuery.ReturnProperty addProperty(String propertyName) { public NativeQuery.ReturnProperty addProperty(String propertyName) {
return null; DynamicFetchBuilderStandard fetchBuilder = new DynamicFetchBuilderStandard( propertyName );
fetchBuilderMap.put( propertyName, fetchBuilder );
return fetchBuilder;
} }
@Override @Override

View File

@ -14,6 +14,7 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.NativeQuery; import org.hibernate.query.NativeQuery;
import org.hibernate.query.spi.NavigablePath; import org.hibernate.query.spi.NavigablePath;
@ -38,24 +39,15 @@ import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnRefere
public class DynamicFetchBuilderStandard public class DynamicFetchBuilderStandard
implements DynamicFetchBuilder, NativeQuery.ReturnProperty { implements DynamicFetchBuilder, NativeQuery.ReturnProperty {
private final DynamicFetchBuilderContainer container;
private final String fetchableName; private final String fetchableName;
private final List<String> columnNames; private final List<String> columnNames;
public DynamicFetchBuilderStandard( public DynamicFetchBuilderStandard(String fetchableName) {
DynamicFetchBuilderContainer container,
String fetchableName) {
this.container = container;
this.fetchableName = fetchableName; this.fetchableName = fetchableName;
this.columnNames = new ArrayList<>(); this.columnNames = new ArrayList<>();
} }
private DynamicFetchBuilderStandard( private DynamicFetchBuilderStandard(String fetchableName, List<String> columnNames) {
DynamicFetchBuilderContainer container,
String fetchableName,
List<String> columnNames) {
this.container = container;
this.fetchableName = fetchableName; this.fetchableName = fetchableName;
this.columnNames = columnNames; this.columnNames = columnNames;
} }
@ -63,7 +55,6 @@ public class DynamicFetchBuilderStandard
@Override @Override
public DynamicFetchBuilderStandard cacheKeyInstance() { public DynamicFetchBuilderStandard cacheKeyInstance() {
return new DynamicFetchBuilderStandard( return new DynamicFetchBuilderStandard(
container,
fetchableName, fetchableName,
List.copyOf( columnNames ) List.copyOf( columnNames )
); );
@ -71,7 +62,6 @@ public class DynamicFetchBuilderStandard
public DynamicFetchBuilderStandard cacheKeyInstance(DynamicFetchBuilderContainer container) { public DynamicFetchBuilderStandard cacheKeyInstance(DynamicFetchBuilderContainer container) {
return new DynamicFetchBuilderStandard( return new DynamicFetchBuilderStandard(
container,
fetchableName, fetchableName,
List.copyOf( columnNames ) List.copyOf( columnNames )
); );
@ -124,6 +114,17 @@ public class DynamicFetchBuilderStandard
creationStateImpl creationStateImpl
); );
} }
else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
attributeMapping.forEachSelectable( selectableConsumer );
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.IMMEDIATE,
false,
null,
creationStateImpl
);
}
else if ( attributeMapping instanceof ToOneAttributeMapping ) { else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping; final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
toOneAttributeMapping.getForeignKeyDescriptor().visitKeySelectables( selectableConsumer ); toOneAttributeMapping.getForeignKeyDescriptor().visitKeySelectables( selectableConsumer );

View File

@ -69,6 +69,11 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde
return navigablePath; return navigablePath;
} }
@Override
public LockMode getLockMode() {
return explicitLockMode;
}
@Override @Override
public NativeQuery.RootReturn setLockMode(LockMode lockMode) { public NativeQuery.RootReturn setLockMode(LockMode lockMode) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View File

@ -103,6 +103,11 @@ public class DynamicResultBuilderEntityStandard
return navigablePath; return navigablePath;
} }
@Override
public LockMode getLockMode() {
return lockMode;
}
@Override @Override
public NativeQuery.RootReturn addIdColumnAliases(String... aliases) { public NativeQuery.RootReturn addIdColumnAliases(String... aliases) {
if ( idColumnNames == null ) { if ( idColumnNames == null ) {
@ -153,7 +158,7 @@ public class DynamicResultBuilderEntityStandard
return buildResultOrFetch( return buildResultOrFetch(
(tableGroup) -> parent.generateFetchableFetch( (tableGroup) -> parent.generateFetchableFetch(
fetchable, fetchable,
navigablePath, parent.resolveNavigablePath( fetchable ),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
true, true,
null, null,

View File

@ -173,16 +173,17 @@ public class ParameterParser {
} }
private static void checkIsNotAFunctionCall(String sqlString) { private static void checkIsNotAFunctionCall(String sqlString) {
if ( !( sqlString.startsWith( "{" ) && sqlString.endsWith( "}" ) ) ) { final String trimmed = sqlString.trim();
if ( !( trimmed.startsWith( "{" ) && trimmed.endsWith( "}" ) ) ) {
return; return;
} }
final int chopLocation = sqlString.indexOf( "call" ); final int chopLocation = trimmed.indexOf( "call" );
if ( chopLocation <= 0 ) { if ( chopLocation <= 0 ) {
return; return;
} }
final String checkString = sqlString.substring( 1, chopLocation + 4 ); final String checkString = trimmed.substring( 1, chopLocation + 4 );
final String fixture = "?=call"; final String fixture = "?=call";
int fixturePosition = 0; int fixturePosition = 0;
boolean matches = true; boolean matches = true;

View File

@ -13,6 +13,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.engine.query.ParameterRecognitionException;
import org.hibernate.query.internal.QueryParameterNamedImpl; import org.hibernate.query.internal.QueryParameterNamedImpl;
import org.hibernate.query.internal.QueryParameterPositionalImpl; import org.hibernate.query.internal.QueryParameterPositionalImpl;
import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.QueryParameterImplementor;
@ -125,7 +126,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
parameterStyle = ParameterStyle.NAMED; parameterStyle = ParameterStyle.NAMED;
} }
else if ( parameterStyle != ParameterStyle.NAMED ) { else if ( parameterStyle != ParameterStyle.NAMED ) {
throw new IllegalStateException( "Cannot mix parameter styles between JDBC-style, ordinal and named in the same query" ); throw new ParameterRecognitionException( "Cannot mix parameter styles between JDBC-style, ordinal and named in the same query" );
} }
QueryParameterImplementor<?> parameter = null; QueryParameterImplementor<?> parameter = null;

View File

@ -17,6 +17,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
@ -233,6 +234,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
alias2Persister.get( fetchBuilder.getTableAlias() ).findContainingEntityMapping(), alias2Persister.get( fetchBuilder.getTableAlias() ).findContainingEntityMapping(),
fetchBuilder.getTableAlias(), fetchBuilder.getTableAlias(),
suffix, suffix,
null,
determineNavigablePath( fetchBuilder ) determineNavigablePath( fetchBuilder )
); );
final SQLLoadable loadable = (SQLLoadable) alias2Persister.get( fetchBuilder.getOwnerAlias() ); final SQLLoadable loadable = (SQLLoadable) alias2Persister.get( fetchBuilder.getOwnerAlias() );
@ -296,6 +298,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
rootReturn.getEntityMapping(), rootReturn.getEntityMapping(),
rootReturn.getTableAlias(), rootReturn.getTableAlias(),
suffix, suffix,
rootReturn.getLockMode(),
new NavigablePath( rootReturn.getEntityMapping().getEntityName() ) new NavigablePath( rootReturn.getEntityMapping().getEntityName() )
); );
} }
@ -304,6 +307,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
EntityMappingType entityMapping, EntityMappingType entityMapping,
String tableAlias, String tableAlias,
String suffix, String suffix,
LockMode lockMode,
NavigablePath navigablePath) { NavigablePath navigablePath) {
final SQLLoadable loadable = (SQLLoadable) entityMapping.getEntityPersister(); final SQLLoadable loadable = (SQLLoadable) entityMapping.getEntityPersister();
final DynamicResultBuilderEntityStandard resultBuilderEntity = new DynamicResultBuilderEntityStandard( final DynamicResultBuilderEntityStandard resultBuilderEntity = new DynamicResultBuilderEntityStandard(
@ -311,6 +315,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
tableAlias, tableAlias,
navigablePath navigablePath
); );
resultBuilderEntity.setLockMode( lockMode );
final String[] identifierAliases = loadable.getIdentifierAliases( suffix ); final String[] identifierAliases = loadable.getIdentifierAliases( suffix );
resultBuilderEntity.addIdColumnAliases( identifierAliases ); resultBuilderEntity.addIdColumnAliases( identifierAliases );

View File

@ -49,6 +49,11 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
this.refCursorExtractor = refCursorExtractor; this.refCursorExtractor = refCursorExtractor;
} }
@Override
public String getName() {
return name;
}
@Override @Override
public JdbcParameterBinder getParameterBinder() { public JdbcParameterBinder getParameterBinder() {
return parameterBinder; return parameterBinder;

View File

@ -18,6 +18,9 @@ import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface JdbcCallParameterRegistration { public interface JdbcCallParameterRegistration {
String getName();
ParameterMode getParameterMode(); ParameterMode getParameterMode();
void registerParameter( void registerParameter(

View File

@ -12,6 +12,7 @@ import java.util.List;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.ResultListTransformer;
import org.hibernate.sql.results.jdbc.spi.JdbcValues; import org.hibernate.sql.results.jdbc.spi.JdbcValues;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
@ -124,6 +125,13 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
persistenceContext.getLoadContexts().deregister( jdbcValuesSourceProcessingState ); persistenceContext.getLoadContexts().deregister( jdbcValuesSourceProcessingState );
} }
//noinspection unchecked
final ResultListTransformer<R> resultListTransformer = (ResultListTransformer<R>) jdbcValuesSourceProcessingState.getExecutionContext()
.getQueryOptions()
.getResultListTransformer();
if ( resultListTransformer != null ) {
return resultListTransformer.transformList( results );
}
return results; return results;
} }
catch (RuntimeException e) { catch (RuntimeException e) {

View File

@ -1,87 +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.orm.test.annotations.entity;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
/**
* Used to persist and retrieve objects of type 'PhoneNumber'
*
* @author Sharath Reddy
*/
public class PhoneNumberType implements UserType {
public int[] sqlTypes() {
return new int[]{Types.VARCHAR};
}
public Class<?> returnedClass() {
return PhoneNumber.class;
}
public boolean equals(Object x, Object y) throws HibernateException {
return ( x == y ) || ( x != null && x.equals( y ) );
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
public Object nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException {
String result = rs.getString( position );
if ( rs.wasNull() ) return null;
if (result.length() <= 6) {
return new PhoneNumber(result);
}
else {
return new OverseasPhoneNumber(result);
}
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if ( value == null ) {
st.setNull( index, sqlTypes()[0] );
return;
}
PhoneNumber phoneNumber = (PhoneNumber) value;
String number = phoneNumber.getNumber();
st.setString( index, number);
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false;
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
}

View File

@ -44,7 +44,6 @@ import static org.hibernate.jpa.HibernateHints.HINT_CALLABLE_FUNCTION;
@NamedNativeQuery( @NamedNativeQuery(
name = "fn_person_and_phones_hana", name = "fn_person_and_phones_hana",
query = "select \"pr.id\", \"pr.name\", \"pr.nickName\", \"pr.address\", \"pr.createdOn\", \"pr.version\", \"ph.id\", \"ph.person_id\", \"ph.phone_number\", \"ph.valid\" from fn_person_and_phones( ? )", query = "select \"pr.id\", \"pr.name\", \"pr.nickName\", \"pr.address\", \"pr.createdOn\", \"pr.version\", \"ph.id\", \"ph.person_id\", \"ph.phone_number\", \"ph.valid\" from fn_person_and_phones( ? )",
callable = false,
resultSetMapping = "person_with_phones_hana" resultSetMapping = "person_with_phones_hana"
) )
@SqlResultSetMappings({ @SqlResultSetMappings({