Fix some stored procedure and native query issues
This commit is contained in:
parent
8ed1ed5159
commit
439788198f
|
@ -7,13 +7,15 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.EntityResult;
|
||||
import jakarta.persistence.FieldResult;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.NamedStoredProcedureQuery;
|
||||
import jakarta.persistence.QueryHint;
|
||||
import jakarta.persistence.SqlResultSetMapping;
|
||||
import jakarta.persistence.SqlResultSetMappings;
|
||||
import jakarta.persistence.StoredProcedureParameter;
|
||||
|
||||
import org.hibernate.annotations.NamedNativeQuery;
|
||||
import org.hibernate.c3p0.internal.C3P0ConnectionProvider;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
import org.hibernate.dialect.OracleDialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
@ -27,7 +29,7 @@ import static org.junit.Assert.assertEquals;
|
|||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
@RequiresDialect(OracleDialect.class)
|
||||
@TestForIssue( jiraKey = "HHH-10256" )
|
||||
public class OracleSQLCallableStatementProxyTest extends
|
||||
BaseCoreFunctionalTestCase {
|
||||
|
@ -95,19 +97,19 @@ public class OracleSQLCallableStatementProxyTest extends
|
|||
public void testStoredProcedureOutParameter() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
List<Object[]> persons = session
|
||||
.createNamedQuery(
|
||||
"getPerson")
|
||||
.createNamedStoredProcedureQuery( "getPerson" )
|
||||
.setParameter(1, 1L)
|
||||
.getResultList();
|
||||
assertEquals(1, persons.size());
|
||||
} );
|
||||
}
|
||||
|
||||
@NamedNativeQuery(
|
||||
@NamedStoredProcedureQuery(
|
||||
name = "getPerson",
|
||||
query = "{ ? = call fn_person( ? ) }",
|
||||
callable = true,
|
||||
resultSetMapping = "person"
|
||||
procedureName = "fn_person",
|
||||
resultSetMappings = "person",
|
||||
hints = @QueryHint(name = "org.hibernate.callableFunction", value = "true"),
|
||||
parameters = @StoredProcedureParameter(type = Long.class)
|
||||
)
|
||||
@SqlResultSetMappings({
|
||||
@SqlResultSetMapping(
|
||||
|
|
|
@ -153,7 +153,7 @@ public class Identifier implements Comparable<Identifier> {
|
|||
public static String unQuote(String name) {
|
||||
assert isQuoted( name );
|
||||
|
||||
return name.substring( 1, name.length() - 2 );
|
||||
return name.substring( 1, name.length() - 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -288,9 +288,6 @@ public class MetadataBuildingProcess {
|
|||
processor.postProcessEntityHierarchies();
|
||||
|
||||
processor.processResultSetMappings();
|
||||
processor.processNamedQueries();
|
||||
|
||||
processor.finishUp();
|
||||
|
||||
for ( MetadataContributor contributor : classLoaderService.loadJavaServices( MetadataContributor.class ) ) {
|
||||
log.tracef( "Calling MetadataContributor : %s", contributor );
|
||||
|
@ -299,6 +296,11 @@ public class MetadataBuildingProcess {
|
|||
|
||||
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() ) {
|
||||
final Iterable<AdditionalJaxbMappingProducer> producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class );
|
||||
if ( producers != null ) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
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.MetadataBuildingContext;
|
||||
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.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
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.internal.FetchMementoBasicStandard;
|
||||
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.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
|
||||
import org.hibernate.type.BasicType;
|
||||
|
||||
/**
|
||||
|
@ -498,6 +512,21 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
|
|||
this.propertyPathParts = propertyPath.split( "\\." );
|
||||
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(
|
||||
"Creating PropertyFetchDescriptor (%s : %s) for ResultSet mapping - %s",
|
||||
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
|
||||
public String getFetchablePath() {
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
public List<String> getColumnAliases() {
|
||||
return columnAliases;
|
||||
}
|
||||
|
||||
private static List<String> extractColumnAliases(
|
||||
JaxbHbmNativeQueryPropertyReturnType hbmPropertyMapping,
|
||||
MetadataBuildingContext context) {
|
||||
|
@ -520,7 +638,7 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
|
|||
|
||||
final List<String> columnAliases = new ArrayList<>( hbmPropertyMapping.getReturnColumn().size() );
|
||||
hbmPropertyMapping.getReturnColumn().forEach(
|
||||
(column) -> columnAliases.add( column.getName() )
|
||||
column -> columnAliases.add( column.getName() )
|
||||
);
|
||||
return columnAliases;
|
||||
}
|
||||
|
@ -552,7 +670,28 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
|
|||
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
|
||||
|
@ -632,16 +771,26 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
|
|||
applyFetchJoins( joinDescriptorsAccess, tableAlias, propertyFetchDescriptors );
|
||||
|
||||
final Map<String, FetchMemento> fetchDescriptorMap = new HashMap<>();
|
||||
final List<String> keyColumnNames = new ArrayList<>();
|
||||
final boolean isPlural = thisAsParentMemento.getFetchableContainer() instanceof PluralAttributeMapping;
|
||||
propertyFetchDescriptors.forEach(
|
||||
hbmFetchDescriptor -> fetchDescriptorMap.put(
|
||||
hbmFetchDescriptor.getFetchablePath(),
|
||||
hbmFetchDescriptor.resolve( resolutionContext )
|
||||
)
|
||||
hbmFetchDescriptor -> {
|
||||
if ( isPlural && "key".equals( hbmFetchDescriptor.getFetchablePath() ) ) {
|
||||
keyColumnNames.addAll( ( (PropertyFetchDescriptor) hbmFetchDescriptor ).getColumnAliases() );
|
||||
}
|
||||
else {
|
||||
fetchDescriptorMap.put(
|
||||
hbmFetchDescriptor.getFetchablePath(),
|
||||
hbmFetchDescriptor.resolve( resolutionContext )
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
memento = new FetchMementoHbmStandard(
|
||||
thisAsParentMemento.getNavigablePath(),
|
||||
ownerTableAlias,
|
||||
tableAlias,
|
||||
keyColumnNames,
|
||||
lockMode,
|
||||
thisAsParentMemento,
|
||||
fetchDescriptorMap,
|
||||
|
@ -665,7 +814,14 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
|
|||
final FetchParentMemento ownerMemento = hbmFetchParent.resolveParentMemento( resolutionContext );
|
||||
|
||||
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 );
|
||||
|
||||
for ( int i = 1; i < parts.length; i++ ) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.boot.query;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -76,10 +77,26 @@ public class NamedNativeQueryDefinitionBuilder extends AbstractNamedQueryBuilder
|
|||
return this;
|
||||
}
|
||||
|
||||
public String getSqlString() {
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
public Set<String> getQuerySpaces() {
|
||||
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) {
|
||||
if ( querySpaces == null || querySpaces.isEmpty() ) {
|
||||
return this;
|
||||
|
|
|
@ -1271,6 +1271,12 @@ public class OracleDialect extends Dialect {
|
|||
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
|
||||
public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException {
|
||||
return (ResultSet) statement.getObject( name );
|
||||
|
|
|
@ -884,7 +884,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
return exporter;
|
||||
}
|
||||
|
||||
private class SqlServerSequenceExporter extends StandardSequenceExporter {
|
||||
private static class SqlServerSequenceExporter extends StandardSequenceExporter {
|
||||
|
||||
public SqlServerSequenceExporter(Dialect 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
|
||||
public String generatedAs(String generatedAs) {
|
||||
return " as (" + generatedAs + ") persisted";
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
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.spi.QueryImplementor;
|
||||
|
||||
import jakarta.persistence.Parameter;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -34,7 +37,9 @@ public class CollectionLoaderNamedQuery implements CollectionLoader {
|
|||
@Override
|
||||
public PersistentCollection<?> load(Object key, SharedSessionContractImplementor 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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
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.spi.QueryImplementor;
|
||||
|
||||
import jakarta.persistence.Parameter;
|
||||
|
||||
/**
|
||||
* Implementation of SingleIdEntityLoader for cases where the application has
|
||||
* provided the select load query
|
||||
|
@ -44,7 +47,9 @@ public class SingleIdEntityLoaderProvidedQueryImpl<T> implements SingleIdEntityL
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -2678,20 +2678,19 @@ public abstract class AbstractEntityPersister
|
|||
Component component = (Component) property.getValue();
|
||||
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 );
|
||||
subclassPropertyColumnNames.put( name, cols );
|
||||
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 );
|
||||
subclassPropertyColumnNames.put( name, cols );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.sql.CallableStatement;
|
|||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.ParameterStrategy;
|
||||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
@ -22,7 +21,6 @@ public abstract class AbstractStandardCallableStatementSupport implements Callab
|
|||
String procedureName,
|
||||
JdbcCall procedureCall,
|
||||
CallableStatement statement,
|
||||
ParameterStrategy parameterStrategy,
|
||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( procedureCall.getFunctionReturn() != null ) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
|||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
|
@ -38,7 +39,7 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
|
|||
final boolean firstParamIsRefCursor = parameterMetadata.getParameterCount() != 0
|
||||
&& isFirstParameterModeRefCursor( parameterMetadata );
|
||||
|
||||
if ( firstParamIsRefCursor ) {
|
||||
if ( firstParamIsRefCursor || functionReturn != null ) {
|
||||
// validate that the parameter strategy is positional (cannot mix, and REF_CURSOR is inherently positional)
|
||||
if ( parameterMetadata.hasNamedParameters() ) {
|
||||
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 JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder(
|
||||
parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL
|
||||
);
|
||||
final ParameterStrategy parameterStrategy = parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL;
|
||||
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder( parameterStrategy );
|
||||
|
||||
final StringBuilder buffer;
|
||||
final int offset;
|
||||
|
@ -82,9 +82,19 @@ public class PostgresCallableStatementSupport extends AbstractStandardCallableSt
|
|||
throw new HibernateException(
|
||||
"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 = ",";
|
||||
builder.addParameterRegistration( parameter.toJdbcParameterRegistration( i + offset, procedureCall ) );
|
||||
builder.addParameterRegistration( registration );
|
||||
}
|
||||
|
||||
buffer.append( ")}" );
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.hibernate.internal.util.StringHelper;
|
|||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.procedure.NoSuchParameterException;
|
||||
import org.hibernate.procedure.ParameterStrategyException;
|
||||
import org.hibernate.procedure.ProcedureCall;
|
||||
|
@ -591,7 +590,7 @@ public class ProcedureCallImpl<R>
|
|||
|
||||
final Map<ProcedureParameter<?>, JdbcCallParameterRegistration> parameterRegistrations = new IdentityHashMap<>();
|
||||
final List<JdbcCallRefCursorExtractor> refCursorExtractors = new ArrayList<>();
|
||||
if ( functionReturn != null ) {
|
||||
if ( call.getFunctionReturn() != null ) {
|
||||
parameterRegistrations.put( functionReturn, call.getFunctionReturn() );
|
||||
final JdbcCallRefCursorExtractorImpl refCursorExtractor = call.getFunctionReturn().getRefCursorExtractor();
|
||||
if ( refCursorExtractor != null ) {
|
||||
|
@ -620,7 +619,6 @@ public class ProcedureCallImpl<R>
|
|||
procedureName,
|
||||
call,
|
||||
statement,
|
||||
parameterMetadata.getParameterStrategy(),
|
||||
parameterMetadata,
|
||||
getSession()
|
||||
);
|
||||
|
@ -690,7 +688,7 @@ public class ProcedureCallImpl<R>
|
|||
// Note that this should actually happen in an executor
|
||||
|
||||
try {
|
||||
int paramBindingPosition = functionReturn == null ? 1 : 2;
|
||||
int paramBindingPosition = call.getFunctionReturn() == null ? 1 : 2;
|
||||
for ( JdbcParameterBinder parameterBinder : call.getParameterBinders() ) {
|
||||
parameterBinder.bindParameterValue(
|
||||
statement,
|
||||
|
|
|
@ -201,10 +201,10 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
|
|||
.getJdbcSessionContext()
|
||||
.getServiceRegistry().getService( JdbcEnvironment.class )
|
||||
.getExtractedDatabaseMetaData();
|
||||
return
|
||||
databaseMetaData.supportsNamedParameters()
|
||||
&& hibernateType instanceof ProcedureParameterNamedBinder
|
||||
&& ( (ProcedureParameterNamedBinder<?>) hibernateType ).canDoSetting();
|
||||
return procedureCall.getFunctionReturn() == null
|
||||
&& databaseMetaData.supportsNamedParameters()
|
||||
&& hibernateType instanceof ProcedureParameterNamedBinder
|
||||
&& ( (ProcedureParameterNamedBinder<?>) hibernateType ).canDoSetting();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.hibernate.procedure.spi.ProcedureParameterImplementor;
|
|||
import org.hibernate.query.spi.ProcedureParameterMetadataImplementor;
|
||||
import org.hibernate.sql.exec.internal.JdbcCallImpl;
|
||||
import org.hibernate.sql.exec.spi.JdbcCall;
|
||||
import org.hibernate.sql.exec.spi.JdbcCallParameterRegistration;
|
||||
|
||||
import jakarta.persistence.ParameterMode;
|
||||
|
||||
|
@ -38,9 +39,11 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
|||
public static final StandardCallableStatementSupport REF_CURSOR_INSTANCE = new StandardCallableStatementSupport( true );
|
||||
|
||||
private final boolean supportsRefCursors;
|
||||
private final boolean implicitReturn;
|
||||
|
||||
public StandardCallableStatementSupport(boolean supportsRefCursors) {
|
||||
this.supportsRefCursors = supportsRefCursors;
|
||||
this.implicitReturn = !supportsRefCursors;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,14 +53,13 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
|||
final ProcedureParameterMetadataImplementor parameterMetadata = procedureCall.getParameterMetadata();
|
||||
final SharedSessionContractImplementor session = procedureCall.getSession();
|
||||
final List<? extends ProcedureParameterImplementor<?>> registrations = parameterMetadata.getRegistrationsAsList();
|
||||
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder(
|
||||
parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL
|
||||
);
|
||||
final ParameterStrategy parameterStrategy = functionReturn == null && parameterMetadata.hasNamedParameters() ?
|
||||
ParameterStrategy.NAMED :
|
||||
ParameterStrategy.POSITIONAL;
|
||||
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder( parameterStrategy );
|
||||
final StringBuilder buffer;
|
||||
final int offset;
|
||||
if ( functionReturn != null ) {
|
||||
if ( functionReturn != null && !implicitReturn ) {
|
||||
offset = 2;
|
||||
buffer = new StringBuilder( 11 + procedureName.length() + registrations.size() * 2 ).append( "{?=call " );
|
||||
builder.setFunctionReturn( functionReturn.toJdbcFunctionReturn( session ) );
|
||||
|
@ -75,9 +77,19 @@ public class StandardCallableStatementSupport extends AbstractStandardCallableSt
|
|||
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
|
||||
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 = ",";
|
||||
builder.addParameterRegistration( parameter.toJdbcParameterRegistration( i + offset, procedureCall ) );
|
||||
builder.addParameterRegistration( registration );
|
||||
}
|
||||
|
||||
buffer.append( ")}" );
|
||||
|
|
|
@ -22,7 +22,6 @@ public interface CallableStatementSupport {
|
|||
String procedureName,
|
||||
JdbcCall procedureCall,
|
||||
CallableStatement statement,
|
||||
ParameterStrategy parameterStrategy,
|
||||
ProcedureParameterMetadataImplementor parameterMetadata,
|
||||
SharedSessionContractImplementor session);
|
||||
}
|
||||
|
|
|
@ -404,6 +404,8 @@ public interface NativeQuery<T> extends Query<T>, SynchronizeableQuery {
|
|||
|
||||
NavigablePath getNavigablePath();
|
||||
|
||||
LockMode getLockMode();
|
||||
|
||||
/**
|
||||
* Set the lock mode for this return.
|
||||
*
|
||||
|
|
|
@ -39,6 +39,14 @@ public class FetchMementoBasicStandard implements FetchMementoBasic {
|
|||
return navigablePath;
|
||||
}
|
||||
|
||||
public BasicValuedModelPart getFetchedAttribute() {
|
||||
return fetchedAttribute;
|
||||
}
|
||||
|
||||
public String getColumnAlias() {
|
||||
return columnAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchBuilder resolve(
|
||||
Parent parent,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -6,12 +6,16 @@
|
|||
*/
|
||||
package org.hibernate.query.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
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.named.FetchMemento;
|
||||
import org.hibernate.query.results.FetchBuilder;
|
||||
|
@ -31,6 +35,7 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
|
|||
private final NavigablePath navigablePath;
|
||||
private final String ownerTableAlias;
|
||||
private final String tableAlias;
|
||||
private final List<String> keyColumnNames;
|
||||
private final LockMode lockMode;
|
||||
private final FetchParentMemento parent;
|
||||
private final Map<String, FetchMemento> fetchMementoMap;
|
||||
|
@ -40,6 +45,7 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
|
|||
NavigablePath navigablePath,
|
||||
String ownerTableAlias,
|
||||
String tableAlias,
|
||||
List<String> keyColumnNames,
|
||||
LockMode lockMode,
|
||||
FetchParentMemento parent,
|
||||
Map<String, FetchMemento> fetchMementoMap,
|
||||
|
@ -47,6 +53,7 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
|
|||
this.navigablePath = navigablePath;
|
||||
this.ownerTableAlias = ownerTableAlias;
|
||||
this.tableAlias = tableAlias;
|
||||
this.keyColumnNames = keyColumnNames;
|
||||
this.lockMode = lockMode;
|
||||
this.parent = parent;
|
||||
this.fetchMementoMap = fetchMementoMap;
|
||||
|
@ -64,19 +71,34 @@ public class FetchMementoHbmStandard implements FetchMemento, FetchMemento.Paren
|
|||
Consumer<String> querySpaceConsumer,
|
||||
ResultSetMappingResolutionContext context) {
|
||||
final Map<String, FetchBuilder> fetchBuilderMap = new HashMap<>();
|
||||
|
||||
fetchMementoMap.forEach(
|
||||
(attrName, fetchMemento) -> fetchBuilderMap.put(
|
||||
attrName,
|
||||
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(
|
||||
tableAlias,
|
||||
ownerTableAlias,
|
||||
fetchable.getFetchableName(),
|
||||
new ArrayList<>(),
|
||||
fetchBuilderMap
|
||||
keyColumnNames,
|
||||
fetchBuilderMap,
|
||||
resultBuilder
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.query.named;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.query.internal.ResultSetMappingResolutionContext;
|
||||
import org.hibernate.query.named.FetchMemento;
|
||||
import org.hibernate.query.results.FetchBuilder;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.hibernate.query.results.ResultBuilder;
|
|||
import org.hibernate.query.results.ResultsHelper;
|
||||
import org.hibernate.query.results.ResultSetMappingSqlSelection;
|
||||
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.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
|
@ -105,7 +106,9 @@ public class CompleteResultBuilderCollectionStandard implements CompleteResultBu
|
|||
navigablePath,
|
||||
tableAlias,
|
||||
null,
|
||||
creationStateImpl,
|
||||
new SqlAliasBaseConstant( tableAlias ),
|
||||
creationStateImpl.getSqlExpressionResolver(),
|
||||
creationStateImpl.getFromClauseAccess(),
|
||||
sessionFactory
|
||||
);
|
||||
fromClauseAccess.registerTableGroup( navigablePath, rootTableGroup );
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.query.results.FetchBuilder;
|
|||
import org.hibernate.query.results.ResultBuilder;
|
||||
import org.hibernate.query.results.ResultsHelper;
|
||||
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.entity.EntityResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
|
@ -82,6 +83,11 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
|
|||
return entityDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode getLockMode() {
|
||||
return lockMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQuery.RootReturn setLockMode(LockMode lockMode) {
|
||||
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.
|
||||
true,
|
||||
navigablePath,
|
||||
tableAlias,
|
||||
null,
|
||||
null,
|
||||
impl,
|
||||
new SqlAliasBaseConstant( tableAlias ),
|
||||
impl.getSqlExpressionResolver(),
|
||||
impl.getFromClauseAccess(),
|
||||
impl.getCreationContext()
|
||||
)
|
||||
);
|
||||
|
@ -141,7 +149,7 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
|
|||
return new EntityResultImpl(
|
||||
navigablePath,
|
||||
entityDescriptor,
|
||||
null,
|
||||
tableAlias,
|
||||
lockMode,
|
||||
(entityResult) -> {
|
||||
if ( discriminatorFetchBuilder == null ) {
|
||||
|
|
|
@ -85,7 +85,6 @@ public abstract class AbstractFetchBuilderContainer<T extends AbstractFetchBuild
|
|||
}
|
||||
|
||||
final DynamicFetchBuilderStandard fetchBuilder = new DynamicFetchBuilderStandard(
|
||||
this,
|
||||
propertyName
|
||||
);
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
|
|||
private final Map<String, FetchBuilder> fetchBuilderMap;
|
||||
private final DynamicResultBuilderEntityStandard resultBuilderEntity;
|
||||
|
||||
private LockMode lockMode;
|
||||
|
||||
public DynamicFetchBuilderLegacy(
|
||||
String tableAlias,
|
||||
String ownerTableAlias,
|
||||
|
@ -164,41 +166,41 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
|
|||
keyDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
|
||||
}
|
||||
|
||||
keyDescriptor.forEachSelectable(
|
||||
(selectionIndex, selectableMapping) -> {
|
||||
resolveSqlSelection(
|
||||
columnNames.get( selectionIndex ),
|
||||
createColumnReferenceKey(
|
||||
tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ),
|
||||
selectableMapping.getSelectionExpression()
|
||||
),
|
||||
selectableMapping.getJdbcMapping(),
|
||||
jdbcResultsMetadata,
|
||||
domainResultCreationState
|
||||
);
|
||||
}
|
||||
);
|
||||
if ( !columnNames.isEmpty() ) {
|
||||
keyDescriptor.forEachSelectable(
|
||||
(selectionIndex, selectableMapping) -> {
|
||||
resolveSqlSelection(
|
||||
columnNames.get( selectionIndex ),
|
||||
createColumnReferenceKey(
|
||||
tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ),
|
||||
selectableMapping.getSelectionExpression()
|
||||
),
|
||||
selectableMapping.getJdbcMapping(),
|
||||
jdbcResultsMetadata,
|
||||
domainResultCreationState
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// We process the fetch builder such that it contains a resultBuilderEntity before calling this method in ResultSetMappingProcessor
|
||||
assert resultBuilderEntity != null;
|
||||
|
||||
return resultBuilderEntity.buildFetch(
|
||||
parent,
|
||||
attributeMapping,
|
||||
jdbcResultsMetadata,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
else {
|
||||
return parent.generateFetchableFetch(
|
||||
attributeMapping,
|
||||
fetchPath,
|
||||
FetchTiming.IMMEDIATE,
|
||||
true,
|
||||
null,
|
||||
domainResultCreationState
|
||||
);
|
||||
if ( resultBuilderEntity != null ) {
|
||||
return resultBuilderEntity.buildFetch(
|
||||
parent,
|
||||
attributeMapping,
|
||||
jdbcResultsMetadata,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
}
|
||||
return parent.generateFetchableFetch(
|
||||
attributeMapping,
|
||||
parent.resolveNavigablePath( attributeMapping ),
|
||||
FetchTiming.IMMEDIATE,
|
||||
true,
|
||||
null,
|
||||
domainResultCreationState
|
||||
);
|
||||
}
|
||||
|
||||
private void resolveSqlSelection(
|
||||
|
@ -235,17 +237,21 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
|
|||
|
||||
@Override
|
||||
public NativeQuery.FetchReturn setLockMode(LockMode lockMode) {
|
||||
return null;
|
||||
this.lockMode = lockMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQuery.FetchReturn addProperty(String propertyName, String columnAlias) {
|
||||
return null;
|
||||
addProperty( propertyName ).addColumnAlias( columnAlias );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQuery.ReturnProperty addProperty(String propertyName) {
|
||||
return null;
|
||||
DynamicFetchBuilderStandard fetchBuilder = new DynamicFetchBuilderStandard( propertyName );
|
||||
fetchBuilderMap.put( propertyName, fetchBuilder );
|
||||
return fetchBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.engine.FetchTiming;
|
|||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.query.NativeQuery;
|
||||
import org.hibernate.query.spi.NavigablePath;
|
||||
|
@ -38,24 +39,15 @@ import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnRefere
|
|||
public class DynamicFetchBuilderStandard
|
||||
implements DynamicFetchBuilder, NativeQuery.ReturnProperty {
|
||||
|
||||
private final DynamicFetchBuilderContainer container;
|
||||
private final String fetchableName;
|
||||
|
||||
private final List<String> columnNames;
|
||||
|
||||
public DynamicFetchBuilderStandard(
|
||||
DynamicFetchBuilderContainer container,
|
||||
String fetchableName) {
|
||||
this.container = container;
|
||||
public DynamicFetchBuilderStandard(String fetchableName) {
|
||||
this.fetchableName = fetchableName;
|
||||
this.columnNames = new ArrayList<>();
|
||||
}
|
||||
|
||||
private DynamicFetchBuilderStandard(
|
||||
DynamicFetchBuilderContainer container,
|
||||
String fetchableName,
|
||||
List<String> columnNames) {
|
||||
this.container = container;
|
||||
private DynamicFetchBuilderStandard(String fetchableName, List<String> columnNames) {
|
||||
this.fetchableName = fetchableName;
|
||||
this.columnNames = columnNames;
|
||||
}
|
||||
|
@ -63,7 +55,6 @@ public class DynamicFetchBuilderStandard
|
|||
@Override
|
||||
public DynamicFetchBuilderStandard cacheKeyInstance() {
|
||||
return new DynamicFetchBuilderStandard(
|
||||
container,
|
||||
fetchableName,
|
||||
List.copyOf( columnNames )
|
||||
);
|
||||
|
@ -71,7 +62,6 @@ public class DynamicFetchBuilderStandard
|
|||
|
||||
public DynamicFetchBuilderStandard cacheKeyInstance(DynamicFetchBuilderContainer container) {
|
||||
return new DynamicFetchBuilderStandard(
|
||||
container,
|
||||
fetchableName,
|
||||
List.copyOf( columnNames )
|
||||
);
|
||||
|
@ -124,6 +114,17 @@ public class DynamicFetchBuilderStandard
|
|||
creationStateImpl
|
||||
);
|
||||
}
|
||||
else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
|
||||
attributeMapping.forEachSelectable( selectableConsumer );
|
||||
return parent.generateFetchableFetch(
|
||||
attributeMapping,
|
||||
fetchPath,
|
||||
FetchTiming.IMMEDIATE,
|
||||
false,
|
||||
null,
|
||||
creationStateImpl
|
||||
);
|
||||
}
|
||||
else if ( attributeMapping instanceof ToOneAttributeMapping ) {
|
||||
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
|
||||
toOneAttributeMapping.getForeignKeyDescriptor().visitKeySelectables( selectableConsumer );
|
||||
|
|
|
@ -69,6 +69,11 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde
|
|||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode getLockMode() {
|
||||
return explicitLockMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQuery.RootReturn setLockMode(LockMode lockMode) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -103,6 +103,11 @@ public class DynamicResultBuilderEntityStandard
|
|||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode getLockMode() {
|
||||
return lockMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQuery.RootReturn addIdColumnAliases(String... aliases) {
|
||||
if ( idColumnNames == null ) {
|
||||
|
@ -153,7 +158,7 @@ public class DynamicResultBuilderEntityStandard
|
|||
return buildResultOrFetch(
|
||||
(tableGroup) -> parent.generateFetchableFetch(
|
||||
fetchable,
|
||||
navigablePath,
|
||||
parent.resolveNavigablePath( fetchable ),
|
||||
FetchTiming.IMMEDIATE,
|
||||
true,
|
||||
null,
|
||||
|
|
|
@ -173,16 +173,17 @@ public class ParameterParser {
|
|||
}
|
||||
|
||||
private static void checkIsNotAFunctionCall(String sqlString) {
|
||||
if ( !( sqlString.startsWith( "{" ) && sqlString.endsWith( "}" ) ) ) {
|
||||
final String trimmed = sqlString.trim();
|
||||
if ( !( trimmed.startsWith( "{" ) && trimmed.endsWith( "}" ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int chopLocation = sqlString.indexOf( "call" );
|
||||
final int chopLocation = trimmed.indexOf( "call" );
|
||||
if ( chopLocation <= 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String checkString = sqlString.substring( 1, chopLocation + 4 );
|
||||
final String checkString = trimmed.substring( 1, chopLocation + 4 );
|
||||
final String fixture = "?=call";
|
||||
int fixturePosition = 0;
|
||||
boolean matches = true;
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
import org.hibernate.engine.query.ParameterRecognitionException;
|
||||
import org.hibernate.query.internal.QueryParameterNamedImpl;
|
||||
import org.hibernate.query.internal.QueryParameterPositionalImpl;
|
||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||
|
@ -125,7 +126,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
|
|||
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;
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
|
@ -233,6 +234,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
|
|||
alias2Persister.get( fetchBuilder.getTableAlias() ).findContainingEntityMapping(),
|
||||
fetchBuilder.getTableAlias(),
|
||||
suffix,
|
||||
null,
|
||||
determineNavigablePath( fetchBuilder )
|
||||
);
|
||||
final SQLLoadable loadable = (SQLLoadable) alias2Persister.get( fetchBuilder.getOwnerAlias() );
|
||||
|
@ -296,6 +298,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
|
|||
rootReturn.getEntityMapping(),
|
||||
rootReturn.getTableAlias(),
|
||||
suffix,
|
||||
rootReturn.getLockMode(),
|
||||
new NavigablePath( rootReturn.getEntityMapping().getEntityName() )
|
||||
);
|
||||
}
|
||||
|
@ -304,6 +307,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
|
|||
EntityMappingType entityMapping,
|
||||
String tableAlias,
|
||||
String suffix,
|
||||
LockMode lockMode,
|
||||
NavigablePath navigablePath) {
|
||||
final SQLLoadable loadable = (SQLLoadable) entityMapping.getEntityPersister();
|
||||
final DynamicResultBuilderEntityStandard resultBuilderEntity = new DynamicResultBuilderEntityStandard(
|
||||
|
@ -311,6 +315,7 @@ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext {
|
|||
tableAlias,
|
||||
navigablePath
|
||||
);
|
||||
resultBuilderEntity.setLockMode( lockMode );
|
||||
|
||||
final String[] identifierAliases = loadable.getIdentifierAliases( suffix );
|
||||
resultBuilderEntity.addIdColumnAliases( identifierAliases );
|
||||
|
|
|
@ -49,6 +49,11 @@ public class JdbcCallParameterRegistrationImpl implements JdbcCallParameterRegis
|
|||
this.refCursorExtractor = refCursorExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcParameterBinder getParameterBinder() {
|
||||
return parameterBinder;
|
||||
|
|
|
@ -18,6 +18,9 @@ import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface JdbcCallParameterRegistration {
|
||||
|
||||
String getName();
|
||||
|
||||
ParameterMode getParameterMode();
|
||||
|
||||
void registerParameter(
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
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.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -124,6 +125,13 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
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;
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -44,7 +44,6 @@ import static org.hibernate.jpa.HibernateHints.HINT_CALLABLE_FUNCTION;
|
|||
@NamedNativeQuery(
|
||||
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( ? )",
|
||||
callable = false,
|
||||
resultSetMapping = "person_with_phones_hana"
|
||||
)
|
||||
@SqlResultSetMappings({
|
||||
|
|
Loading…
Reference in New Issue