2

I'm new to Spring Data JDBC and create a Customer aggregate with two Address values using Spring-Boot 2.5.0, Java 11 and Lombok (code examples simplified).

I have one Customer entity (aggregate root) and one Address value object

@Data
@Builder
@AllArgsConstructor
class Customer {
    @Id Long id;
    String name;
    Address address1;
    Address address2;
}

@Data
@Builder
@AllArgsConstructor
class Address {
    String city;
}

and one Repository for the Customer entity

@Repository
public interface CustomeRepository extends CrudRepository<Customer, Long> {
}

Using Postgres the db schema looks like this

CREATE TABLE "customer" (
  "id"                  BIGSERIAL       NOT NULL,
  "name"                VARCHAR(255)    NOT NULL,
  PRIMARY KEY (id)
);

CREATE TABLE "address" (
  "id"                  BIGSERIAL       NOT NULL,
  "customer"            BIGINT,
  "city"                VARCHAR(255)    NOT NULL,
  PRIMARY KEY (id)
);

Creating and storing a Customer

        var address1 = Address.builder().city("New York").build();
        var address2 = Address.builder().city("Chicago").build();
        var customer = Customer.builder().name("Joe").address1(address1).address2(address2).build();
        var result = customerRegistry.save(customer);

So far so good, also the entries in the database looking fine

 id | name 
----+------
  1 | Joe


 id | customer |   city   
----+----------+----------
  1 |        1 | Chicago
  2 |        1 | New York

So expecting one Customer, but when doing this

var customers = customerService.findAll();
customers.forEach(c -> log.debug("Customer: {}", c));

the output will be

Customer(id=1, name=Joe, address1=Address(city=New York), address2=Address(city=Chicago))
Customer(id=1, name=Joe, address1=Address(city=Chicago), address2=Address(city=Chicago))
Customer(id=1, name=Joe, address1=Address(city=New York), address2=Address(city=New York))
Customer(id=1, name=Joe, address1=Address(city=Chicago), address2=Address(city=New York))

and doing this

var customer = customerRepository.getById(result.getId());

will result in

org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 1, actual 4

Btw. If the Customer has only one address field everything works as expected.

So do I miss something or is this a bug?

Stefan
  • 67
  • 6

1 Answers1

2

You can put a @Column annotation on one or both attributes, specifying different columns to use for the back-reference to Customer.

For example:

class Customer {
    @Id Long id;
    String name;
    @Column("first")
    Address address1;
    @Column("second")
    Address address2;
}

would expect the following address table

CREATE TABLE "address" (
  "id"                  BIGSERIAL       NOT NULL,
  "first"               BIGINT,
  "second"              BIGINT,
  "city"                VARCHAR(255)    NOT NULL,
  PRIMARY KEY (id)
);

For more background and other alternatives that go beyond the specific problem at hand see Why is a entity - value relationship implemented as a back reference in Spring Data JDBC

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
  • Hi Jens, thank you very much for the quick reply! Actually I used instead a map like Map for three different address types. It works fine, but I think your proposal is the better solution. Thanks and greetings from Flensburg, Stefan – Stefan Jun 08 '21 at 16:30
  • First and second these columns should be in Customer table or in address table? – Gaurav Jun 17 '21 at 04:57
  • @Gaurav In the address table – Jens Schauder Jun 17 '21 at 05:32
  • And where will be exact address will store, I am considering first and second as ID of address – Gaurav Jun 17 '21 at 05:36
  • @Gaurav Sorry I don't understand your question. The `id` in `address` is not necessary and I'd recommend against it. The combination of `first` and `second` is unique. – Jens Schauder Jun 17 '21 at 05:47
  • @Jens Just for my understanding - why is this relationship Entity - Value implemented like this as a backreference? If I'm using another entity with an address value I need another unique column in the Address value, I cannot reuse the first or second column. These columns have to be unique for each entity. – Stefan Jun 21 '21 at 06:58
  • @Stefan could you ask that as a separate question please. It's a good question but the answer wouldn't fit in a comment. – Jens Schauder Jun 21 '21 at 07:39
  • @Jens Ok, thanks, you will find it [here](https://stackoverflow.com/questions/68073332/why-is-a-entity-value-relationship-implemented-as-a-back-reference-in-spring-d). – Stefan Jun 22 '21 at 06:06