简体   繁体   中英

spring-data-rest integration test fails with simple json request

My spring-data-rest integration test fails for a simple json request. Consider the below jpa models

Order.java

public class Order {

    @Id @GeneratedValue//
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)//
    private Person creator;
    private String type;

    public Order(Person creator) {
        this.creator = creator;
    }

    // getters and setters
}

Person.java

ic class Person {

    @Id @GeneratedValue private Long id;

    @Description("A person's first name") //
    private String firstName;

    @Description("A person's last name") //
    private String lastName;

    @Description("A person's siblings") //
    @ManyToMany //
    private List<Person> siblings = new ArrayList<Person>();

    @ManyToOne //
    private Person father;

    @Description("Timestamp this person object was created") //
    private Date created;

    @JsonIgnore //
    private int age;

    private int height, weight;
    private Gender gender;

    // ... getters and setters
}

In my test I created a person by using personRepository and inited order by passing person

Person creator = new Person();
creator.setFirstName("Joe");
creator.setLastName("Keith");
created.setCreated(new Date());
created.setAge("30");
creator = personRepository.save(creator);

Order order = new Order(creator);
String orderJson = new ObjectMapper().writeValueAsString(order);

mockMvc.perform(post("/orders").content(orderJson).andDoPrint());

Order is created but creator is not associated with the order. Also I want to pass request body as a json object. In this my json object should contain creator as follows

{
"type": "1",
"creator": {
    "id": 1,
    "firstName": "Joe",
    "lastName": "Keith",
    "age": 30
}
}

If I send request body with the following json, the call works fine

{
"type": "1",
"creator": "http://localhost/people/1"
}

But I don't want to send the second json. Any idea how to solve the issue. Because already my client is consuming the server response by sending first json. Now I migrated my server to use spring-data-rest. After that all my client code is not working.

How to solve this?

You are correctly associating order with the creator, however the Person is not associated with the orders. You are missing the List<Order> orders field in Person class. Add this, add annotations, add methods for adding order to person and then before sending JSON you should call something like this:

creator.addOrder(order);
order.setCreator(cretr);

Did you try using cascade = CascadeType.ALL in @ManyToOne annotation

 public class Order { @Id @GeneratedValue// private Long id; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)// private Person creator; private String type; public Order(Person creator) { this.creator = creator; } // getters and setters } 

OrderPerson类都应该实现Serializable以便将它们正确分解并从JSON重建它们。

There are some ways to solve your problem, but I want give you a hint. You just can save only "id" of your person and get the person by "id" from your database, when you need this.

It solves your problem and it also saves the memory.

I believe you need to do two things to get this work.

  1. Handle the deserialization properly. As you expect Jackson to populate the nested Person object via the constructor you need to annotate this with @JsonCreator . See here:

http://www.cowtowncoder.com/blog/archives/2011/07/entry_457.html

One of more powerful features of Jackson is its ability to use arbitrary >constructors for creating POJO instances, by indicating constructor to use with @JsonCreator annotation ........................................... Property-based creators are typically used to pass one or more obligatory parameters into constructor (either directly or via factory method). If a property is not found from JSON, null is passed instead (or, in case of primitives, so-called default value; 0 for ints and so on).

See also here on why Jackson may not be able to automatically work this out.

https://stackoverflow.com/a/22013603/1356423

  1. Update your JPA mappings. If the associated Person is now populated correctly by the Jackson deserializer then by adding the necessary JPA cascade options to the relationship then both instances should be persisted.

I think then the following should work as expected:

public class Order {

    @Id 
    @GeneratedValue(...)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, cascade = cascadeType.ALL)
    private Person creator;

    private String type;

    @JsonCreator
    public Order(@JsonProperty("creator") Person creator) {
        this.creator = creator;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM