Skip to content

Fall back to canonical constructor in constructor resolution when using Java Records #2625

Closed
@vab2048

Description

@vab2048

Hello,

I have an issue when attempting to persist a record.

The following is fine:

@Table("cat")
public record Cat(
        @Id @Column("id") Long id,
        @Column("name") String name) {

    // Instead of using a constructor uses a factory method.
    public static Cat of(String name) {
        // The 'id' is auto-generated by the DB and populated on insert.
        return new Cat(null, name);
    }

}

But when I use another constructor instead of a factory method I get an exception. The code:

@Table("dog")

public record Dog(
        @Id @Column("id") Long id,
        @Column("name") String name) {

    /**
     * Convenience constructor which sets the id to null with the expectation that it will
     * be populated when the entity is inserted into the DB (since it is an auto-incremented column).
     */
    public Dog(String name) {
        this(null, name);
    }
}

Test which fails that I would except to pass:

@SpringBootTest
public class DogTest {
    private static final Logger log = LoggerFactory.getLogger(DogTest.class);

    @Autowired
    private JdbcAggregateTemplate jdbcAggregateTemplate;

    // THIS TEST FAILS CURRENTLY.
    @Test
    void animal_insertingIntoDb_succeeds() {
        /* Given: A dog. */
        var fido = new Dog("Fido");

        /* When: We try to insert it in to the DB */
        var insertedFido = jdbcAggregateTemplate.insert(fido);

        /* Then: It should populate the ID field */
        assertThat(insertedFido.id()).isNotNull();
    }
}

And the exception:

Cannot set property id because no setter, wither or copy constructor exists for class com.example.demo.persistence.Dog!
java.lang.IllegalStateException: Cannot set property id because no setter, wither or copy constructor exists for class com.example.demo.persistence.Dog!
	at org.springframework.data.mapping.model.InstantiationAwarePropertyAccessor.setProperty(InstantiationAwarePropertyAccessor.java:113)
	at org.springframework.data.mapping.model.ConvertingPropertyAccessor.setProperty(ConvertingPropertyAccessor.java:64)
	at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.setIdAndCascadingProperties(JdbcAggregateChangeExecutionContext.java:337)
	at org.springframework.data.jdbc.core.JdbcAggregateChangeExecutionContext.populateIdsIfNecessary(JdbcAggregateChangeExecutionContext.java:305)
	at org.springframework.data.jdbc.core.AggregateChangeExecutor.execute(AggregateChangeExecutor.java:52)
	at org.springframework.data.jdbc.core.JdbcAggregateTemplate.store(JdbcAggregateTemplate.java:339)
	at org.springframework.data.jdbc.core.JdbcAggregateTemplate.insert(JdbcAggregateTemplate.java:167)
	at com.example.demo.persistence.DogTest.animal_insertingIntoDb_succeeds(DogTest.java:26)

Attached is a repo you can run to reproduce the problem: spring-data-jdbc-records-bug.zip

Am I doing something wrong here (missing annotation/etc.) I would expect the framework to be able to pick up the correct constructor for the record but it currently does not.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions