【问题标题】:Dealing with m-to-n relations in @RepositoryRestResource处理 @RepositoryRestResource 中的 m 对 n 关系
【发布时间】:2018-05-02 19:31:14
【问题描述】:

前言

我想在 one 调用中创建另一个资源的子资源。这些资源具有@ManyToMany 关系:用户和组。

想先创建一个用户,然后是组,然后是Working with Relationships in Spring Data REST 中显示的关系 - 仅仅是因为我认为资源不能单独存在,例如组,只有在至少 一个 用户也与该资源相关联时才应创建。为此,我需要一个端点 like this one(这对我不起作用,否则我不会在这里)创建一个组并在一个事务中设置关联的“种子”用户。

目前,使这项工作对我有用的唯一方法是像这样手动“同步”关系:

public void setUsers(Set<AppUser> users) {
    users.forEach(u -> u.getGroups().add(this));
    this.users = users;
}

这样我就可以

POST http://localhost:8080/groups

{
  "name": "Group X",
  "users": ["http://localhost:8080/users/1"]
}

但我的问题是这对我来说感觉不对 - 它似乎是一种解决方法,而不是使这个要求起作用的实际 Spring 方式。所以..


我目前正在努力使用 Spring 的 @RepositoryRestResource 创建关系资源。我想创建一个新组并将其与调用用户相关联,如下所示:

POST http://localhost:8080/users/1/groups

{
  "name": "Group X"
}

但唯一的结果是响应 204 No Content。我不知道为什么。这可能与我的另一个问题相关,也可能不相关(请参阅here),我尝试通过在 JSON 有效负载中设置相关资源来实现相同的目的 - 这也不起作用。

服务器端出现以下错误:

tion$ResourceSupportHttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class org.springframework.hateoas.Resources<java.lang.Object>]]: java.lang.NullPointerException

如果您需要任何特定代码,请告诉我。

试过

我在UserGroupRepository@RepositoryRestResource 中添加了exported = false

@RepositoryRestResource(collectionResourceRel = "groups", path = "groups", exported = false)
public interface UserGroupRepository extends JpaRepository<UserGroup, Long> {
    List<UserGroup> findByName(@Param("name") String name);
}

我正在发送:

PATCH http://localhost:8080/users/1

{
  "groups": [
    {
      "name": "Group X"
    }
  ]
}

但是,结果仍然只是204 No Content 和服务器端的ResourceNotFoundException


单元测试

基本上,下面的单元测试应该可以工作,但我也可以接受为什么这不能工作以及如何正确完成的答案。

@Autowired
private TestRestTemplate template;

private static String USERS_ENDPOINT = "http://localhost:8080/users/";
private static String GROUPS_ENDPOINT = "http://localhost:8080/groups/";

// ..

@Test
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
public void whenCreateUserGroup() {

    // Creates a user
    whenCreateAppUser();

    ResponseEntity<AppUser> appUserResponse = template.getForEntity(USERS_ENDPOINT + "1/", AppUser.class);
    AppUser appUser = appUserResponse.getBody();

    UserGroup userGroup = new UserGroup();
    userGroup.setName("Test Group");
    userGroup.setUsers(Collections.singleton(appUser));

    template.postForEntity(GROUPS_ENDPOINT, userGroup, UserGroup.class);

    ResponseEntity<UserGroup> userGroupResponse = template.getForEntity(GROUPS_ENDPOINT + "2/", UserGroup.class);

    Predicate<String> username = other -> appUser.getUsername().equals(other);

    assertNotNull("Response must not be null.", userGroupResponse.getBody());

    assertTrue("User was not associated with the group he created.",
            userGroupResponse.getBody().getUsers().stream()
                    .map(AppUser::getUsername).anyMatch(username));
}

但是,行

userGroup.setUsers(Collections.singleton(appUser)); 

将中断此测试并返回404 Bad Request

【问题讨论】:

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


    【解决方案1】:

    根据SDR reference

    发布

    仅支持集合关联。向集合中添加一个新元素。支持的媒体类型:

    text/uri-list - 指向要添加到关联的资源的 URI。

    所以要将group 添加到user 尝试这样做:

    POST http://localhost:8080/users/1/groups (with Content-Type:text/uri-list)
    http://localhost:8080/groups/1
    

    附加info

    【讨论】:

    • 不,抱歉,但这不是我想要的。我希望能够创建一个新资源 like this 而不必从客户端向管理我的数据库中的关系的服务器发出另一个 POST 请求。
    • @displayname 检查我的答案:stackoverflow.com/a/47198584stackoverflow.com/a/47302168
    • 嗯,如果我做对了,第一个示例将创建组 用户。在我的示例中,用户已经存在并且只创建了一个新组。第二个链接有两个例子 - 第一个是相同的,第二个基本上是你的答案目前提出的 - 还是我弄错了?
    • 在我的示例中,现有资源用户创建了一个全新的资源(组),我不希望我的客户负责创建组关系。这应该在服务器端的一次调用中发生。
    • @displayname 如果您已经拥有“父”对象并希望向其添加“子”对象,则必须使用“PATCH”请求(即您正在更新您的“父母”)。只需按照我在这些答案中的建议进行操作,但将 POST 替换为 PATCH。 (但总的来说,我认为让“组”依赖于“用户”是非常糟糕的主意......)
    猜你喜欢
    • 2022-10-25
    • 2014-11-08
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    • 2018-08-18
    相关资源
    最近更新 更多