使用JPA关系映射进行Spring Data Rest测试:更新

时间:2017-07-23 10:34:41

标签: jpa spring-boot spring-data-rest

我按照本教程(https://spring.io/guides/tutorials/react-and-spring-data-rest/#react-and-spring-data-rest-part-5)来试验Spring Data REST,我想用TestRestTemplate测试CRUD。

  • 添加(postForEntity)没问题。
  • 删除(删除)没问题。
  • 阅读(getForEntity)没问题。
  • 更新(template.exchange(URL,HttpMethod.PUT,entity,String.class,ID))仅在我与其他实体没有任何关系时才有效......我不明白为什么。

以下是一个例子:

@Data
@Entity
public class Dojo {

    private @Id @GeneratedValue Long id;
    private String name;
    private String location;
    private Date created;
    @OneToMany(mappedBy = "dojo")
    @JsonIgnore
    private List<Workshop> workshops;

    private Dojo() {}

    public Dojo(String name, String location) {
        this.name = name;
        this.location = location;
        this.created = new Date();
        this.workshops = new ArrayList<>();
    }
//getters and setters ...
}

@Data
@Entity
public class Workshop {

    private @Id @GeneratedValue Long id;
    private String name;
    @ManyToOne
    private Dojo dojo;

    private Workshop() {}

    public Workshop(String name, Dojo dojo) {
        this.name = name;
        this.dojo = dojo;
    }
}

所以,我在Dojo和amp;之间有一个双向的1:n关系。作坊。 @JsonIgnore注释用于避免与JSON Marshaller的无限循环。 存储库是标准的

 public interface WorkshopRepository extends CrudRepository<Workshop, Long> {}

现在我的测试:我想更新研讨会。听起来不错,不起作用。

@Test
public void testUpdateWorkshop() throws Exception {

    final String DOJO_NAME="My Dojo";
    final String DOJO_LOCATION="Liege";
    final String WORKSHOP_NAME="Stuff";
    final String HOST_PORT="http://localhost:8080";

     //creation of a dojo
    final Dojo DOJO = dojoRep.save(new Dojo(DOJO_NAME,DOJO_LOCATION));
     //creation of a workshop
    Workshop workshop = workshopRep.save(new Workshop(WORKSHOP_NAME,DOJO));

    String newValue = "After Test";

    System.out.println("before update");
    System.out.println(workshop.getName()+" == "+WORKSHOP_NAME);

    Long oldID = workshop.getId();

    //As you can see I didn't modify the workshop object
    HttpEntity<Workshop> entity = new HttpEntity<Workshop>(workshop);
    ResponseEntity<String> response = template.exchange(HOST_PORT+"/api/workshops/"+oldID, HttpMethod.PUT, entity, String.class, oldID);

    assert response.getStatusCodeValue() == 200;

    //re-Get the updated workshop
    workshop = workshopRep.findOne(oldID);

    System.out.println("after update");
    System.out.println(workshop.getName()+" == "+WORKSHOP_NAME);

    // as I didn't set the newValue, it must fail and workshop.getName() must stay equal to "Stuff".
    Assert.assertEquals("Update does not work",newValue,workshop.getName());
}

我运行mvn clean test

before update
Stuff == Stuff

after update
My Dojo == Stuff

Failed tests: 
  WorkshopTests.testUpdateWorkshop:218 Update not work expected:<[After Test]> but was:<[My Dojo]>

所以基本上,我没有改变任何东西到我的对象但是

  1. 结果代码为200。
  2. 它改变了我的对象的属性。
  3. 修改了名称以获取dojo.name值!
  4. 只是......为什么?

    更多信息:

    • 当我使用新名称(使用newValue ;-))和新的Dojo创建新的研讨会对象并尝试更新现有研讨会时,结果仍然相同。 workshop.dojo保持不变,名称从dojo.name复制。所以基本上,我的更新不起作用。
    • 我也尝试使用mockMvc代替TestRestTemplate

      mockMvc.perform(put(HOST_PORT+"/api/workshops/"+oldID)
                      .contentType(MediaType.APPLICATION_JSON_UTF8)
                      .content(convertObjectToJsonBytes(workshop))
      );
      

      具有功能

      private byte[] convertObjectToJsonBytes(Object object) throws IOException {
          ObjectMapper mapper = new ObjectMapper();
          System.out.println("log my face ");
          System.out.println(mapper.writeValueAsString(object));
          return mapper.writeValueAsBytes(object);
      }
      

      在更新之前,日志似乎正确地解析了我的对象......

      {"id":1,"name":"Stuff","dojo":{"id":1,"name":"My Dojo","location":"Liege","created":1500799092330}}
      

      但仍然不起作用:(

    • 当我运行应用程序(mvn spring-boot:run)时,在localhost上获取GET:8080 / api / workshops / 1返回

      {
        "name" : "Stuff",
        "_links" : {
          "self" : {
            "href" : "http://localhost-core:8080/api/workshops/1"
          },
          "workshop" : {
            "href" : "http://localhost-core:8080/api/workshops/1"
          },
          "dojo" : {
            "href" : "http://localhost-core:8080/api/workshops/1/dojo"
          }
        }
      }
      
    • 如果我通过nameD更改了我的Dojo类的属性名称,并使用新名称和新的Dojo(先前保存到DB)进行更新,则更新名称,但不会更新道场。

    总结一下我的问题是:

    • 只是......为什么?
    • 使用HTTP请求更新Workshop等对象的正确方法是什么?
    • 测试此更新的正确方法是什么?

    感谢所有人,祝你有个美好的一天! : - )

1 个答案:

答案 0 :(得分:0)

我认为这是因为你正在使用bidirectional one-to-many关联。在这种情况下,您必须自己提供实体的链接/取消链接。例如,在集合设置器中,如下所示:

@Data
@ToString(exclude = "slaves")
@Entity
public class Master {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "master", cascade = {PERSIST, MERGE})
    private List<Slave> slaves;

    public void setSlaves(List<Slave> slaves) {

        // link new slaves to this master
        slaves.forEach(slave -> slave.setMaster(this)); 

        // unlink prev slaves
        if (this.slaves != null) this.slaves.forEach(slave -> slave.setMaster(null)); 

        this.slaves = slaves;
    }
}

@Data
@Entity
public class Slave {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    private Master master;
}

然后您可以存储Slave

POST http://localhost:8080/api/slaves
{
    "name": "slave1"
}

// the same for salve2, slave3, slave4

存储Master

POST http://localhost:8080/api/masters
{
    "name": "master1", 
    "slaves": [
        "http://localhost:8080/api/slaves/1",
        "http://localhost:8080/api/slaves/2"
        ]
}

更新Master

PUT http://localhost:8080/api/masters/1
{
    "name": "master1u", 
    "slaves": [
        "http://localhost:8080/api/slaves/3",
        "http://localhost:8080/api/slaves/4"
        ]
}

PUT http://localhost:8080/api/masters/2
{
    "name": "master2"
}

或更新Slave

PUT http://localhost:8080/api/slaves/1
{
    "name": "slave1u", 
    "master": "http://localhost:8080/api/masters/2"
}

PUT http://localhost:8080/api/slaves/2
{
    "name": "slave2u", 
    "master": "http://localhost:8080/api/masters/2"
}

见工作example

其他info