Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
NielsCW opened this issue Jul 3, 2024 · 2 comments
Assignees
Labels
for: team-attention An issue we need to discuss as a team to make progress status: waiting-for-triage An issue we've not yet triaged type: bug A general bug

Comments

@NielsCW
Copy link

NielsCW commented Jul 3, 2024

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

  • SQL schema:
CREATE TABLE project (
    id UUID NOT NULL PRIMARY KEY,
    name varchar NOT NULL
);

CREATE TABLE user (
    id UUID NOT NULL PRIMARY KEY,
    name varchar NOT NULL,
    project_id UUID NOT NULL REFERENCES project(id)
);
  • Entity classes:
@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;
}
  • Custom Id (UserId is identical):
@Value(staticConstructor = "from")
final public class ProjectId {

    @NonNull
    @Getter
    private final UUID value;

    @Override
    public String toString() {
        return this.getValue().toString();
    }
}
  • Converters (those for UserId are identical):
@WritingConverter
public class ProjectIdToUUIDConverter implements Converter<ProjectId, UUID> {

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

@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
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 3, 2024
@schauder schauder self-assigned this Jul 5, 2024
@schauder schauder added the type: bug A general bug label Jul 5, 2024
@schauder
Copy link
Contributor

schauder commented Jul 5, 2024

I did some reproducing, debugging and testing here: #1829
That draft PR also explains what the problem is. This will need some input from the rest of the team.

@schauder
Copy link
Contributor

schauder commented Jul 5, 2024

#1194 feels somewhat related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: team-attention An issue we need to discuss as a team to make progress status: waiting-for-triage An issue we've not yet triaged type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants