英文:
How to add data to Many to Many association with extra column using JPA, Hibernate
问题
以下是翻译好的内容:
我有一个名为 User 的表和一个名为 Book 的表,我想要将它们连接起来。
因此,我创建了第三个名为 Borrow 的表,它具有外键 (book_id, user_id),以及 takenDate 和 broughtDate 字段。
User.java
@Entity
@Table(name = "Users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String surname;
    private String username;
    private String email;
    private String password;
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Borrow> borrow;
    ....
Book.java
@Entity
@Table(name = "Books")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String title;
    private String ISBN;
    private String author;
    private String issuer;
    private Integer dateOfIssue;
    private Boolean IsRented;
    @OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Borrow> borrow;
    .....
Borrow.java
@Entity
@Table(name = "Borrows")
@IdClass(BorrowId.class)
public class Borrow {
    private Date takenDate;
    private Date broughtDate;
    @Id
    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id", referencedColumnName = "id")
    private Book book;
    @Id
    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;
    ....
BorrowId.java
public class BorrowId implements Serializable {
    private int book;
    private int user;
    // getters/setters and most importantly equals() and hashCode()
    public int getBook() {
        return book;
    }
    public void setBook(int book) {
        this.book = book;
    }
    public int getUser() {
        return user;
    }
    public void setUser(int user) {
        this.user = user;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BorrowId)) return false;
        BorrowId borrowId = (BorrowId) o;
        return getBook() == borrowId.getBook() &&
                getUser() == borrowId.getUser();
    }
    @Override
    public int hashCode() {
        return Objects.hash(getBook(), getUser());
    }
}
我尝试像这样向 Borrow 表中添加数据:
@Transactional
@PostMapping("/addUser/{id}/borrow")
public ResponseEntity<Object> createItem(@PathVariable int id, @RequestBody Borrow borrow, @RequestBody Book book){
    Optional<User> userOptional = userRepository.findById(id);
    Optional<Book> bookOptional = bookRepository.findById(book.getId());
    if(!userOptional.isPresent()){
        throw new UserNotFoundException("id-" + id);
    }
    User user = userOptional.get();
    borrow.setUser(user);
    borrow.setBook(book);
    borrowRepository.save(borrow);
    URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(borrow.getId()).toUri();
    return ResponseEntity.created(location).build();
}
我还没有完成它,因为我不确定如何操作 :/
欢迎任何提示!
英文:
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
@Entity
@Table(name = "Users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String surname;
    private String username;
    private String email;
    private String password;
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Borrow> borrow;
    ....
Book.java
@Entity
@Table(name = "Books")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String title;
    private String ISBN;
    private String author;
    private String issuer;
    private Integer dateOfIssue;
    private Boolean IsRented;
    @OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Borrow> borrow;
    .....
Borrow.java
@Entity
@Table(name = "Borrows")
@IdClass(BorrowId.class)
public class Borrow {
    private Date takenDate;
    private Date broughtDate;
    //lazy means it will get details of book
    // only if we call GET method
    @Id
    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id", referencedColumnName = "id")
    private Book book;
    @Id
    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;
    ....
BorowId.java
public class BorrowId implements Serializable {
    private int book;
    private int user;
    // getters/setters and most importantly equals() and hashCode()
    public int getBook() {
        return book;
    }
    public void setBook(int book) {
        this.book = book;
    }
    public int getUser() {
        return user;
    }
    public void setUser(int user) {
        this.user = user;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BorrowId)) return false;
        BorrowId borrowId = (BorrowId) o;
        return getBook() == borrowId.getBook() &&
                getUser() == borrowId.getUser();
    }
    @Override
    public int hashCode() {
        return Objects.hash(getBook(), getUser());
    }
}
My MySql database design looks like this:
I am trying to add data to Borrow table something like this:
EDITED
@Transactional
    @PostMapping("/addUser/{id}/borrow")
    public ResponseEntity<Object> createItem(@PathVariable int id, @RequestBody Borrow borrow, @RequestBody Book book){
        Optional<User> userOptional = userRepository.findById(id);
        Optional<Book> bookOptional = bookRepository.findById(book.getId());
        if(!userOptional.isPresent()){
            throw new UserNotFoundException("id-" + id);
        }
        User user = userOptional.get();
        borrow.setUser(user);
        borrow.setBook(book);
        borrowRepository.save(borrow);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(borrow.getId()).toUri();
                return ResponseEntity.created(location).build();
    }
I have't finished it because I am not sure how :/
Any tip is appreciated!
答案1
得分: 0
你已经接近成功了。你只需要记住两件事:
1) 你还需要通过存储库获取Book(目前只获取了User)
2) 所有三个操作必须在同一个事务上下文中进行:
获取`User`,获取`Book`和保存`Borrow`实体。
提示:你可以将所有这些放在一个Service内,并将其标记为@Transactional,或者将@Post方法标记为@Transactional。我建议第一种选项,但由你决定。
编辑:
Optional<Book> bookOptional = bookRepository.findById(book.getId());
此外,似乎在这里使用@EmbeddedId而不是@IdClass更合适,因为这些id实际上是外键实体:
@Embeddable
public class BorrowId {
   
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id", referencedColumnName = "id")
    private Book book;
    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;
}
然后在Borrow类中:
@Entity
class Borrow {
  @EmbeddedId 
  BorrowId borrowId;
  ...
}
最后,在Post方法中:
BorrowId borrowId = new BorrowId();
borrowId.setUser(user);
borrowId.setBook(book);
borrow.setBorrowId(borrowId);
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:
   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:
Optional<Book> bookOptional = bookRepository.findById(book.getId());
Also, it seems adequate to use @EmbeddedId instead of @IdClass here as ids are actual foreign entities:
@Embeddable
public class BorrowId{
   
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id", referencedColumnName = "id")
    private Book book;
    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;
}
and then in the Borrow class:
@Entity class Borrow{
  @EmbeddedId BorrwId borrowId;
  ...
}
and in the Post method:
    BorrowId borrowId = new BorrowId();
    borrowId.setUser(user);
    borrowId.setBook(book);
    borrow.setBorrowId(borrowId);
    borrowRepository.save(borrow);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。



评论