Closed
Description
With the upgrade from spring boot 3.0.4 (spring-data-bom:2022.0.3) to spring boot 3.0.5 (spring-data-bom:2022.0.4), entities with arrays of enumerated types are not working anymore. Our application relies a lot on such arrays, and it fails for each when upgrading to this minor release.
It seems to be related to this change.
User object :
public record User(String username, Locale locale, Color color, List<Color> colors) {}
Color enum :
public enum Color {
GREEN,
YELLOW,
RED,
BLUE
}
Entity object:
@Table("sample_user")
public record UserEntity(
@Id @Column("user_id") FakeFunctionalUuid userId,
@Column("username") String username,
@Column("locale") Locale locale,
@Column("color") Color color,
@Column("colors") Color[] colors) {
public static UserEntity from(final FakeFunctionalUuid userId, final User user) {
return new UserEntity(
userId, user.username(), user.locale(), user.color(), user.colors().toArray(Color[]::new));
}
public User toUser() {
return new User(username, locale, color, Arrays.stream(colors).toList());
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof final UserEntity that)) {
return false;
}
return userId.equals(that.userId)
&& username.equals(that.username)
&& locale.equals(that.locale)
&& color == that.color
&& Arrays.equals(colors, that.colors);
}
@Override
public int hashCode() {
return Objects.hash(userId);
}
@Override
public String toString() {
return new StringJoiner(", ", MethodHandles.lookup().lookupClass().getSimpleName() + "[", "]")
.add("userId=" + userId)
.add("username='" + username)
.add("locale=" + locale)
.add("color=" + color)
.add("colors=" + Arrays.toString(colors))
.toString();
}
}
Example of stack trace when trying to save such an entity:
UserServiceTest > should save a user containing empty color list FAILED
org.springframework.data.relational.core.conversion.DbActionExecutionException: Failed to execute InsertRoot{entity=UserEntity[userId=7b2817d1-ac50-4826-8dc5-92c25be72b03, username='John, locale=en_US, color=RED, colors=[]], idValueSource=PROVIDED}
at app//org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:118)
at app//org.springframework.data.jdbc.core.AggregateChangeExecutor.lambda$executeSave$0(AggregateChangeExecutor.java:61)
at java.base@17.0.1/java.util.ArrayList.forEach(ArrayList.java:1511)
at app//org.springframework.data.relational.core.conversion.SaveBatchingAggregateChange.forEachAction(SaveBatchingAggregateChange.java:74)
at app//org.springframework.data.jdbc.core.AggregateChangeExecutor.executeSave(AggregateChangeExecutor.java:61)
at app//org.springframework.data.jdbc.core.JdbcAggregateTemplate.performSave(JdbcAggregateTemplate.java:446)
at app//org.springframework.data.jdbc.core.JdbcAggregateTemplate.insert(JdbcAggregateTemplate.java:195)
at app//com.quicksign.kyc.starter.database.infra.repository.WithInsertDelegateImpl.insert(WithInsertDelegateImpl.java:18)
at app//com.quicksign.kyc.starter.database.domain.repository.WithInsertImpl.insert(WithInsertImpl.java:18)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.1/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at app//org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at app//org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)
at app//io.micrometer.observation.Observation.observeChecked(Observation.java:594)
at app//com.quicksign.kyc.starter.observability.domain.ObservationService.observeChecked(ObservationService.java:24)
at app//com.quicksign.kyc.starter.database.infra.observability.TransactionObservabilityAspect.measureTransactionTime(TransactionObservabilityAspect.java:70)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.1/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:637)
at app//org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:627)
at app//org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:71)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:173)
at app//org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at app//org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at app//org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391)
at app//org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at app//org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at app//org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:702)
at app//com.quicksign.kyc.starter.database.domain.repository.WithInsertImpl$$SpringCGLIB$$0.insert(<generated>)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.1/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:288)
at app//org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136)
at app//org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120)
at app//org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516)
at app//org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
at app//org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:168)
at app//org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at app//org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391)
at app//org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218)
at app/jdk.proxy3/jdk.proxy3.$Proxy97.insert(Unknown Source)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.1/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218)
at app/jdk.proxy3/jdk.proxy3.$Proxy97.insert(Unknown Source)
at app//com.quicksign.kyc.starter.database.infra.dao.UserRepositoryImpl.insert(UserRepositoryImpl.java:20)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.1/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at app//org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at app//org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:702)
at app//com.quicksign.kyc.starter.database.infra.dao.UserRepositoryImpl$$SpringCGLIB$$0.insert(<generated>)
at app//com.quicksign.kyc.starter.database.domain.repository.UserService.create(UserService.java:20)
at app//com.quicksign.kyc.starter.database.infra.repository.UserServiceTest.findByIdWithEmptyColorList(UserServiceTest.java:50)
Caused by:
org.springframework.jdbc.BadSqlGrammarException: ConnectionCallback; bad SQL grammar []
at app//org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:99)
at app//org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70)
at app//org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:79)
at app//org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1538)
at app//org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:342)
at app//org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory.createArray(DefaultJdbcTypeFactory.java:74)
at app//org.springframework.data.jdbc.core.convert.BasicJdbcConverter.writeJdbcValue(BasicJdbcConverter.java:277)
at app//org.springframework.data.jdbc.core.convert.SqlParametersFactory.addConvertedValue(SqlParametersFactory.java:190)
at app//org.springframework.data.jdbc.core.convert.SqlParametersFactory.addConvertedPropertyValue(SqlParametersFactory.java:177)
at app//org.springframework.data.jdbc.core.convert.SqlParametersFactory.lambda$getParameterSource$2(SqlParametersFactory.java:260)
at app//org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:298)
at app//org.springframework.data.mapping.PersistentEntity.doWithAll(PersistentEntity.java:284)
at app//org.springframework.data.jdbc.core.convert.SqlParametersFactory.getParameterSource(SqlParametersFactory.java:238)
at app//org.springframework.data.jdbc.core.convert.SqlParametersFactory.forInsert(SqlParametersFactory.java:74)
at app//org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy.insert(DefaultDataAccessStrategy.java:104)
at app//org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.executeInsertRoot(JdbcAggregateChangeExecutionContext.java:74)
at app//org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:85)
... 90 more
Caused by:
org.postgresql.util.PSQLException: Unable to find server array type for provided name UNKNOWN.
at app//org.postgresql.jdbc.PgConnection.createArrayOf(PgConnection.java:1458)
at app//org.postgresql.jdbc.PgConnection.createArrayOf(PgConnection.java:1478)
at app//com.zaxxer.hikari.pool.HikariProxyConnection.createArrayOf(HikariProxyConnection.java)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base@17.0.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@17.0.1/java.lang.reflect.Method.invoke(Method.java:568)
at app//org.springframework.jdbc.core.JdbcTemplate$CloseSuppressingInvocationHandler.invoke(JdbcTemplate.java:1606)
at app/jdk.proxy3/jdk.proxy3.$Proxy89.createArrayOf(Unknown Source)
at app//org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory.lambda$createArray$1(DefaultJdbcTypeFactory.java:74)
at app//org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:334)
... 102 more