HHH-9548 - Allow propagation of NULL for stored-procedure argument parameters to database
This commit is contained in:
parent
90b7f9e07c
commit
a5e65834a1
|
@ -81,6 +81,7 @@ import static org.hibernate.cfg.AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLV
|
|||
import static org.hibernate.cfg.AvailableSettings.ORDER_INSERTS;
|
||||
import static org.hibernate.cfg.AvailableSettings.ORDER_UPDATES;
|
||||
import static org.hibernate.cfg.AvailableSettings.PREFER_USER_TRANSACTION;
|
||||
import static org.hibernate.cfg.AvailableSettings.PROCEDURE_NULL_PARAM_PASSING;
|
||||
import static org.hibernate.cfg.AvailableSettings.QUERY_CACHE_FACTORY;
|
||||
import static org.hibernate.cfg.AvailableSettings.QUERY_STARTUP_CHECKING;
|
||||
import static org.hibernate.cfg.AvailableSettings.QUERY_SUBSTITUTIONS;
|
||||
|
@ -525,6 +526,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
private Map querySubstitutions;
|
||||
private boolean strictJpaQueryLanguageCompliance;
|
||||
private boolean namedQueryStartupCheckingEnabled;
|
||||
private final boolean procedureParameterNullPassingEnabled;
|
||||
|
||||
// Caching
|
||||
private boolean secondLevelCacheEnabled;
|
||||
|
@ -640,6 +642,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
this.querySubstitutions = ConfigurationHelper.toMap( QUERY_SUBSTITUTIONS, " ,=;:\n\t\r\f", configurationSettings );
|
||||
this.strictJpaQueryLanguageCompliance = cfgService.getSetting( JPAQL_STRICT_COMPLIANCE, BOOLEAN, false );
|
||||
this.namedQueryStartupCheckingEnabled = cfgService.getSetting( QUERY_STARTUP_CHECKING, BOOLEAN, true );
|
||||
this.procedureParameterNullPassingEnabled = cfgService.getSetting( PROCEDURE_NULL_PARAM_PASSING, BOOLEAN, false );
|
||||
|
||||
this.secondLevelCacheEnabled = cfgService.getSetting( USE_SECOND_LEVEL_CACHE, BOOLEAN, true );
|
||||
this.queryCacheEnabled = cfgService.getSetting( USE_QUERY_CACHE, BOOLEAN, false );
|
||||
|
@ -889,6 +892,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
return namedQueryStartupCheckingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProcedureParameterNullPassingEnabled() {
|
||||
return procedureParameterNullPassingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecondLevelCacheEnabled() {
|
||||
return secondLevelCacheEnabled;
|
||||
|
@ -1160,6 +1168,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
return options.isNamedQueryStartupCheckingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProcedureParameterNullPassingEnabled() {
|
||||
return options.isProcedureParameterNullPassingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecondLevelCacheEnabled() {
|
||||
return options.isSecondLevelCacheEnabled();
|
||||
|
|
|
@ -89,6 +89,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
|||
private final Map querySubstitutions;
|
||||
private final boolean strictJpaQueryLanguageCompliance;
|
||||
private final boolean namedQueryStartupCheckingEnabled;
|
||||
private final boolean procedureParameterNullPassingEnabled;
|
||||
|
||||
// Caching
|
||||
private final boolean secondLevelCacheEnabled;
|
||||
|
@ -159,6 +160,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
|||
this.querySubstitutions = state.getQuerySubstitutions();
|
||||
this.strictJpaQueryLanguageCompliance = state.isStrictJpaQueryLanguageCompliance();
|
||||
this.namedQueryStartupCheckingEnabled = state.isNamedQueryStartupCheckingEnabled();
|
||||
this.procedureParameterNullPassingEnabled = state.isProcedureParameterNullPassingEnabled();
|
||||
|
||||
this.secondLevelCacheEnabled = state.isSecondLevelCacheEnabled();
|
||||
this.queryCacheEnabled = state.isQueryCacheEnabled();
|
||||
|
@ -336,6 +338,11 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
|||
return namedQueryStartupCheckingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProcedureParameterNullPassingEnabled() {
|
||||
return procedureParameterNullPassingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecondLevelCacheEnabled() {
|
||||
return secondLevelCacheEnabled;
|
||||
|
|
|
@ -37,97 +37,99 @@ import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface SessionFactoryOptionsState {
|
||||
public StandardServiceRegistry getServiceRegistry();
|
||||
StandardServiceRegistry getServiceRegistry();
|
||||
|
||||
public Object getBeanManagerReference();
|
||||
Object getBeanManagerReference();
|
||||
|
||||
public Object getValidatorFactoryReference();
|
||||
Object getValidatorFactoryReference();
|
||||
|
||||
public String getSessionFactoryName();
|
||||
String getSessionFactoryName();
|
||||
|
||||
public boolean isSessionFactoryNameAlsoJndiName();
|
||||
boolean isSessionFactoryNameAlsoJndiName();
|
||||
|
||||
public boolean isFlushBeforeCompletionEnabled();
|
||||
boolean isFlushBeforeCompletionEnabled();
|
||||
|
||||
public boolean isAutoCloseSessionEnabled();
|
||||
boolean isAutoCloseSessionEnabled();
|
||||
|
||||
public boolean isStatisticsEnabled();
|
||||
boolean isStatisticsEnabled();
|
||||
|
||||
public Interceptor getInterceptor();
|
||||
Interceptor getInterceptor();
|
||||
|
||||
public StatementInspector getStatementInspector();
|
||||
StatementInspector getStatementInspector();
|
||||
|
||||
public SessionFactoryObserver[] getSessionFactoryObservers();
|
||||
SessionFactoryObserver[] getSessionFactoryObservers();
|
||||
|
||||
public BaselineSessionEventsListenerBuilder getBaselineSessionEventsListenerBuilder();
|
||||
BaselineSessionEventsListenerBuilder getBaselineSessionEventsListenerBuilder();
|
||||
|
||||
public boolean isIdentifierRollbackEnabled();
|
||||
boolean isIdentifierRollbackEnabled();
|
||||
|
||||
public EntityMode getDefaultEntityMode();
|
||||
EntityMode getDefaultEntityMode();
|
||||
|
||||
public EntityTuplizerFactory getEntityTuplizerFactory();
|
||||
EntityTuplizerFactory getEntityTuplizerFactory();
|
||||
|
||||
public boolean isCheckNullability();
|
||||
boolean isCheckNullability();
|
||||
|
||||
public boolean isInitializeLazyStateOutsideTransactionsEnabled();
|
||||
boolean isInitializeLazyStateOutsideTransactionsEnabled();
|
||||
|
||||
public MultiTableBulkIdStrategy getMultiTableBulkIdStrategy();
|
||||
MultiTableBulkIdStrategy getMultiTableBulkIdStrategy();
|
||||
|
||||
public TempTableDdlTransactionHandling getTempTableDdlTransactionHandling();
|
||||
TempTableDdlTransactionHandling getTempTableDdlTransactionHandling();
|
||||
|
||||
public BatchFetchStyle getBatchFetchStyle();
|
||||
BatchFetchStyle getBatchFetchStyle();
|
||||
|
||||
public int getDefaultBatchFetchSize();
|
||||
int getDefaultBatchFetchSize();
|
||||
|
||||
public Integer getMaximumFetchDepth();
|
||||
Integer getMaximumFetchDepth();
|
||||
|
||||
public NullPrecedence getDefaultNullPrecedence();
|
||||
NullPrecedence getDefaultNullPrecedence();
|
||||
|
||||
public boolean isOrderUpdatesEnabled();
|
||||
boolean isOrderUpdatesEnabled();
|
||||
|
||||
public boolean isOrderInsertsEnabled();
|
||||
boolean isOrderInsertsEnabled();
|
||||
|
||||
public MultiTenancyStrategy getMultiTenancyStrategy();
|
||||
MultiTenancyStrategy getMultiTenancyStrategy();
|
||||
|
||||
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver();
|
||||
CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver();
|
||||
|
||||
public boolean isJtaTrackByThread();
|
||||
boolean isJtaTrackByThread();
|
||||
|
||||
public Map getQuerySubstitutions();
|
||||
Map getQuerySubstitutions();
|
||||
|
||||
public boolean isStrictJpaQueryLanguageCompliance();
|
||||
boolean isStrictJpaQueryLanguageCompliance();
|
||||
|
||||
public boolean isNamedQueryStartupCheckingEnabled();
|
||||
boolean isNamedQueryStartupCheckingEnabled();
|
||||
|
||||
public boolean isSecondLevelCacheEnabled();
|
||||
boolean isProcedureParameterNullPassingEnabled();
|
||||
|
||||
public boolean isQueryCacheEnabled();
|
||||
boolean isSecondLevelCacheEnabled();
|
||||
|
||||
public QueryCacheFactory getQueryCacheFactory();
|
||||
boolean isQueryCacheEnabled();
|
||||
|
||||
public String getCacheRegionPrefix();
|
||||
QueryCacheFactory getQueryCacheFactory();
|
||||
|
||||
public boolean isMinimalPutsEnabled();
|
||||
String getCacheRegionPrefix();
|
||||
|
||||
public boolean isStructuredCacheEntriesEnabled();
|
||||
boolean isMinimalPutsEnabled();
|
||||
|
||||
public boolean isDirectReferenceCacheEntriesEnabled();
|
||||
boolean isStructuredCacheEntriesEnabled();
|
||||
|
||||
public boolean isAutoEvictCollectionCache();
|
||||
boolean isDirectReferenceCacheEntriesEnabled();
|
||||
|
||||
public SchemaAutoTooling getSchemaAutoTooling();
|
||||
boolean isAutoEvictCollectionCache();
|
||||
|
||||
public int getJdbcBatchSize();
|
||||
SchemaAutoTooling getSchemaAutoTooling();
|
||||
|
||||
public boolean isJdbcBatchVersionedData();
|
||||
int getJdbcBatchSize();
|
||||
|
||||
public boolean isScrollableResultSetsEnabled();
|
||||
boolean isJdbcBatchVersionedData();
|
||||
|
||||
public boolean isWrapResultSetsEnabled();
|
||||
boolean isScrollableResultSetsEnabled();
|
||||
|
||||
public boolean isGetGeneratedKeysEnabled();
|
||||
boolean isWrapResultSetsEnabled();
|
||||
|
||||
public Integer getJdbcFetchSize();
|
||||
boolean isGetGeneratedKeysEnabled();
|
||||
|
||||
Integer getJdbcFetchSize();
|
||||
|
||||
PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode();
|
||||
|
||||
|
@ -137,15 +139,16 @@ public interface SessionFactoryOptionsState {
|
|||
@Deprecated
|
||||
ConnectionReleaseMode getConnectionReleaseMode();
|
||||
|
||||
public boolean isCommentsEnabled();
|
||||
boolean isCommentsEnabled();
|
||||
|
||||
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy();
|
||||
CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy();
|
||||
|
||||
public EntityNameResolver[] getEntityNameResolvers();
|
||||
EntityNameResolver[] getEntityNameResolvers();
|
||||
|
||||
public EntityNotFoundDelegate getEntityNotFoundDelegate();
|
||||
EntityNotFoundDelegate getEntityNotFoundDelegate();
|
||||
|
||||
public Map<String, SQLFunction> getCustomSqlFunctionMap();
|
||||
Map<String, SQLFunction> getCustomSqlFunctionMap();
|
||||
|
||||
boolean isPreferUserTransaction();
|
||||
|
||||
public boolean isPreferUserTransaction();
|
||||
}
|
||||
|
|
|
@ -198,6 +198,11 @@ public abstract class AbstractDelegatingSessionFactoryOptions implements Session
|
|||
return delegate.isNamedQueryStartupCheckingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProcedureParameterNullPassingEnabled() {
|
||||
return delegate.isProcedureParameterNullPassingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecondLevelCacheEnabled() {
|
||||
return delegate.isSecondLevelCacheEnabled();
|
||||
|
|
|
@ -42,11 +42,11 @@ public interface SessionFactoryOptions {
|
|||
*
|
||||
* @return The service registry to use.
|
||||
*/
|
||||
public StandardServiceRegistry getServiceRegistry();
|
||||
StandardServiceRegistry getServiceRegistry();
|
||||
|
||||
public Object getBeanManagerReference();
|
||||
Object getBeanManagerReference();
|
||||
|
||||
public Object getValidatorFactoryReference();
|
||||
Object getValidatorFactoryReference();
|
||||
|
||||
/**
|
||||
* The name to be used for the SessionFactory. This is use both in:<ul>
|
||||
|
@ -56,7 +56,7 @@ public interface SessionFactoryOptions {
|
|||
*
|
||||
* @return The SessionFactory name
|
||||
*/
|
||||
public String getSessionFactoryName();
|
||||
String getSessionFactoryName();
|
||||
|
||||
/**
|
||||
* Is the {@link #getSessionFactoryName SesssionFactory name} also a JNDI name, indicating we
|
||||
|
@ -64,94 +64,94 @@ public interface SessionFactoryOptions {
|
|||
*
|
||||
* @return {@code true} if the SessionFactory name is also a JNDI name; {@code false} otherwise.
|
||||
*/
|
||||
public boolean isSessionFactoryNameAlsoJndiName();
|
||||
boolean isSessionFactoryNameAlsoJndiName();
|
||||
|
||||
public boolean isFlushBeforeCompletionEnabled();
|
||||
boolean isFlushBeforeCompletionEnabled();
|
||||
|
||||
public boolean isAutoCloseSessionEnabled();
|
||||
boolean isAutoCloseSessionEnabled();
|
||||
|
||||
public boolean isStatisticsEnabled();
|
||||
boolean isStatisticsEnabled();
|
||||
|
||||
/**
|
||||
* Get the interceptor to use by default for all sessions opened from this factory.
|
||||
*
|
||||
* @return The interceptor to use factory wide. May be {@code null}
|
||||
*/
|
||||
public Interceptor getInterceptor();
|
||||
Interceptor getInterceptor();
|
||||
|
||||
public StatementInspector getStatementInspector();
|
||||
StatementInspector getStatementInspector();
|
||||
|
||||
public SessionFactoryObserver[] getSessionFactoryObservers();
|
||||
SessionFactoryObserver[] getSessionFactoryObservers();
|
||||
|
||||
public BaselineSessionEventsListenerBuilder getBaselineSessionEventsListenerBuilder();
|
||||
BaselineSessionEventsListenerBuilder getBaselineSessionEventsListenerBuilder();
|
||||
|
||||
public boolean isIdentifierRollbackEnabled();
|
||||
boolean isIdentifierRollbackEnabled();
|
||||
|
||||
public EntityMode getDefaultEntityMode();
|
||||
EntityMode getDefaultEntityMode();
|
||||
|
||||
public EntityTuplizerFactory getEntityTuplizerFactory();
|
||||
EntityTuplizerFactory getEntityTuplizerFactory();
|
||||
|
||||
public boolean isCheckNullability();
|
||||
boolean isCheckNullability();
|
||||
|
||||
public boolean isInitializeLazyStateOutsideTransactionsEnabled();
|
||||
boolean isInitializeLazyStateOutsideTransactionsEnabled();
|
||||
|
||||
public MultiTableBulkIdStrategy getMultiTableBulkIdStrategy();
|
||||
MultiTableBulkIdStrategy getMultiTableBulkIdStrategy();
|
||||
|
||||
public TempTableDdlTransactionHandling getTempTableDdlTransactionHandling();
|
||||
TempTableDdlTransactionHandling getTempTableDdlTransactionHandling();
|
||||
|
||||
public BatchFetchStyle getBatchFetchStyle();
|
||||
BatchFetchStyle getBatchFetchStyle();
|
||||
|
||||
public int getDefaultBatchFetchSize();
|
||||
int getDefaultBatchFetchSize();
|
||||
|
||||
public Integer getMaximumFetchDepth();
|
||||
Integer getMaximumFetchDepth();
|
||||
|
||||
public NullPrecedence getDefaultNullPrecedence();
|
||||
NullPrecedence getDefaultNullPrecedence();
|
||||
|
||||
public boolean isOrderUpdatesEnabled();
|
||||
boolean isOrderUpdatesEnabled();
|
||||
|
||||
public boolean isOrderInsertsEnabled();
|
||||
boolean isOrderInsertsEnabled();
|
||||
|
||||
public MultiTenancyStrategy getMultiTenancyStrategy();
|
||||
MultiTenancyStrategy getMultiTenancyStrategy();
|
||||
|
||||
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver();
|
||||
CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver();
|
||||
|
||||
public boolean isJtaTrackByThread();
|
||||
boolean isJtaTrackByThread();
|
||||
|
||||
public Map getQuerySubstitutions();
|
||||
Map getQuerySubstitutions();
|
||||
|
||||
public boolean isStrictJpaQueryLanguageCompliance();
|
||||
boolean isStrictJpaQueryLanguageCompliance();
|
||||
|
||||
public boolean isNamedQueryStartupCheckingEnabled();
|
||||
boolean isNamedQueryStartupCheckingEnabled();
|
||||
|
||||
public boolean isSecondLevelCacheEnabled();
|
||||
boolean isSecondLevelCacheEnabled();
|
||||
|
||||
public boolean isQueryCacheEnabled();
|
||||
boolean isQueryCacheEnabled();
|
||||
|
||||
public QueryCacheFactory getQueryCacheFactory();
|
||||
QueryCacheFactory getQueryCacheFactory();
|
||||
|
||||
public String getCacheRegionPrefix();
|
||||
String getCacheRegionPrefix();
|
||||
|
||||
public boolean isMinimalPutsEnabled();
|
||||
boolean isMinimalPutsEnabled();
|
||||
|
||||
public boolean isStructuredCacheEntriesEnabled();
|
||||
boolean isStructuredCacheEntriesEnabled();
|
||||
|
||||
public boolean isDirectReferenceCacheEntriesEnabled();
|
||||
boolean isDirectReferenceCacheEntriesEnabled();
|
||||
|
||||
public boolean isAutoEvictCollectionCache();
|
||||
boolean isAutoEvictCollectionCache();
|
||||
|
||||
public SchemaAutoTooling getSchemaAutoTooling();
|
||||
SchemaAutoTooling getSchemaAutoTooling();
|
||||
|
||||
public int getJdbcBatchSize();
|
||||
int getJdbcBatchSize();
|
||||
|
||||
public boolean isJdbcBatchVersionedData();
|
||||
boolean isJdbcBatchVersionedData();
|
||||
|
||||
public boolean isScrollableResultSetsEnabled();
|
||||
boolean isScrollableResultSetsEnabled();
|
||||
|
||||
public boolean isWrapResultSetsEnabled();
|
||||
boolean isWrapResultSetsEnabled();
|
||||
|
||||
public boolean isGetGeneratedKeysEnabled();
|
||||
boolean isGetGeneratedKeysEnabled();
|
||||
|
||||
public Integer getJdbcFetchSize();
|
||||
Integer getJdbcFetchSize();
|
||||
|
||||
PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode();
|
||||
|
||||
|
@ -161,22 +161,24 @@ public interface SessionFactoryOptions {
|
|||
@Deprecated
|
||||
ConnectionReleaseMode getConnectionReleaseMode();
|
||||
|
||||
public boolean isCommentsEnabled();
|
||||
boolean isCommentsEnabled();
|
||||
|
||||
|
||||
public CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy();
|
||||
public EntityNameResolver[] getEntityNameResolvers();
|
||||
CustomEntityDirtinessStrategy getCustomEntityDirtinessStrategy();
|
||||
EntityNameResolver[] getEntityNameResolvers();
|
||||
|
||||
/**
|
||||
* Get the delegate for handling entity-not-found exception conditions.
|
||||
*
|
||||
* @return The specific EntityNotFoundDelegate to use, May be {@code null}
|
||||
*/
|
||||
public EntityNotFoundDelegate getEntityNotFoundDelegate();
|
||||
EntityNotFoundDelegate getEntityNotFoundDelegate();
|
||||
|
||||
public Map<String, SQLFunction> getCustomSqlFunctionMap();
|
||||
Map<String, SQLFunction> getCustomSqlFunctionMap();
|
||||
|
||||
void setCheckNullability(boolean enabled);
|
||||
|
||||
public boolean isPreferUserTransaction();
|
||||
boolean isPreferUserTransaction();
|
||||
|
||||
boolean isProcedureParameterNullPassingEnabled();
|
||||
}
|
||||
|
|
|
@ -997,4 +997,16 @@ public interface AvailableSettings {
|
|||
*/
|
||||
String AUTO_SESSION_EVENTS_LISTENER = "hibernate.session.events.auto";
|
||||
|
||||
/**
|
||||
* Global setting for whether NULL parameter bindings should be passed to database
|
||||
* procedure/function calls as part of {@link org.hibernate.procedure.ProcedureCall}
|
||||
* handling. Implicitly Hibernate will not pass the NULL, the intention being to allow
|
||||
* any default argumnet values to be applied.
|
||||
* <p/>
|
||||
* This defines a global setting, which can them be controlled per parameter via
|
||||
* {@link org.hibernate.procedure.ParameterRegistration#enablePassingNulls(boolean)}
|
||||
* <p/>
|
||||
* Values are {@code true} (pass the NULLs) or {@code false} (do not pass the NULLs).
|
||||
*/
|
||||
String PROCEDURE_NULL_PARAM_PASSING = "hibernate.proc.param_null_passing";
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import javax.persistence.ParameterMode;
|
|||
import javax.persistence.StoredProcedureParameter;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.ResultSetMappingDefinition;
|
||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -48,10 +49,11 @@ public class NamedProcedureCallDefinition {
|
|||
NamedProcedureCallDefinition(NamedStoredProcedureQuery annotation) {
|
||||
this.registeredName = annotation.name();
|
||||
this.procedureName = annotation.procedureName();
|
||||
this.hints = new QueryHintDefinition( annotation.hints() ).getHintsMap();
|
||||
this.resultClasses = annotation.resultClasses();
|
||||
this.resultSetMappings = annotation.resultSetMappings();
|
||||
this.parameterDefinitions = new ParameterDefinitions( annotation.parameters() );
|
||||
this.hints = new QueryHintDefinition( annotation.hints() ).getHintsMap();
|
||||
|
||||
this.parameterDefinitions = new ParameterDefinitions( annotation.parameters(), hints );
|
||||
|
||||
final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0;
|
||||
final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0;
|
||||
|
@ -145,7 +147,7 @@ public class NamedProcedureCallDefinition {
|
|||
private final ParameterStrategy parameterStrategy;
|
||||
private final ParameterDefinition[] parameterDefinitions;
|
||||
|
||||
ParameterDefinitions(StoredProcedureParameter[] parameters) {
|
||||
ParameterDefinitions(StoredProcedureParameter[] parameters, Map<String, Object> queryHintMap) {
|
||||
if ( parameters == null || parameters.length == 0 ) {
|
||||
parameterStrategy = ParameterStrategy.POSITIONAL;
|
||||
parameterDefinitions = new ParameterDefinition[0];
|
||||
|
@ -155,9 +157,15 @@ public class NamedProcedureCallDefinition {
|
|||
? ParameterStrategy.NAMED
|
||||
: ParameterStrategy.POSITIONAL;
|
||||
parameterDefinitions = new ParameterDefinition[ parameters.length ];
|
||||
|
||||
for ( int i = 0; i < parameters.length; i++ ) {
|
||||
// i+1 for the position because the apis say the numbers are 1-based, not zero
|
||||
parameterDefinitions[i] = new ParameterDefinition( i+1, parameters[i] );
|
||||
parameterDefinitions[i] = ParameterDefinition.from(
|
||||
parameterStrategy,
|
||||
parameters[i],
|
||||
// i+1 for the position because the apis say the numbers are 1-based, not zero
|
||||
i+1,
|
||||
queryHintMap
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,21 +188,62 @@ public class NamedProcedureCallDefinition {
|
|||
private final String name;
|
||||
private final ParameterMode parameterMode;
|
||||
private final Class type;
|
||||
private final Boolean explicitPassNullSetting;
|
||||
|
||||
ParameterDefinition(int position, StoredProcedureParameter annotation) {
|
||||
static ParameterDefinition from(
|
||||
ParameterStrategy parameterStrategy,
|
||||
StoredProcedureParameter parameterAnnotation,
|
||||
int adjustedPosition,
|
||||
Map<String, Object> queryHintMap) {
|
||||
// see if there was an explicit hint for this parameter in regards to NULL passing
|
||||
final Object explicitNullPassingHint;
|
||||
if ( parameterStrategy == ParameterStrategy.NAMED ) {
|
||||
explicitNullPassingHint = queryHintMap.get( AvailableSettings.PROCEDURE_NULL_PARAM_PASSING + '.' + parameterAnnotation.name() );
|
||||
}
|
||||
else {
|
||||
explicitNullPassingHint = queryHintMap.get( AvailableSettings.PROCEDURE_NULL_PARAM_PASSING + '.' + adjustedPosition );
|
||||
}
|
||||
|
||||
return new ParameterDefinition(
|
||||
adjustedPosition,
|
||||
parameterAnnotation,
|
||||
interpretBoolean( explicitNullPassingHint )
|
||||
);
|
||||
}
|
||||
|
||||
private static Boolean interpretBoolean(Object value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof Boolean ) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
return Boolean.valueOf( value.toString() );
|
||||
}
|
||||
|
||||
ParameterDefinition(int position, StoredProcedureParameter annotation, Boolean explicitPassNullSetting) {
|
||||
this.position = position;
|
||||
this.name = normalize( annotation.name() );
|
||||
this.parameterMode = annotation.mode();
|
||||
this.type = annotation.type();
|
||||
this.explicitPassNullSetting = explicitPassNullSetting;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnnecessaryUnboxing")
|
||||
public ParameterMemento toMemento(SessionFactoryImpl sessionFactory) {
|
||||
final boolean initialPassNullSetting = explicitPassNullSetting != null
|
||||
? explicitPassNullSetting.booleanValue()
|
||||
: sessionFactory.getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
|
||||
|
||||
return new ParameterMemento(
|
||||
position,
|
||||
name,
|
||||
parameterMode,
|
||||
type,
|
||||
sessionFactory.getTypeResolver().heuristicType( type.getName() )
|
||||
sessionFactory.getTypeResolver().heuristicType( type.getName() ),
|
||||
initialPassNullSetting
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import javax.persistence.TemporalType;
|
|||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Describes a registered procedure/function parameter.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ParameterRegistration<T> {
|
||||
|
@ -21,7 +23,7 @@ public interface ParameterRegistration<T> {
|
|||
*
|
||||
* @return The name;
|
||||
*/
|
||||
public String getName();
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The position at which this parameter was registered. Can be {@code null} which should indicate that
|
||||
|
@ -29,7 +31,7 @@ public interface ParameterRegistration<T> {
|
|||
*
|
||||
* @return The name;
|
||||
*/
|
||||
public Integer getPosition();
|
||||
Integer getPosition();
|
||||
|
||||
/**
|
||||
* Obtain the Java type of parameter. This is used to guess the Hibernate type (unless {@link #setHibernateType}
|
||||
|
@ -37,7 +39,7 @@ public interface ParameterRegistration<T> {
|
|||
*
|
||||
* @return The parameter Java type.
|
||||
*/
|
||||
public Class<T> getType();
|
||||
Class<T> getType();
|
||||
|
||||
/**
|
||||
* Retrieves the parameter "mode" which describes how the parameter is defined in the actual database procedure
|
||||
|
@ -45,14 +47,32 @@ public interface ParameterRegistration<T> {
|
|||
*
|
||||
* @return The parameter mode.
|
||||
*/
|
||||
public ParameterMode getMode();
|
||||
ParameterMode getMode();
|
||||
|
||||
/**
|
||||
* Controls how unbound values for this IN/INOUT parameter registration will be handled prior to
|
||||
* execution. There are 2 possible options to handle it:<ul>
|
||||
* <li>bind the NULL to the parameter</li>
|
||||
* <li>do not bind the NULL to the parameter</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* The reason for the distinction comes from default values defined on the corresponding
|
||||
* database procedure/function argument. Any time a value (including NULL) is bound to the
|
||||
* argument, its default value will not be used. So effectively this setting controls
|
||||
* whether the NULL should be interpreted as "pass the NULL" or as "apply the argument default".
|
||||
* <p/>
|
||||
* The (global) default this setting is defined by {@link org.hibernate.cfg.AvailableSettings#PROCEDURE_NULL_PARAM_PASSING}
|
||||
*
|
||||
* @param enabled {@code true} indicates that the NULL should be passed; {@code false} indicates it should not.
|
||||
*/
|
||||
void enablePassingNulls(boolean enabled);
|
||||
|
||||
/**
|
||||
* Set the Hibernate mapping type for this parameter.
|
||||
*
|
||||
* @param type The Hibernate mapping type.
|
||||
*/
|
||||
public void setHibernateType(Type type);
|
||||
void setHibernateType(Type type);
|
||||
|
||||
/**
|
||||
* Retrieve the binding associated with this parameter. The binding is only relevant for INPUT parameters. Can
|
||||
|
@ -61,7 +81,7 @@ public interface ParameterRegistration<T> {
|
|||
*
|
||||
* @return The parameter binding
|
||||
*/
|
||||
public ParameterBind<T> getBind();
|
||||
ParameterBind<T> getBind();
|
||||
|
||||
/**
|
||||
* Bind a value to the parameter. How this value is bound to the underlying JDBC CallableStatement is
|
||||
|
@ -69,7 +89,7 @@ public interface ParameterRegistration<T> {
|
|||
*
|
||||
* @param value The value to bind.
|
||||
*/
|
||||
public void bindValue(T value);
|
||||
void bindValue(T value);
|
||||
|
||||
/**
|
||||
* Bind a value to the parameter, using just a specified portion of the DATE/TIME value. It is illegal to call
|
||||
|
@ -79,5 +99,5 @@ public interface ParameterRegistration<T> {
|
|||
* @param value The value to bind
|
||||
* @param explicitTemporalType An explicitly supplied TemporalType.
|
||||
*/
|
||||
public void bindValue(T value, TemporalType explicitTemporalType);
|
||||
void bindValue(T value, TemporalType explicitTemporalType);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
private final Class<T> type;
|
||||
|
||||
private ParameterBindImpl bind;
|
||||
private boolean passNulls;
|
||||
|
||||
private int startIndex;
|
||||
private Type hibernateType;
|
||||
|
@ -56,8 +57,9 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
ProcedureCallImpl procedureCall,
|
||||
Integer position,
|
||||
ParameterMode mode,
|
||||
Class<T> type) {
|
||||
this( procedureCall, position, null, mode, type );
|
||||
Class<T> type,
|
||||
boolean initialPassNullsSetting) {
|
||||
this( procedureCall, position, null, mode, type, initialPassNullsSetting );
|
||||
}
|
||||
|
||||
protected AbstractParameterRegistrationImpl(
|
||||
|
@ -65,8 +67,9 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
Integer position,
|
||||
ParameterMode mode,
|
||||
Class<T> type,
|
||||
Type hibernateType) {
|
||||
this( procedureCall, position, null, mode, type, hibernateType );
|
||||
Type hibernateType,
|
||||
boolean initialPassNullsSetting) {
|
||||
this( procedureCall, position, null, mode, type, hibernateType, initialPassNullsSetting );
|
||||
}
|
||||
|
||||
|
||||
|
@ -76,8 +79,9 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
ProcedureCallImpl procedureCall,
|
||||
String name,
|
||||
ParameterMode mode,
|
||||
Class<T> type) {
|
||||
this( procedureCall, null, name, mode, type );
|
||||
Class<T> type,
|
||||
boolean initialPassNullsSetting) {
|
||||
this( procedureCall, null, name, mode, type, initialPassNullsSetting );
|
||||
}
|
||||
|
||||
protected AbstractParameterRegistrationImpl(
|
||||
|
@ -85,8 +89,9 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
String name,
|
||||
ParameterMode mode,
|
||||
Class<T> type,
|
||||
Type hibernateType) {
|
||||
this( procedureCall, null, name, mode, type, hibernateType );
|
||||
Type hibernateType,
|
||||
boolean initialPassNullsSetting) {
|
||||
this( procedureCall, null, name, mode, type, hibernateType, initialPassNullsSetting );
|
||||
}
|
||||
|
||||
|
||||
|
@ -98,7 +103,8 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
String name,
|
||||
ParameterMode mode,
|
||||
Class<T> type,
|
||||
Type hibernateType) {
|
||||
Type hibernateType,
|
||||
boolean initialPassNullsSetting) {
|
||||
this.procedureCall = procedureCall;
|
||||
|
||||
this.position = position;
|
||||
|
@ -111,6 +117,7 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
return;
|
||||
}
|
||||
|
||||
this.passNulls = initialPassNullsSetting;
|
||||
setHibernateType( hibernateType );
|
||||
}
|
||||
|
||||
|
@ -119,14 +126,16 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
Integer position,
|
||||
String name,
|
||||
ParameterMode mode,
|
||||
Class<T> type) {
|
||||
Class<T> type,
|
||||
boolean initialPassNullsSetting) {
|
||||
this(
|
||||
procedureCall,
|
||||
position,
|
||||
name,
|
||||
mode,
|
||||
type,
|
||||
procedureCall.getSession().getFactory().getTypeResolver().heuristicType( type.getName() )
|
||||
procedureCall.getSession().getFactory().getTypeResolver().heuristicType( type.getName() ),
|
||||
initialPassNullsSetting
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -154,6 +163,16 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
return mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPassNullsEnabled() {
|
||||
return passNulls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enablePassingNulls(boolean enabled) {
|
||||
this.passNulls = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getHibernateType() {
|
||||
return hibernateType;
|
||||
|
@ -258,17 +277,28 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
|
|||
|
||||
if ( mode == ParameterMode.INOUT || mode == ParameterMode.IN ) {
|
||||
if ( bind == null || bind.getValue() == null ) {
|
||||
// the user did not bind a value to the parameter being processed. That might be ok *if* the
|
||||
// procedure as defined in the database defines a default value for that parameter.
|
||||
// the user did not bind a value to the parameter being processed. This is the condition
|
||||
// defined by `passNulls` and that value controls what happens here. If `passNulls` is
|
||||
// {@code true} we will bind the NULL value into the statement; if `passNulls` is
|
||||
// {@code false} we will not.
|
||||
//
|
||||
// Unfortunately there is not a way to reliably know through JDBC metadata whether a procedure
|
||||
// parameter defines a default value. So we simply allow the procedure execution to happen
|
||||
// assuming that the database will complain appropriately if not setting the given parameter
|
||||
// bind value is an error.
|
||||
log.debugf(
|
||||
"Stored procedure [%s] IN/INOUT parameter [%s] not bound; assuming procedure defines default value",
|
||||
procedureCall.getProcedureName(),
|
||||
this
|
||||
);
|
||||
// parameter defines a default value. Deferring to that information would be the best option
|
||||
if ( passNulls ) {
|
||||
log.debugf(
|
||||
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to true; binding NULL",
|
||||
procedureCall.getProcedureName(),
|
||||
this
|
||||
);
|
||||
typeToUse.nullSafeSet( statement, null, startIndex, session() );
|
||||
}
|
||||
else {
|
||||
log.debugf(
|
||||
"Stored procedure [%s] IN/INOUT parameter [%s] not bound and `passNulls` was set to false; assuming procedure defines default value",
|
||||
procedureCall.getProcedureName(),
|
||||
this
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
typeToUse.nullSafeSet( statement, bind.getValue(), startIndex, session() );
|
||||
|
|
|
@ -20,8 +20,9 @@ public class NamedParameterRegistration<T> extends AbstractParameterRegistration
|
|||
ProcedureCallImpl procedureCall,
|
||||
String name,
|
||||
ParameterMode mode,
|
||||
Class<T> type) {
|
||||
super( procedureCall, name, mode, type );
|
||||
Class<T> type,
|
||||
boolean initialPassNullsSetting) {
|
||||
super( procedureCall, name, mode, type, initialPassNullsSetting );
|
||||
}
|
||||
|
||||
NamedParameterRegistration(
|
||||
|
@ -29,7 +30,8 @@ public class NamedParameterRegistration<T> extends AbstractParameterRegistration
|
|||
String name,
|
||||
ParameterMode mode,
|
||||
Class<T> type,
|
||||
Type hibernateType) {
|
||||
super( procedureCall, name, mode, type, hibernateType );
|
||||
Type hibernateType,
|
||||
boolean initialPassNullsSetting) {
|
||||
super( procedureCall, name, mode, type, hibernateType, initialPassNullsSetting );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,9 @@ public class PositionalParameterRegistration<T> extends AbstractParameterRegistr
|
|||
ProcedureCallImpl procedureCall,
|
||||
Integer position,
|
||||
ParameterMode mode,
|
||||
Class<T> type) {
|
||||
super( procedureCall, position, mode, type );
|
||||
Class<T> type,
|
||||
boolean initialPassNullsSetting) {
|
||||
super( procedureCall, position, mode, type, initialPassNullsSetting );
|
||||
}
|
||||
|
||||
PositionalParameterRegistration(
|
||||
|
@ -29,7 +30,8 @@ public class PositionalParameterRegistration<T> extends AbstractParameterRegistr
|
|||
Integer position,
|
||||
ParameterMode mode,
|
||||
Class<T> type,
|
||||
Type hibernateType) {
|
||||
super( procedureCall, position, mode, type, hibernateType );
|
||||
Type hibernateType,
|
||||
boolean initialPassNullsSetting) {
|
||||
super( procedureCall, position, mode, type, hibernateType, initialPassNullsSetting );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
private final String procedureName;
|
||||
private final NativeSQLQueryReturn[] queryReturns;
|
||||
|
||||
private final boolean globalParameterPassNullsSetting;
|
||||
|
||||
private ParameterStrategy parameterStrategy = ParameterStrategy.UNKNOWN;
|
||||
private List<ParameterRegistrationImplementor<?>> registeredParameters = new ArrayList<ParameterRegistrationImplementor<?>>();
|
||||
|
||||
|
@ -77,6 +79,8 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
public ProcedureCallImpl(SessionImplementor session, String procedureName) {
|
||||
super( session );
|
||||
this.procedureName = procedureName;
|
||||
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
|
||||
|
||||
this.queryReturns = NO_RETURNS;
|
||||
}
|
||||
|
||||
|
@ -90,6 +94,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
public ProcedureCallImpl(final SessionImplementor session, String procedureName, Class... resultClasses) {
|
||||
super( session );
|
||||
this.procedureName = procedureName;
|
||||
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
|
||||
|
||||
final List<NativeSQLQueryReturn> collectedQueryReturns = new ArrayList<NativeSQLQueryReturn>();
|
||||
final Set<String> collectedQuerySpaces = new HashSet<String>();
|
||||
|
@ -128,6 +133,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
public ProcedureCallImpl(final SessionImplementor session, String procedureName, String... resultSetMappings) {
|
||||
super( session );
|
||||
this.procedureName = procedureName;
|
||||
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
|
||||
|
||||
final List<NativeSQLQueryReturn> collectedQueryReturns = new ArrayList<NativeSQLQueryReturn>();
|
||||
final Set<String> collectedQuerySpaces = new HashSet<String>();
|
||||
|
@ -171,6 +177,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
ProcedureCallImpl(SessionImplementor session, ProcedureCallMementoImpl memento) {
|
||||
super( session );
|
||||
this.procedureName = memento.getProcedureName();
|
||||
this.globalParameterPassNullsSetting = session.getFactory().getSessionFactoryOptions().isProcedureParameterNullPassingEnabled();
|
||||
|
||||
this.queryReturns = memento.getQueryReturns();
|
||||
this.synchronizedQuerySpaces = Util.copy( memento.getSynchronizedQuerySpaces() );
|
||||
|
@ -207,7 +214,8 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
storedRegistration.getName(),
|
||||
storedRegistration.getMode(),
|
||||
storedRegistration.getType(),
|
||||
storedRegistration.getHibernateType()
|
||||
storedRegistration.getHibernateType(),
|
||||
storedRegistration.isPassNullsEnabled()
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -221,7 +229,8 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
storedRegistration.getPosition(),
|
||||
storedRegistration.getMode(),
|
||||
storedRegistration.getType(),
|
||||
storedRegistration.getHibernateType()
|
||||
storedRegistration.getHibernateType(),
|
||||
storedRegistration.isPassNullsEnabled()
|
||||
);
|
||||
}
|
||||
parameterRegistrations.add( registration );
|
||||
|
@ -257,7 +266,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
@SuppressWarnings("unchecked")
|
||||
public <T> ParameterRegistration<T> registerParameter(int position, Class<T> type, ParameterMode mode) {
|
||||
final PositionalParameterRegistration parameterRegistration =
|
||||
new PositionalParameterRegistration( this, position, mode, type );
|
||||
new PositionalParameterRegistration( this, position, mode, type, globalParameterPassNullsSetting );
|
||||
registerParameter( parameterRegistration );
|
||||
return parameterRegistration;
|
||||
}
|
||||
|
@ -326,7 +335,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
|
|||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> ParameterRegistration<T> registerParameter(String name, Class<T> type, ParameterMode mode) {
|
||||
final NamedParameterRegistration parameterRegistration = new NamedParameterRegistration( this, name, mode, type );
|
||||
final NamedParameterRegistration parameterRegistration = new NamedParameterRegistration( this, name, mode, type, globalParameterPassNullsSetting );
|
||||
registerParameter( parameterRegistration );
|
||||
return parameterRegistration;
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
|
|||
private final ParameterMode mode;
|
||||
private final Class type;
|
||||
private final Type hibernateType;
|
||||
private final boolean passNulls;
|
||||
|
||||
/**
|
||||
* Create the memento
|
||||
|
@ -114,13 +115,21 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
|
|||
* @param mode The parameter mode
|
||||
* @param type The Java type of the parameter
|
||||
* @param hibernateType The Hibernate Type.
|
||||
* @param passNulls Should NULL values to passed to the database?
|
||||
*/
|
||||
public ParameterMemento(int position, String name, ParameterMode mode, Class type, Type hibernateType) {
|
||||
public ParameterMemento(
|
||||
int position,
|
||||
String name,
|
||||
ParameterMode mode,
|
||||
Class type,
|
||||
Type hibernateType,
|
||||
boolean passNulls) {
|
||||
this.position = position;
|
||||
this.name = name;
|
||||
this.mode = mode;
|
||||
this.type = type;
|
||||
this.hibernateType = hibernateType;
|
||||
this.passNulls = passNulls;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
|
@ -143,6 +152,10 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
|
|||
return hibernateType;
|
||||
}
|
||||
|
||||
public boolean isPassNullsEnabled() {
|
||||
return passNulls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a ParameterMemento from the given parameter registration
|
||||
*
|
||||
|
@ -156,7 +169,8 @@ public class ProcedureCallMementoImpl implements ProcedureCallMemento {
|
|||
registration.getName(),
|
||||
registration.getMode(),
|
||||
registration.getType(),
|
||||
registration.getHibernateType()
|
||||
registration.getHibernateType(),
|
||||
registration.isPassNullsEnabled()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,21 +26,23 @@ public interface ParameterRegistrationImplementor<T> extends ParameterRegistrati
|
|||
*
|
||||
* @throws SQLException Indicates a problem accessing the statement object
|
||||
*/
|
||||
public void prepare(CallableStatement statement, int i) throws SQLException;
|
||||
void prepare(CallableStatement statement, int i) throws SQLException;
|
||||
|
||||
/**
|
||||
* Access to the Hibernate type for this parameter registration
|
||||
*
|
||||
* @return The Hibernate Type
|
||||
*/
|
||||
public Type getHibernateType();
|
||||
Type getHibernateType();
|
||||
|
||||
boolean isPassNullsEnabled();
|
||||
|
||||
/**
|
||||
* Access to the SQL type(s) for this parameter
|
||||
*
|
||||
* @return The SQL types (JDBC type codes)
|
||||
*/
|
||||
public int[] getSqlTypes();
|
||||
int[] getSqlTypes();
|
||||
|
||||
/**
|
||||
* Extract value from the statement after execution (used for OUT/INOUT parameters).
|
||||
|
@ -49,6 +51,6 @@ public interface ParameterRegistrationImplementor<T> extends ParameterRegistrati
|
|||
*
|
||||
* @return The extracted value
|
||||
*/
|
||||
public T extract(CallableStatement statement);
|
||||
T extract(CallableStatement statement);
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
package org.hibernate.test.sql.storedproc;
|
||||
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.NamedStoredProcedureQueries;
|
||||
import javax.persistence.NamedStoredProcedureQuery;
|
||||
import javax.persistence.ParameterMode;
|
||||
import javax.persistence.QueryHint;
|
||||
import javax.persistence.StoredProcedureParameter;
|
||||
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.Session;
|
||||
|
@ -34,6 +40,11 @@ import static org.junit.Assert.fail;
|
|||
*/
|
||||
@RequiresDialect( H2Dialect.class )
|
||||
public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { MyEntity.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
super.configure( configuration );
|
||||
|
@ -328,4 +339,109 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
|
|||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInParametersNotSetPass() {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
// unlike #testInParametersNotSet here we are asking that the NULL be passed
|
||||
// so these executions should succeed
|
||||
|
||||
|
||||
ProcedureCall query = session.createStoredProcedureCall( "findUserRange" );
|
||||
query.registerParameter( 1, Integer.class, ParameterMode.IN ).enablePassingNulls( true );
|
||||
query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 );
|
||||
query.getOutputs();
|
||||
|
||||
// H2 does not support named parameters
|
||||
// {
|
||||
// ProcedureCall query = session.createStoredProcedureCall( "findUserRange" );
|
||||
// query.registerParameter( "start", Integer.class, ParameterMode.IN );
|
||||
// query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 );
|
||||
// try {
|
||||
// query.getOutputs();
|
||||
// fail( "Expecting failure due to missing parameter bind" );
|
||||
// }
|
||||
// catch (JDBCException expected) {
|
||||
// }
|
||||
// }
|
||||
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testInParametersNullnessPassingInNamedQueriesViaHints() {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
// similar to #testInParametersNotSet and #testInParametersNotSetPass in terms of testing
|
||||
// support for specifying whether to pass NULL argument values or not. This version tests
|
||||
// named procedure support via hints.
|
||||
|
||||
// first a fixture - this execution should fail
|
||||
{
|
||||
ProcedureCall query = session.getNamedProcedureCall( "findUserRangeNoNullPassing" );
|
||||
query.getParameterRegistration( 2 ).bindValue( 2 );
|
||||
try {
|
||||
query.getOutputs();
|
||||
fail( "Expecting failure due to missing parameter bind" );
|
||||
}
|
||||
catch (JDBCException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
// here we enable NULL passing via hint through a named parameter
|
||||
{
|
||||
ProcedureCall query = session.getNamedProcedureCall( "findUserRangeNamedNullPassing" );
|
||||
query.getParameterRegistration( "secondArg" ).bindValue( 2 );
|
||||
query.getOutputs();
|
||||
}
|
||||
|
||||
// here we enable NULL passing via hint through a named parameter
|
||||
{
|
||||
ProcedureCall query = session.getNamedProcedureCall( "findUserRangeOrdinalNullPassing" );
|
||||
query.getParameterRegistration( 2 ).bindValue( 2 );
|
||||
query.getOutputs();
|
||||
}
|
||||
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NamedStoredProcedureQueries( {
|
||||
@NamedStoredProcedureQuery(
|
||||
name = "findUserRangeNoNullPassing",
|
||||
procedureName = "findUserRange",
|
||||
parameters = {
|
||||
@StoredProcedureParameter( type = Integer.class ),
|
||||
@StoredProcedureParameter( type = Integer.class ),
|
||||
}
|
||||
),
|
||||
@NamedStoredProcedureQuery(
|
||||
name = "findUserRangeNamedNullPassing",
|
||||
procedureName = "findUserRange",
|
||||
hints = @QueryHint( name = "hibernate.proc.param_null_passing.firstArg", value = "true" ),
|
||||
parameters = {
|
||||
@StoredProcedureParameter( name = "firstArg", type = Integer.class ),
|
||||
@StoredProcedureParameter( name = "secondArg", type = Integer.class ),
|
||||
}
|
||||
),
|
||||
@NamedStoredProcedureQuery(
|
||||
name = "findUserRangeOrdinalNullPassing",
|
||||
procedureName = "findUserRange",
|
||||
hints = @QueryHint( name = "hibernate.proc.param_null_passing.1", value = "true" ),
|
||||
parameters = {
|
||||
@StoredProcedureParameter( type = Integer.class ),
|
||||
@StoredProcedureParameter( type = Integer.class ),
|
||||
}
|
||||
)
|
||||
} )
|
||||
public static class MyEntity {
|
||||
@Id
|
||||
public Integer id;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue