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

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

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

@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 = &quot;Users&quot;)
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 = &quot;user&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
    private List&lt;Borrow&gt; borrow;
    ....

Book.java

@Entity
@Table(name = &quot;Books&quot;)
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 = &quot;book&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
    private List&lt;Borrow&gt; borrow;
    .....

Borrow.java

@Entity
@Table(name = &quot;Borrows&quot;)
@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 = &quot;book_id&quot;, referencedColumnName = &quot;id&quot;)
    private Book book;


    @Id
    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = &quot;user_id&quot;, referencedColumnName = &quot;id&quot;)
    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() &amp;&amp;
                getUser() == borrowId.getUser();
    }

    @Override
    public int hashCode() {
        return Objects.hash(getBook(), getUser());
    }
}

My MySql database design looks like this:

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

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

EDITED

@Transactional
    @PostMapping(&quot;/addUser/{id}/borrow&quot;)
    public ResponseEntity&lt;Object&gt; createItem(@PathVariable int id, @RequestBody Borrow borrow, @RequestBody Book book){
        Optional&lt;User&gt; userOptional = userRepository.findById(id);
        Optional&lt;Book&gt; bookOptional = bookRepository.findById(book.getId());


        if(!userOptional.isPresent()){
            throw new UserNotFoundException(&quot;id-&quot; + id);
        }

        User user = userOptional.get();
        borrow.setUser(user);
        borrow.setBook(book);

        borrowRepository.save(borrow);

        URI location = ServletUriComponentsBuilder.fromCurrentRequest().path(&quot;/{id}&quot;).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&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:

@Embeddable
public class BorrowId{
   
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;book_id&quot;, referencedColumnName = &quot;id&quot;)
    private Book book;


    @ManyToOne
    @JoinColumn(name = &quot;user_id&quot;, referencedColumnName = &quot;id&quot;)
    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);

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:

确定