spring-projects / spring-data-relational

Spring Data Relational. Home of Spring Data JDBC and Spring Data R2DBC.
https://spring.io/projects/spring-data-jdbc
Apache License 2.0
737 stars 339 forks source link

AggregateReference with custom datatypes can not be converted to simple type #1828

Open NielsCW opened 6 days ago

NielsCW commented 6 days ago

Given following example with 2 entities: project and user, each having a custom datatype as id. User has a reference to project using AggregatedReference<ProjectEntity, ProjectId>. When Upgrading Spring Boot from 3.2.5 to 3.3.1, the application fails to create a user because the AggregatedReference is not resolved to a simple type (creating a project works).

During debugging I see that in Spring Boot 3.3.1 AggregatedReference<ProjectEntity, ProjectId> is only converted to a ProjectId while in Spring Boot 3.2.5 the resulting ProjectId is further converted to UUID by MappingRelationalConverter::getPotentiallyConvertedSimpleWrite

CREATE TABLE user ( id UUID NOT NULL PRIMARY KEY, name varchar NOT NULL, project_id UUID NOT NULL REFERENCES project(id) );


* Entity classes:
```java
@Table("project")
@Data
public class ProjectEntity implements Persistable<ProjectId> {

    @Id
    @NonNull
    private final ProjectId id;
    private final String name;
}

@Table("user")
@Data
public class UserEntity implements Persistable<UserId> {

    @Id
    @NonNull
    private final UserId id;
    private final String name;
    @NonNull
    @Column("project_id")
    private final AggregateReference<ProjectEntity, ProjectId> project;
}

@ReadingConverter public class UUIDToProjectIdConverter implements Converter<UUID, ProjectId> {

@Override
public ProjectId convert(UUID source) {
    return ProjectId.from(source);
}

}


* Stack trace:

org.springframework.data.relational.core.conversion.DbActionExecutionException: Failed to execute InsertRoot{entity=com.example.demo.model.UserEntity@1b06dc57, idValueSource=PROVIDED}

at org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:118)
at org.springframework.data.jdbc.core.AggregateChangeExecutor.lambda$executeSave$0(AggregateChangeExecutor.java:61)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.springframework.data.relational.core.conversion.SaveBatchingAggregateChange.forEachAction(SaveBatchingAggregateChange.java:74)
at org.springframework.data.jdbc.core.AggregateChangeExecutor.executeSave(AggregateChangeExecutor.java:61)
at org.springframework.data.jdbc.core.JdbcAggregateTemplate.performSave(JdbcAggregateTemplate.java:491)
at org.springframework.data.jdbc.core.JdbcAggregateTemplate.save(JdbcAggregateTemplate.java:168)
at org.springframework.data.jdbc.repository.support.SimpleJdbcRepository.save(SimpleJdbcRepository.java:68)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:277)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
... 21 more

Caused by: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO "user" ("id", "name", "project_id") VALUES (?, ?, ?)] at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:112) at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:107) at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:116) at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1548) at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:677) at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:970) at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:991) at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:337) at org.springframework.data.jdbc.core.convert.InsertStrategyFactory$DefaultInsertStrategy.execute(InsertStrategyFactory.java:96) at org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy.insert(DefaultDataAccessStrategy.java:110) at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.executeInsertRoot(JdbcAggregateChangeExecutionContext.java:83) at org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:85) ... 35 more Caused by: org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of com.example.demo.model.ProjectId. Use setObject() with an explicit Types value to specify the type to use. at org.postgresql.jdbc.PgPreparedStatement.setObject(PgPreparedStatement.java:1076) at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setObject(HikariProxyPreparedStatement.java) at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:453) at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:247) at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:163) at org.springframework.jdbc.core.PreparedStatementCreatorFactory$PreparedStatementCreatorImpl.setValues(PreparedStatementCreatorFactory.java:287) at org.springframework.jdbc.core.PreparedStatementCreatorFactory$PreparedStatementCreatorImpl.createPreparedStatement(PreparedStatementCreatorFactory.java:245) at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:656) ... 42 more

schauder commented 4 days ago

I did some reproducing, debugging and testing here: https://github.com/spring-projects/spring-data-relational/pull/1829 That draft PR also explains what the problem is. This will need some input from the rest of the team.

schauder commented 4 days ago

1194 feels somewhat related.