Sử dụng @ManyToMany annotation trong JPA

Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu một annotation cuối cùng trong JPA, ngoài @ManyToOne, @OneToOne, @OneToMany, để thể hiện mối quan hệ giữa hai bảng bất kỳ trong database. Đó chính là annotation @ManyToMany! Trong trường hợp 2 bảng bất kỳ có quan hệ nhiều nhiều, ví dụ như một developer có thể làm trong nhiều dự án khác nhau và một dự án thì lại có nhiều developer, thì các bạn có thể sử dụng annotation @ManyToMany để thể hiện mối quan hệ đó. Hãy cùng mình tìm hiểu cách sử dụng annotation này trong bài viết này các bạn nhé!

Đầu tiên, mình sẽ tạo một Maven project để làm ví dụ:

Sử dụng @ManyToMany annotation trong JPA
Mình sẽ sử dụng Hibernate làm implementation của JPA nên sẽ thêm Hibernate dependency như sau:

MySQL Connector:

Giả sử bây giờ mình định nghĩa cấu trúc của 2 bảng developer và project có nội dung như sau:

Như các bạn thấy, mình đã định nghĩa bảng developer với 2 cột là id và name trong đó cột id là primary key. Còn trong bảng project, mình cũng định nghĩa 2 cột id và name với primary key cũng là id.

Ở đây, chúng ta không thể định nghĩa các cột foreign key từ bảng này sang bảng kia bởi vì chúng sẽ không thể hiện được mối quan hệ nhiều nhiều giữa chúng. Ví dụ như: nếu mình định nghĩa một column project_id trong bảng developer thì mình chỉ định nghĩa được một project mà developer đó thuộc về, không thể định nghĩa nhiều project được. Ngược lại đối với bảng project cũng thế.

Một giải pháp để giải quyết vấn đề này chúng ta có thể làm đó là định nghĩa một bảng nữa để thể hiện mối quan hệ nhiều nhiều giữa bảng developer và project. Bảng này sẽ chứa id của developer tương ứng với id của project, còn được gọi là joinTable, cụ thể như sau:

Bây giờ thì chúng ta có thể định nghĩa một developer có thể thuộc về nhiều project và một project có thể chứa nhiều developer rồi!

Entity của 2 bảng developer và project lúc này có thể định nghĩa với annotation @ManyToMany như sau:

Entity Developer:

Entity Project:

Chúng ta sẽ đặt annotation @ManyToMany cùng với khai báo một biến Collection trong cả hai entity Developer và Project và để thể hiện mối quan hệ nhiều nhiều, chúng ta sẽ sử dụng một annotation khác tên là @JoinTable trong một trong hai entity này. Các bạn có thể đặt annotation @JoinTable ở đâu cũng được, nếu annotation này nằm trong entity @Developer thì trong entity Project các bạn phải khai báo thêm thuộc tính mappedBy trong annotation @ManyToMany và ngược lại.

Trong annotation @JoinTable mà mình định nghĩa trong entity Developer, ở đây chúng ta có 3 thuộc tính chúng ta cần phải khai báo:

  • name: một là tên của joinTable (là bảng developer_project đó các bạn),
  • joinColumns: hai là tên column trong bảng joinTable mà bảng developer sẽ foreign key tới,
  • inverseJoinColumns: ba là tên column trong bảng joinTable mà bảng project sẽ foreign key tới.

OK, vậy là chúng ta đã định nghĩa xong entity Developer và entity Project với annotation @ManyToMany để thể hiện mối quan hệ nhiều nhiều giữa 2 bảng: developer và project. Bây giờ hãy thử làm ví dụ để xem nó làm việc như thế nào nhé các bạn!

Mình sẽ thêm tập tin cấu hình cho JPA, persistence.xml, nằm trong thư mục /src/main/resources/META-INF với nội dung như sau:

Giả sử trong database mình đang có những dữ liệu sau:

Sử dụng @ManyToMany annotation trong JPA

thì khi chạy ví dụ sau:

Kết quả sẽ như sau:

Sử dụng @ManyToMany annotation trong JPA


Chia sẽ bài viết này ...Share on Facebook
Facebook
0Tweet about this on Twitter
Twitter
Share on LinkedIn
Linkedin

16 thoughts on “Sử dụng @ManyToMany annotation trong JPA

  1. Avatar

    Cám ơn vì bài viết cảu a .
    cho em hỏi về việc “phải khai báo thêm thuộc tính mappedBy”
    Khi nào thì mình khai báo mappedBy còn khi nào thì ko cần hả a .vì e có thấy vài ví dụ ko cần khai báo nó vẫn chạy bình thường.

  2. Avatar

    Chào a Khanh.
    Em cảm ơn anh về loạt bài rất dễ hiểu ạ.E có làm theo hướng dẫn của a về manytomany và sử dụng thêm phần JPAQL + RESTful. Nhưng khi e ResponseEntity(List, HttpStatus.OK); về Json thì dữ liệu trả về sai.
    A có thể viết bài hướng dẫn về manytomany + JPAQL + RESTful không ạ?
    Cảm ơn a.

  3. Avatar

    Chào anh Khanh Nguyen, e có làm theo a e dùng Spring MVC và sử dụng Hibernate nhưng ko hiểu tại sao nó cứ báo lỗi failed to lazily initialize a collection of role mong a giải đáp dùm e. Thanks a.

      1. Avatar

        29-Oct-2017 10:16:28.845 WARN [http-nio-8080-exec-1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper.logExceptions SQL Error: 1054, SQLState: 42S22
        29-Oct-2017 10:16:28.848 ERROR [http-nio-8080-exec-1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper.logExceptions Unknown column ‘pros0_.project_proID’ in ‘field list’
        lỗi này nè a nó ko vô đc database

          1. Avatar

            ok a e sửa đc rồi mà ko hiểu tại sao e bỏ đi @ManyToMany(mappedBy = “projects”)
            private Collection developers; thì nó lại chạy đc 😀

  4. Avatar

    Em cảm ơn anh, anh viết rất dễ hiểu ạ
    Giả sử như trong bảng developer_project yêu cầu thêm 1 cột “Nhiệm vụ” để thể hiện nhiệm vụ của devloper trong project đó thì mình thiết kế sao a.
    Em giả sử thôi vì thường database thiết kế bảng n-n sẽ sinh ra cột phụ, chứ không đơn giản thì có 2 cột vừa làm khóa chính vừa làm khóa ngoại như thế này

      1. Avatar

        huhu,e đang làm cần làm project phần này a gợi ý giúp e trước em nghiên cứu thêm được không ạ?
        em nghĩ là tạo 1 entity mới developer_project, sau đó dùng manytoone và onetomany để nối giữa entity này với 2 entity developer_project, làm vậy được không anh? Cấu trúc 3 entity bây giờ sẽ là

        public class Project {

        @OneToMany(mappedBy = “project”)
        private Collection dev_pro;
        }

        public class Developer{

        @OneToMany(mappedBy = “developer”)
        private Collection dev_pro;
        }

        public class Developer_Project {

        @ManytoOne(mappedBy = “developer”)
        @JoinColumn(name = “developer_id”
        private Developer developer;
        @ManytoOne(mappedBy = “project”)
        @JoinColumn(name = “project_id”
        private Project project;
        @Column
        private String nhiemvu;
        }

Add Comment