如何使用JPA和Hibernate向具有额外列的多对多关联添加数据

huangapple go评论107阅读模式
英文:

How to add data to Many to Many association with extra column using JPA, Hibernate

问题

以下是翻译好的内容:

我有一个名为 User 的表和一个名为 Book 的表,我想要将它们连接起来。

因此,我创建了第三个名为 Borrow 的表,它具有外键 (book_id, user_id),以及 takenDatebroughtDate 字段。

User.java

  1. @Entity
  2. @Table(name = "Users")
  3. public class User {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private int id;
  7. private String name;
  8. private String surname;
  9. private String username;
  10. private String email;
  11. private String password;
  12. @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
  13. private List<Borrow> borrow;
  14. ....

Book.java

  1. @Entity
  2. @Table(name = "Books")
  3. public class Book {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private int id;
  7. private String title;
  8. private String ISBN;
  9. private String author;
  10. private String issuer;
  11. private Integer dateOfIssue;
  12. private Boolean IsRented;
  13. @OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
  14. private List<Borrow> borrow;
  15. .....

Borrow.java

  1. @Entity
  2. @Table(name = "Borrows")
  3. @IdClass(BorrowId.class)
  4. public class Borrow {
  5. private Date takenDate;
  6. private Date broughtDate;
  7. @Id
  8. @JsonIgnore
  9. @ManyToOne(fetch = FetchType.LAZY)
  10. @JoinColumn(name = "book_id", referencedColumnName = "id")
  11. private Book book;
  12. @Id
  13. @JsonIgnore
  14. @ManyToOne
  15. @JoinColumn(name = "user_id", referencedColumnName = "id")
  16. private User user;
  17. ....

BorrowId.java

  1. public class BorrowId implements Serializable {
  2. private int book;
  3. private int user;
  4. // getters/setters and most importantly equals() and hashCode()
  5. public int getBook() {
  6. return book;
  7. }
  8. public void setBook(int book) {
  9. this.book = book;
  10. }
  11. public int getUser() {
  12. return user;
  13. }
  14. public void setUser(int user) {
  15. this.user = user;
  16. }
  17. @Override
  18. public boolean equals(Object o) {
  19. if (this == o) return true;
  20. if (!(o instanceof BorrowId)) return false;
  21. BorrowId borrowId = (BorrowId) o;
  22. return getBook() == borrowId.getBook() &&
  23. getUser() == borrowId.getUser();
  24. }
  25. @Override
  26. public int hashCode() {
  27. return Objects.hash(getBook(), getUser());
  28. }
  29. }

我尝试像这样向 Borrow 表中添加数据:

  1. @Transactional
  2. @PostMapping("/addUser/{id}/borrow")
  3. public ResponseEntity<Object> createItem(@PathVariable int id, @RequestBody Borrow borrow, @RequestBody Book book){
  4. Optional<User> userOptional = userRepository.findById(id);
  5. Optional<Book> bookOptional = bookRepository.findById(book.getId());
  6. if(!userOptional.isPresent()){
  7. throw new UserNotFoundException("id-" + id);
  8. }
  9. User user = userOptional.get();
  10. borrow.setUser(user);
  11. borrow.setBook(book);
  12. borrowRepository.save(borrow);
  13. URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(borrow.getId()).toUri();
  14. return ResponseEntity.created(location).build();
  15. }

我还没有完成它,因为我不确定如何操作 :/
欢迎任何提示!

英文:

I have a User table and a Book table that I would like to connect.

So I created third table Borrow that has foreign key (book_id, user_id) and takenDate and broughtDate fields.

User.java

  1. @Entity
  2. @Table(name = &quot;Users&quot;)
  3. public class User {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private int id;
  7. private String name;
  8. private String surname;
  9. private String username;
  10. private String email;
  11. private String password;
  12. @OneToMany(mappedBy = &quot;user&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
  13. private List&lt;Borrow&gt; borrow;
  14. ....

Book.java

  1. @Entity
  2. @Table(name = &quot;Books&quot;)
  3. public class Book {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private int id;
  7. private String title;
  8. private String ISBN;
  9. private String author;
  10. private String issuer;
  11. private Integer dateOfIssue;
  12. private Boolean IsRented;
  13. @OneToMany(mappedBy = &quot;book&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
  14. private List&lt;Borrow&gt; borrow;
  15. .....

Borrow.java

  1. @Entity
  2. @Table(name = &quot;Borrows&quot;)
  3. @IdClass(BorrowId.class)
  4. public class Borrow {
  5. private Date takenDate;
  6. private Date broughtDate;
  7. //lazy means it will get details of book
  8. // only if we call GET method
  9. @Id
  10. @JsonIgnore
  11. @ManyToOne(fetch = FetchType.LAZY)
  12. @JoinColumn(name = &quot;book_id&quot;, referencedColumnName = &quot;id&quot;)
  13. private Book book;
  14. @Id
  15. @JsonIgnore
  16. @ManyToOne
  17. @JoinColumn(name = &quot;user_id&quot;, referencedColumnName = &quot;id&quot;)
  18. private User user;
  19. ....

BorowId.java

  1. public class BorrowId implements Serializable {
  2. private int book;
  3. private int user;
  4. // getters/setters and most importantly equals() and hashCode()
  5. public int getBook() {
  6. return book;
  7. }
  8. public void setBook(int book) {
  9. this.book = book;
  10. }
  11. public int getUser() {
  12. return user;
  13. }
  14. public void setUser(int user) {
  15. this.user = user;
  16. }
  17. @Override
  18. public boolean equals(Object o) {
  19. if (this == o) return true;
  20. if (!(o instanceof BorrowId)) return false;
  21. BorrowId borrowId = (BorrowId) o;
  22. return getBook() == borrowId.getBook() &amp;&amp;
  23. getUser() == borrowId.getUser();
  24. }
  25. @Override
  26. public int hashCode() {
  27. return Objects.hash(getBook(), getUser());
  28. }
  29. }

My MySql database design looks like this:

如何使用JPA和Hibernate向具有额外列的多对多关联添加数据

I am trying to add data to Borrow table something like this:

EDITED

  1. @Transactional
  2. @PostMapping(&quot;/addUser/{id}/borrow&quot;)
  3. public ResponseEntity&lt;Object&gt; createItem(@PathVariable int id, @RequestBody Borrow borrow, @RequestBody Book book){
  4. Optional&lt;User&gt; userOptional = userRepository.findById(id);
  5. Optional&lt;Book&gt; bookOptional = bookRepository.findById(book.getId());
  6. if(!userOptional.isPresent()){
  7. throw new UserNotFoundException(&quot;id-&quot; + id);
  8. }
  9. User user = userOptional.get();
  10. borrow.setUser(user);
  11. borrow.setBook(book);
  12. borrowRepository.save(borrow);
  13. URI location = ServletUriComponentsBuilder.fromCurrentRequest().path(&quot;/{id}&quot;).buildAndExpand(borrow.getId()).toUri();
  14. return ResponseEntity.created(location).build();
  15. }

I have't finished it because I am not sure how :/
Any tip is appreciated!

答案1

得分: 0

你已经接近成功了。你只需要记住两件事:

1) 你还需要通过存储库获取Book(目前只获取了User

2) 所有三个操作必须在同一个事务上下文中进行:

  1. 获取`User`,获取`Book`和保存`Borrow`实体。

提示:你可以将所有这些放在一个Service内,并将其标记为@Transactional,或者将@Post方法标记为@Transactional。我建议第一种选项,但由你决定。

编辑:

  1. Optional<Book> bookOptional = bookRepository.findById(book.getId());

此外,似乎在这里使用@EmbeddedId而不是@IdClass更合适,因为这些id实际上是外键实体:

  1. @Embeddable
  2. public class BorrowId {
  3. @ManyToOne(fetch = FetchType.LAZY)
  4. @JoinColumn(name = "book_id", referencedColumnName = "id")
  5. private Book book;
  6. @ManyToOne
  7. @JoinColumn(name = "user_id", referencedColumnName = "id")
  8. private User user;
  9. }

然后在Borrow类中:

  1. @Entity
  2. class Borrow {
  3. @EmbeddedId
  4. BorrowId borrowId;
  5. ...
  6. }

最后,在Post方法中:

  1. BorrowId borrowId = new BorrowId();
  2. borrowId.setUser(user);
  3. borrowId.setBook(book);
  4. borrow.setBorrowId(borrowId);
  5. borrowRepository.save(borrow);
英文:

You are almost there. You just have to keep in mind two things:

1) You have to fetch the Book via repository as well (you only fetch the User currently)

2) All three operation have to be within the same transactional context:

  1. fetching of `User`, fetching of `Book` and save `Borrow` entity.

TIP: You can put all these inside a Service and mark it as @Transactional or mark the @Post method as @Transactional. I would suggest first option, but it is up to you.

EDIT:

  1. Optional&lt;Book&gt; bookOptional = bookRepository.findById(book.getId());

Also, it seems adequate to use @EmbeddedId instead of @IdClass here as ids are actual foreign entities:

  1. @Embeddable
  2. public class BorrowId{
  3. @ManyToOne(fetch = FetchType.LAZY)
  4. @JoinColumn(name = &quot;book_id&quot;, referencedColumnName = &quot;id&quot;)
  5. private Book book;
  6. @ManyToOne
  7. @JoinColumn(name = &quot;user_id&quot;, referencedColumnName = &quot;id&quot;)
  8. private User user;
  9. }

and then in the Borrow class:

  1. @Entity class Borrow{
  2. @EmbeddedId BorrwId borrowId;
  3. ...
  4. }

and in the Post method:

  1. BorrowId borrowId = new BorrowId();
  2. borrowId.setUser(user);
  3. borrowId.setBook(book);
  4. borrow.setBorrowId(borrowId);
  5. borrowRepository.save(borrow);

huangapple
  • 本文由 发表于 2020年8月17日 20:49:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/63451175.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定