Anadi Misra
Anadi Misra

Reputation: 2103

Why is the version property not set with Spring Data JPA?

Wanted to know how is @Version annotation in Spring Data REST put to use for ETags, I do not see the ETags populated for some reason

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Venue implements Serializable {

  private static final long serialVersionUID = -5516160437873476233L;

  private Long id;

  ...
  // other properties

  private Long version;

  private Date lastModifiedDate;

  // getters & setters

  @JsonIgnore
  @LastModifiedDate
  public Date getLastModifiedDate() {
    return lastModifiedDate;
  }

  @Version
  @Column
  public Long getVersion() {
    return version;
  }

Going by the docs this should give me an Etag Value? as seen in the snippet from the library

protected HttpHeaders prepareHeaders(PersistentEntity<?, ?> entity, Object value) {

    // Add ETag
    HttpHeaders headers = ETag.from(entity, value).addTo(new HttpHeaders());

    // Add Last-Modified
    AuditableBeanWrapper wrapper = getAuditableBeanWrapper(value);

however, given the entity & following configuration, I still get a null for Version. My Application has the following

@SpringBootApplication
@EnableEntityLinks
@EnableJpaAuditing
public class GabbarSinghApplication

And the Rest Repository is as follows

@RepositoryRestResource(collectionResourceRel = "venue", path = "venues")
public interface VenueRepository extends JpaRepository<Venue, Long> {

While I haven't got to test these methods yet with the headers etc, a simple POST request on http://localhost:8080/workshops gives a 500 because of null pointer exception at getting ETag header value from value of version property.

Update

Moved to @javax.persistence.Version for the entities, I still do not get an ETag header in the response headers.

Here's a failing unit test

  @Before
  public void setUp() throws Exception {

    XStream xstream = new XStream();
    ObjectInputStream in = xstream.createObjectInputStream(venuesXml.getInputStream());
    leela = (Venue) in.readObject();
    paul = (Venue) in.readObject();
    taj = (Venue) in.readObject();
    LOGGER.debug("Initialised Venues from xml file {}", venuesXml.getFilename());

  }

  @Test
  public void testEtagHeaderIsAutoGeneratedOnResourceCreation() {

    final HttpEntity<Venue> httpEntity = new HttpEntity<Venue>(taj, headers);

    ResponseEntity<ResourceSupport> response = restTemplate.exchange(BASE_LOCATION
        + VENUES_ENDPOINT, HttpMethod.POST, httpEntity,
        new ParameterizedTypeReference<ResourceSupport>() {
        });

    assertTrue("Response should contain ETag header", null != response.getHeaders().getETag());

This assertion fails.

Upvotes: 13

Views: 8751

Answers (2)

Augusto Altman Quaranta
Augusto Altman Quaranta

Reputation: 1576

I faced the same problem and after hours and hours I realized the following things that led me to the enlightenment =P

  1. Spring Data Rest provides ETag support for optimistic concurrency control ONLY after version 2.3.0. See this bug published about a year ago. Previous versions of Spring Data Rest will NOT populate the ETag header.
  2. For Spring Boot applications (our case), you need to use Spring Boot 1.3.0.BUILD-SNAPSHOT or higher in order to be able to setup a spring-boot-starter-data-rest that depends on Spring Data Rest 2.4.1 (higher than 2.3.1 which is exactly what we need :P).

In my case I was using Spring Boot 1.2.7 and whenever I installed the spring-boot-starter-data-rest dependency I ended up getting the Spring Data Rest 2.2.0 which don't has the ETag support. After upgrading Spring Boot in my project and reinstalled the dependencies my REST API started to retrieve the ETag header =D

Upvotes: 2

Oliver Drotbohm
Oliver Drotbohm

Reputation: 83051

With Spring Data JPA, you need to use @javax.persistence.Version. @org.springframework.data.annotation.Version is the annotation to use for other Spring Data modules.

Upvotes: 22

Related Questions