Is it possible to pass a null parameter to a stored procedure in Java JPA 2.1?
Set property hibernate.proc.param_null_passing=true
example:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan">
<array>
<value>my.entity.store.package</value>
</array>
</property>
<property name="persistenceUnitName" value="mainPersistenceUnit" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaDialect" ref="jpaDialect" />
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.hbm2ddl.auto" value="validate" />
<entry key="hibernate.show_sql" value="true" />
<entry key="hibernate.format_sql" value="true" />
<entry key="hibernate.proc.param_null_passing" value="true" />
</map>
</property>
</bean>
Yes, it is possible to pass null params to stored procedures when using JPA StoredProcedureQuery.
You have to add the following property in application.properties file and register the parameters with their name.
spring.jpa.properties.hibernate.proc.param_null_passing=true
Example:
StoredProcedureQuery q = em.createStoredProcedureQuery(Globals.SPROC_PROBLEM_COMMENT2, ProblemCommentVO.class);
q.registerStoredProcedureParameter("Patient_ID", Long.class, ParameterMode.IN);
q.registerStoredProcedureParameter("Param2", Long.class, ParameterMode.IN);
q.registerStoredProcedureParameter("Param3", Long.class, ParameterMode.IN);
q.registerStoredProcedureParameter("Param4", Integer.class, ParameterMode.OUT);
q.setParameter("Patient_ID", patientId);
q.setParameter("Param2", null);//passing null value to Param2
q.setParameter("Param3", null);
List<ProblemCommentVO> pComments = q.getResultList();
Integer a = (Integer) q.getOutputParameterValue("Param4");
Here are my findings for Hibernate 4.3 which are relevant to JPA 2.1.
This will throw an exception if the DB does not support default parameters:
ProcedureCall procedure = getSession().createStoredProcedureCall("my_procedure");
procedure.registerParameter("my_nullable_param", String.class, ParameterMode.IN)
.bindValue(null);
// execute
procedure.getOutputs();
From Hibernate's source for binding the parameter to the underlying CallableStatement
:
public abstract class AbstractParameterRegistrationImpl {
..
@Override
public void prepare(CallableStatement statement, int startIndex) throws SQLException {
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.
// 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);
} else {
typeToUse.nullSafeSet( statement, bind.getValue(), startIndex, session() );
}
}
}
..
}
The above comment reads:
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. 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.
I am interpreting that as JPA (specifically Hibernate) DOES NOT support setting null parameters at all. It looks like they are in a struggle with supporting default parameter values versus substituting a null value when appropriate. They choose to support the former. It looks like those who need support for the latter (nullable values) must use java.sql.CallableStatement
:
getSession().doWork(new Work() {
@Override
public void execute(Connection conn) throws SQLException {
CallableStatement stmt = conn.prepareCall("{ call my_prodecure(:my_nullable_param) }");
if(stringVariableThatIsNull != null) {
stmt.setString("my_nullable_param", stringVariableThatIsNull);
} else {
stmt.setNull("my_nullable_param", Types.VARCHAR);
}
stmt.execute();
stmt.close();
}
});
tl;dr we are still forced to deal with low-level JDBC because neither JPA or Hibernate seem to address nullable parameters. They are supporting procedure parameter default values over substituting a null value.