spring-data-rest 集成测试因简单的 json 请求而失败

Posted

技术标签:

【中文标题】spring-data-rest 集成测试因简单的 json 请求而失败【英文标题】:spring-data-rest integration test fails with simple json request 【发布时间】:2017-04-20 19:12:04 【问题描述】:

对于一个简单的 json 请求,我的 spring-data-rest 集成测试失败了。考虑以下 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 类中声明的关联在哪里。它应该返回订单类? 我没有 @OneToMany 与 Person 类中的订单关联。我也不需要这个。 在这个我的 json 对象应该包含如下的创建者。这是正在发送的内容还是您期望发送的内容。如果是后者,实际发送的是什么?还要在构造函数中设置断点或插入打印。传递给构造函数的 Person 对象是否实际填充了预期的数据?澄清这些问题将缩小到客户问题、Jackson 问题或 JPA 问题。 【参考方案1】:

您将订单与创建者正确关联,但 Person 未与订单关联。您缺少 Person 类中的 List&lt;Order&gt; orders 字段。添加这个,添加注释,添加用于向人员添加订单的方法,然后在发送 JSON 之前,您应该调用如下内容:

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

【讨论】:

我也有像@Achaius 一样的场景。我在 Person 类中没有关联。如果没有 Person Class 中的关联,如何处理这种情况? 实体之间必须有关联。没有它就行不通。不知何故,你必须“加入”他们。如果您不想加载具有所有关联的整个对象,只需使用延迟加载。 是的,您应该将订单列表添加到 Person 类中【参考方案2】:

您是否尝试在 @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

【讨论】:

【参考方案3】:

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

【讨论】:

【参考方案4】:

有一些方法可以解决你的问题,但我想给你一个提示。当您需要时,您只需保存您的人的"id" 并通过"id" 从您的数据库中获取此人。

它解决了你的问题,也节省了内存。

【讨论】:

【参考方案5】:

我认为您需要做两件事才能完成这项工作。

    正确处理反序列化。正如您期望 Jackson 通过构造函数填充嵌套的 Person 对象一样,您需要使用 @JsonCreator 对其进行注释。见这里:

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

Jackson 更强大的功能之一是它能够使用任意构造函数来创建 POJO 实例,方法是指示构造函数与 @JsonCreator注解 ……………………………………………………………………………………………………………………………………………… 基于属性的创建者通常用于传递一个或多个 强制参数进入构造函数(直接或通过工厂 方法)。 如果没有从 JSON 中找到属性,则改为传递 null (或者,在原语的情况下,所谓的默认值;0 表示整数等 开)。

另请参阅此处了解 Jackson 可能无法自动解决此问题的原因。

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

    更新您的 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;
    

【讨论】:

以上是关于spring-data-rest 集成测试因简单的 json 请求而失败的主要内容,如果未能解决你的问题,请参考以下文章

我可以让自定义控制器镜像 Spring-Data-Rest / Spring-Hateoas 生成的类的格式吗?

再造测试:集成测试是什么?

如何在 Spring-Data-Rest 中实现细粒度的访问控制?

Spring-Data-Rest 验证器

使用 spring-data-rest 将资源添加到集合

排除 Spring-data-rest 资源的部分字段