Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data REST - PUT request does not work properly since v.2.5.7

Since version 2.5.7 Spring Data REST does not properly perform a PUT request to update resource which has associated resources. Unlike PATCH request that works as expected!

For example, Person has a many-to-one association with Addres. If we perform a PUT request with SDR v.2.5.6 (Spring Boot v.1.4.3) then all works OK. But if we switch to version 2.5.7 (i.e. to Spring Boot v.1.4.4) then we get an error:

Can not construct instance of Address: no String-argument constructor/factory method to deserialize from String value

The same happens with other types of associations, for example with one-to-many (uni- and bidirectional) - see my example application code and tests.

This problem is present in all versions of the Spring Boot since 1.4.4 including the latest stable 1.5.6 version, as well as the newest 2.0.0-SNAPSHOT version!

To work around this situation we can just switch to SDR v.2.5.6 (Spring Boot v.1.4.3).

I've prepared a Postman collection of requests to help you play with the issue: SDR PUT Issue

UPDATE 2017-08-14

I found how to avoid the error Can not construct instance of Address: no String-argument constructor/factory method to deserialize from String value.

Since I'm using Lombok in this project, it is necessary just to tell Lombok to suppress using the @ConstructorProperties annotation in generated constructors. So I set lombok.anyConstructor.suppressConstructorProperties=true in the 'lombok.config' file and the error was gone.

Unfortunately a new problem was found - PUT request does not update associated objects at all!

The example below is demonstrating this. When we are trying to update Person by changing his Address from addresses/1 (initial value) to addresses/2 - then it remains the same: addresses/1! As well as the previous problem this one is present in all versions of the Spring Boot since 1.4.4 (SDR - from v.2.5.7).

I debugged my project and found out that the reason of the issue is hidden in the method DomainObjectReader#mergeForPut (see its source) - it never replaces associated resources with new ones.

Before I post this issue on Spring JIRA, please report here if you have this issue in your projects and what do you think about it.

You can get my test here and check it in your projects - the test is 'standalone' and doesn't depend from other classes/modules (exclude only H2, I hope).

@Entity
public class Person {

    private String name;

    @ManyToOne
    private Address address;

    // other stuff
}

@Entity    
public class Address {

    private String street;

    // other stuff
}

Trying to update Person:

PUT http://localhost:8080/api/persons/1
{
    "name": "person1u",
    "address": "http://localhost:8080/api/addresses/2"
}

Getting the correct response:

{
    "name": "person1u",
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/persons/1"
        },
        "person": {
            "href": "http://localhost:8080/api/persons/1"
        },
        "address": {
            "href": "http://localhost:8080/api/persons/1/address"
        }
    }
}

Then checking for a 'new' Address of Person - address was not updated:

GET http://localhost:8080/api/persons/1/address
{
    "street": "address1",
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/addresses/1"
        },
        "address": {
            "href": "http://localhost:8080/api/addresses/1"
        }
    }
}

UPDATE 2017-08-24

Thanks to Scott C. answer, it turned out that SDR has a bug, which is described in two tickets: DATAREST-1001 and DATAREST-1012.

like image 237
Cepr0 Avatar asked Aug 10 '17 17:08

Cepr0


1 Answers

Looks like the issue has already been reported as a bug: - please verify. As best as I can tell, this is the issue you are reporting above.

Note, I am revising my previous answer to be this bug report.

like image 148
Scott C. Avatar answered Oct 19 '22 02:10

Scott C.