Skip to content
New issue

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

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

Already on GitHub? # 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.

# for free to join this conversation on GitHub. Already have an account? # 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