spring-data-jpa
spring-data-jpa copied to clipboard
Passing null as stored procedure argument is broken in v2.7.0 (works in v2.6.8).
With spring.jpa.properties.hibernate.proc.param_null_passing=true we can pass null as stored procedure as parameter without any problem with Spring Boot 2.6.8 but upgrading to v2.7.0 throws exception with the same Hibernate version. I've tried only with PostgreSQL and UUID as procedure parameter but I suppose it could be for any type of DB and parameters. The exception is:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Bind value [org.hibernate.jpa.TypedParameterValue@5b1420f9] was not of specified type [class java.util.UUID; nested exception is java.lang.IllegalArgumentException: Bind value [org.hibernate.jpa.TypedParameterValue@5b1420f9] was not of specified type [class java.util.UUID
Example:
public interface TestModelRepository extends JpaRepository<TestModel, Long> {
@Procedure("countByUuid")
void countUuid(UUID one);
}
then just call testModelRepository.countUuid(null)
.
I've prepared a sample project to reproduce the problem, just change Spring Boot version to 2.6.8 and it works fine: https://github.com/denis111/spring-data-jpa270test
I ran your code against Spring Boot 3.0.0-M3
and it failed the same. I also ran it against Spring Boot 3.0.0-SNAPSHOT
and it worked. I'd like to pinpoint the patch so I can assess whether it's a candidate for backporting.
Can also confirm the issue still resides in Spring Boot 2.7.2-SNAPSHOT
, so a patch that wasn't backported.
This might explain our current issue using an @Procedure
annotation with a number of parameters where one is nullable, and we suddenly get a Bind value issue. Using V2.7.2
The same problem occurs with Oracle database. Calling a procedure with a nullable parameter leads to the following error:
java.lang.IllegalArgumentException: Bind value [org.hibernate.jpa.TypedParameterValue@42dae24d] was not of specified type [class java.lang.String
It works with all versions < 2.6.10.
Is a correction planned or are nullable parameters no longer supported?
is this still broken with 2.7.3? we downgraded to 2.6.8, waiting for the fix.
is this still broken with 2.7.3? we downgraded to 2.6.8, waiting for the fix.
It should fixed in 2.7.3, but it's not released yet. @gregturn
we are using 2.7.3 and this is still an issue. did you mean it will be fixed in 2.7.4?
I've just tried 2.7.4 and the issue is still there, I have updated my test project.
v2.7.5, the issue is still there, updated my test project.
@denis111 It should be fixed at hibernate side, I have created https://github.com/hibernate/hibernate-orm/pull/5438
Any idea when will the issue be fixed?
The issue is still present un v2.7.6 but it's solved in v3.0.0. I've updated my test project and created the branch spring-boot-3
.
The issue was clearly mended when Spring Data JPA moved to Hibernate 6 in Spring Boot 3.0.0-M4
(Spring Data 2022.0.0-M5
).
You can see ALL the changes that happened in Spring Data JPA between 3.0.0-M4
and 3.0.0-M5
, and nothing stands out except upgrading to Hibernate 6 and properly handling nulls for LIKEs. The former will never be backported to 2.7.x
and the latter already has been in 2.7.6
.
Thank you for that analysis, but can you do one for what happened between 2.6.8 and 2.7.0? It was working and then not anymore.
On Fri, 16 Dec 2022 at 06:18, Greg L. Turnquist @.***> wrote:
The issue was clearly mended when Spring Data JPA moved to Hibernate 6 in Spring Boot 3.0.0-M4 (Spring Data 2022.0.0-M5).
You can see ALL the changes that happened in Spring Data JPA https://github.com/spring-projects/spring-data-jpa/compare/3.0.0-M4...3.0.0-M5 between 3.0.0-M4 and 3.0.0-M5, and nothing stands out except upgrading to Hibernate 6 and properly handling nulls for LIKEs. The former will never be backported to 2.7.x and the latter already has been in 2.7.6.
— Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-data-jpa/issues/2544#issuecomment-1353432200, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHK5HNKSI5OAWG6ADOHJHTWNNHGBANCNFSM5WWOS37Q . You are receiving this because you are subscribed to this thread.Message ID: @.***>
I have created a test case inside Spring Data JPA 2.7.x
that replicates the error message first reported at the top. I want to isolate if this is specific to UUID
or is something else.
Okay, changing the reproducer's type to uuid
to varchar
/String
results in a slightly different error. But it has the same pattern.
Looks like our TypedParameterValue
solution to support Hibernate is the culprit here. If I look for that type when setting stored procedure parameters, and dereference it, it works. I believe that was introduced in 2.7, which would explain why it still works in 2.6.
Thanks @quaff for submitting the PR (https://github.com/spring-projects/spring-data-jpa/issues/2544#issuecomment-1286689469). Until they accept that PR, I can conditionally de-reference the TypedParameterValue
on our end for stored procedure calls.
Issue resolved. Check it out in 2.7.7-SNAPSHOT
.
Merged forward to 3.0.x
and main
.
While TypedParameterValue
is handled properly in Hibernate 6.1 for general parameter types, it still requires dereferencing for temporal types.
CC @quaff
@gregturn it looks like it didn't get to Spring Boot 2.7.7 (I've updated my test project), should we wait for 2.7.8 or it will never be included in 2.7.x branch?
@gregturn We still have this issue. Here a stack trace:
org.springframework.dao.InvalidDataAccessApiUsageException: Bind value [org.hibernate.jpa.TypedParameterValue@2cff35e7] was not of specified type [class java.lang.String; nested exception is java.lang.IllegalArgumentException: Bind value [org.hibernate.jpa.TypedParameterValue@2cff35e7] was not of specified type [class java.lang.String
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:145)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at jdk.proxy3/jdk.proxy3.$Proxy279.assignJob(Unknown Source)
...
Caused by: java.lang.IllegalArgumentException: Bind value [org.hibernate.jpa.TypedParameterValue@2cff35e7] was not of specified type [class java.lang.String
at org.hibernate.procedure.internal.ParameterBindImpl.internalSetValue(ParameterBindImpl.java:94)
at org.hibernate.procedure.internal.ParameterBindImpl.setBindValue(ParameterBindImpl.java:64)
at org.hibernate.procedure.internal.ProcedureCallImpl.setParameter(ProcedureCallImpl.java:817)
at org.hibernate.procedure.internal.ProcedureCallImpl.setParameter(ProcedureCallImpl.java:70)
at org.springframework.data.jpa.repository.query.QueryParameterSetter$BindableQuery.setParameter(QueryParameterSetter.java:326)
at org.springframework.data.jpa.repository.query.QueryParameterSetter$NamedOrIndexedQueryParameterSetter.lambda$setParameter$4(QueryParameterSetter.java:117)
at org.springframework.data.jpa.repository.query.QueryParameterSetter$ErrorHandling$1.execute(QueryParameterSetter.java:140)
at org.springframework.data.jpa.repository.query.QueryParameterSetter$NamedOrIndexedQueryParameterSetter.setParameter(QueryParameterSetter.java:117)
at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:82)
at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:74)
at org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery.doCreateQuery(StoredProcedureJpaQuery.java:102)
at org.springframework.data.jpa.repository.query.StoredProcedureJpaQuery.createQuery(StoredProcedureJpaQuery.java:89)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$ProcedureExecution.doExecute(JpaQueryExecution.java:318)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:156)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:144)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:160)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:81)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
Seems like there is a dependency mismatch. Using springboot-starter-data-jpa 2.7.8 it works, and it has the transient dependency to spring-data-jpa 2.7.7.