Property population via `wither` methods

Viewed 1105

Property Population isn't working as expected for me when using immutable objects.

I'm attempting to follow the Property Population section of the spring-data-couchbase-3.1.4.RELEASE reference docs which suggests that it I can pass in a immutable entity with an appropriate @wither method for setting the id. However, when I attempted it the returned entity still has a null value for the ID.

com/example/demospringdatacouchbaseapp/model/Car.java

import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.USE_ATTRIBUTES;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.IdAttribute;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
import lombok.experimental.Wither;

@Value
@AllArgsConstructor
@Builder(toBuilder=true)
@Document
public class Car {

  public static final String ID_DELIMITER = ".";

  @Id
  @GeneratedValue(strategy = USE_ATTRIBUTES, delimiter = ID_DELIMITER)
  @Wither
  String id;

  @Field
  @IdAttribute(order=0)
  String manufacturer;

  @Field
  @IdAttribute(order=1)
  String model;

  @Field
  @IdAttribute(order=2)
  String spec;

  @Field
  String colour;

}

com/example/demospringdatacouchbaseapp/repository/CarRepository.java

import java.util.Collection;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import com.example.demospringdatacouchbaseapp.model.Car;

public interface CarRepository extends CouchbaseRepository<Car, String> {

  Collection<Car> findByColour(String colour);

}

Create simple car test

  @Test
  public void createSingCarTest() {

    /*
     * Given
     */

    Car givenCar = createMadWeeClio();

    /*
     * When
     */

    Car persistedCar = repository.save(givenCar);

    /*
     * Then
     */

    assertThat(persistedCar).isEqualTo(givenCar.withId(getExpectedId(givenCar)));

  }

  ...

  private String getExpectedId(Car givenCar) {
    return givenCar.getManufacturer() + Car.ID_DELIMITER + givenCar.getModel() + Car.ID_DELIMITER
        + givenCar.getSpec();
  }

  private Car withExpectedId(Car car) {
    return car.withId(getExpectedId(car));
  }

  private Car createMadWeeClio() {
    return Car.builder().manufacturer("RenaultSport").model("Clio").spec("200 Cup").colour("white")
        .build();
  }

  private Car createMadMeg() {
    return Car.builder().manufacturer("RenaultSport").model("Megane").spec("R.S Trophy")
        .colour("Yellow").build();
  }

I expected the CouchbaseRepository::save operation to return a new instance of my immutable entity object with the auto-generated ID property filled in. However, its coming back as null in my tests.

Passing in a mutable entity results in the id field being set as expected. I can also see the ID field being populated in couchbase.

1 Answers

I think the issue is that the save operation is not creating a new instance of your Car object - rather it will try and set the id onto the object (but can't since its immutable).

The @Value Lombok annotation results in a method which looks like

Car withId(String id) {
    return new Car(id, this.model, this.spec, this.colour);
}

but this method is not called when you save your object.

When you retrieve the object from the database the withId method is called. So if you return all the objects from the database (or search for it using some other criteria such as model) you'll see that they do infact have id properties with values. The documentation covers this here:

https://docs.spring.io/spring-data/couchbase/docs/3.1.4.RELEASE/reference/html/#mapping.property-population

Related