Spring Data Rest 集成测试失败,使用简单的 JSON 请求。

24

我的spring-data-rest集成测试因为一个简单的json请求而失败了。考虑以下jpa模型:

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
}

在我的测试中,我使用personRepository创建了一个人,并通过传递该人来初始化订单。

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());

订单已创建,但创建者未与订单关联。另外,我想将请求主体传递为JSON对象。在此中,我的JSON对象应包含以下内容作为创建者。

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

如果我使用以下JSON格式的请求体发送请求,调用将会正常工作

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

但是我不想发送第二个json。有什么办法解决这个问题?因为我的客户端已经通过发送第一个json来使用服务器响应。现在我将服务器迁移到使用spring-data-rest。之后,所有我的客户端代码都无法工作。

如何解决这个问题?


我只是想知道Person类中的关联在哪里声明。它应该引用订单类吗? - notionquest
我在Person类中没有与订单的@OneToMany关联,我也不需要它。 - Achaius
在我的JSON对象中,应包含以下创作者。这是正在发送的内容还是您期望发送的内容?如果是后者,实际上发送了什么?同时在构造函数中设置断点或插入打印语句。传递给构造函数的Person对象是否实际上填充了预期数据?澄清这些问题将缩小问题范围,是客户端问题、Jackson问题还是JPA问题。 - Alan Hay
5个回答

7

你尝试在@ManyToOne注释中使用cascade = CascadeType.ALL了吗?

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
}

7
您正确地将订单与创建者关联起来,但是人员与订单没有关联。您在Person类中缺少List<Order> orders字段。请添加此字段,添加注释,添加向人员添加订单的方法,然后在发送JSON之前,应调用类似以下内容的方法:
creator.addOrder(order);
order.setCreator(cretr);

1
我也有与@Achaius相同的情况。在Person类中没有关联。如果在Person类中没有关联,我该如何处理这种情况? - Galet
你必须在实体之间建立关联,否则它将无法正常工作。你需要以某种方式将它们“连接”起来。如果你不想加载整个对象及其所有关联,只需使用延迟加载即可。 - TomOw
是的,你应该将订单列表添加到Person类中。 - Kiran Kumar

1

你的OrderPerson类都应该实现Serializable,以便将它们正确地转换为JSON格式并从中重建。


1
有一些方法可以解决你的问题,但我想给你一个提示。你只需要保存你的人的"id",并且当你需要时从数据库中通过"id"获取这个人。
这样既解决了你的问题,也节省了内存。

1
我相信你需要做两件事才能使此工作正常。
  1. 正确处理反序列化。由于您希望Jackson通过构造函数填充嵌套的Person对象,因此需要使用@JsonCreator进行注释。请参见此处:

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

Jackson的一个更为强大的特性是其能够使用任意构造函数来创建POJO实例,通过使用@JsonCreator注解来指示要使用的构造函数......基于属性的创建者通常用于将一个或多个必需参数直接传递到构造函数中(或通过工厂方法)。如果在JSON中未找到属性,则传递null(对于原始类型,传递所谓的默认值;例如int类型为0)。 有关Jackson可能无法自动处理此问题的详细信息,请参见此处。

https://dev59.com/JWEh5IYBdhLWcg3w32xG#22013603

  1. 更新您的JPA映射。如果相关的Person现在由Jackson反序列化器正确地填充,则通过将必要的JPA级联选项添加到关系中,两个实例都应该被持久化。

我认为以下内容应该能够按预期工作:

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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接