spring-data-jpa Spring Boot 和JSON:如何在使用POST请求时添加外键

zkure5ic  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(172)

我似乎不知道如何通过JSON添加具有外键的实体。
我有一个用户模型和一个帖子模型。一个用户可以在一个网站上发表不同的帖子。
这是一个多对一的关系。一个用户可以有多个帖子,而一个帖子只能有一个用户(发帖者)。帖子作为外键代表发帖用户的ID。
这是用户模型:

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name = "user")
public class User {

//ID
@Id
@SequenceGenerator(
        name = "user_sequence",
        sequenceName = "user_sequence",
        allocationSize = 1
)
@GeneratedValue(
        strategy = GenerationType.IDENTITY,
        generator = "user_generator"
)
@Column(name = "id",nullable = false)
private int id;

private String username;
private String password;
private String email;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
@Column(name = "creation_date")
private Date creationDate;

//RELATIONSHIP
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<Post> posts = new ArrayList<>();

/* =========== GETTERS AND SETTERS ===========*/

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public Date getCreationDate() {
    return creationDate;
}

public void setCreationDate(Date creationDate) {
    this.creationDate = creationDate;
}

public List<Post> getPosts() {
    return posts;
}

public void setPosts(List<Post> posts) {
    this.posts = posts;
}
}

这是Post模型:

@Entity
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @Table(name = "post")
    public class Post {

    //ID
    @Id
    @SequenceGenerator(
            name = "post_sequence",
            sequenceName = "post_sequence",
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.IDENTITY,
            generator = "post_generator"
    )
    @Column(name = "id", nullable = false)
    private int id;

    @Column(name = "post_content")
    private String postContent;
    private String title;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
    @Column(name = "creation_date")
    private Date creationDate;

    //RELATIONSHIP
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;

    /* ======== GETTERS AND SETTERS ======== */
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPostContent() {
        return postContent;
    }

    public void setPostContent(String postContent) {
        this.postContent = postContent;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    }

这是postController:

@RestController
public class PostController {

@Autowired
private PostService postService;

@PostMapping("/savePost")
public Post getPost(@Validated @RequestBody Post post) {
    return postService.savePost(post);
}

@GetMapping("getPost/{id}")
public Post getPost(@PathVariable int id) {
    return postService.getPost(id);
}

@PutMapping("/deletePost/{id}")
public void deletePost(int id) {
    postService.deletePost(id);
}

}

这是我在添加帖子时发送的JSON。Request to:地址:

{
"postContent": "some content",
"creationDate": "2022-07-31",
"title": "my title",
"user": 1
}

但是在postMan中我得到这个错误:

{
"timestamp": "2022-08-02T10:40:11.794+00:00",
"status": 400,
"error": "Bad Request",
"path": "/savePost"
}

在Spring我得到这个错误:JSON解析错误:无法构造x.model.User的示例(尽管至少存在一个创建者):没有int/Int-argument构造函数/工厂方法要从Number值(1)反序列化;
如果我在JSON中发送请求,其中我调用用户“user_id”或“uderId”,那么我可以发送请求,但随后外键变为空

{
"creationDate": "2022-07-31",
"postContent": "some content",
"title": "my title",
"user_id": 1
}

发送的内容:

{
"id": 2,
"postContent": "some content",
"title": "my title",
"creationDate": "2022-07-31",
"user": null
}

有人知道我做错了什么吗?

xqkwcwgp

xqkwcwgp1#

首先,您的API在REST concept. Here is a nice explanation方面不正确。您最好重新处理它以处理Post实体:

  • userId参数添加到控制器的value中,然后将其从模型中删除。
  • 对传输和业务流程使用不同的类。这至少会给予你两个好处:第一个是通过模型对象传递任何额外数据的能力,或者更广泛地控制哪些属性可以被传递以进行插入/更新(以及对post/put操作进行单独验证的可能性),第二个是保证您不会面临Open session in View问题。
@RestController("/user/{userId}/post")
public class PostController {

  @Autowired
  private PostService postService;

  @PostMapping("/save")
  public PostResponseDTO addPost(@Validated @RequestBody PostAddDTO postModel, @PathVariable Long userId) {
    return postService.savePost(userId, postModel);
  }

  @PutMapping("/{id}")
  public PostResponseDTO updatePost(@Validated @RequestBody PostUpdateDTO postModel, @PathVariable Long userId, @PathVariable Long id) {
    return postService.updatePost(userId, postModel);
  }

  @GetMapping("/{id}")
  public PostResponseDTO getPost(@PathVariable Long userId, @PathVariable Long id) {
    return postService.getPost(userId, id);
  }

  @DeleteMapping("/{id}")
  public void deletePost(Long userId, Long id) {
    postService.deletePost(userId, id);
  }

}

必须

  • 将参数fetch = FetchType.LAZY添加到@OneToMany@ManyToOne声明中;
  • userId属性添加到PostUpdateDTO(如果允许更改帖子的所有者)

在服务层,您可以:

  • 如果POST:通过userId查找User,验证是否存在该实体,如果不存在,则可能引发某个异常,或者创建并持久化一个新Post实体:
@Transactional
  public PostResponseDTO addPost(PostAddDTO postModel, Long userId) {
    User user = getValidUser(userId);
    Post post = new Post(postModel);
    UserDTO userDTO = new UserDTO(user); // here copy only simple properties, not the list of user's posts
    post.setUser(user);
    postRepository.save(post);
    PostAddDTO result = new PostAddDTO(post);
    result.setUser(userDTO);
    return result;
  }

  /**
 - will be used in both post and put operations
 - @param userId user id from a controller
 - @return {@link User} entity if found
 - @throws RuntimeException if the entity has not been found
   */
  private User getValidUser(Long userId) {
    Optional<User> userOpt = userRepository.findById(userId);
    if (!userOpt.isPresent()) {
      log.WARN("addPost. user with id={userId} not found!", userId);
      throw RuntimeException("some exception"); //!!!do not place any business info such as "user with id={userId} not found" because of a scam risc reasons
    }
    return userOpt.get();
  }
  • 如果PUT:通过userIdid找到一个Post实体,验证它是否存在,并实现较好的逻辑。我不知道是否允许重新分配用户?如果允许,请先检查新用户是否存在。
  • 如果DELETE,您可以在实体不存在的情况下引发异常,但许多情况下不会引发异常,并且不会对发送的成功响应采取任何措施

我们使用传输对象的另一个原因。如果你让它保持原样,它将在序列化时导致无限循环:post.user -> (post: user.posts) {post.user -> ...} .
当然,所有这些并不是解决这个问题的唯一方法,也没有回答关于Java Persistance API的所有问题,但这是我不久前在一个具体项目中采用的方法。Here isSpring团队制作的REST指南

相关问题