Trong trường hợp nào bạn sử dụng chú thích JPA @JoinTable?


Câu trả lời:


350

EDIT 2017-04-29 : Như được chỉ ra bởi một số người bình luận, JoinTableví dụ này không cần mappedBythuộc tính chú thích. Trên thực tế, các phiên bản gần đây của Hibernate từ chối khởi động bằng cách in lỗi sau:

org.hibernate.AnnotationException: 
   Associations marked as mappedBy must not define database mappings 
   like @JoinTable or @JoinColumn

Hãy giả vờ rằng bạn có một thực thể được đặt tên Projectvà một thực thể khác được đặt tên Taskvà mỗi dự án có thể có nhiều nhiệm vụ.

Bạn có thể thiết kế lược đồ cơ sở dữ liệu cho kịch bản này theo hai cách.

Giải pháp đầu tiên là tạo một bảng có tên Projectvà một bảng khác được đặt tên Taskvà thêm một cột khóa ngoại vào bảng tác vụ có tên project_id:

Project      Task
-------      ----
id           id
name         name
             project_id

Bằng cách này, sẽ có thể xác định dự án cho mỗi hàng trong bảng nhiệm vụ. Nếu bạn sử dụng phương pháp này, trong các lớp thực thể của bạn, bạn sẽ không cần một bảng tham gia:

@Entity
public class Project {

   @OneToMany(mappedBy = "project")
   private Collection<Task> tasks;

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

Giải pháp khác là sử dụng bảng thứ ba, ví dụ Project_Tasksvà lưu trữ mối quan hệ giữa các dự án và tác vụ trong bảng đó:

Project      Task      Project_Tasks
-------      ----      -------------
id           id        project_id
name         name      task_id

Các Project_Tasksbảng được gọi là "Tham Bảng". Để thực hiện giải pháp thứ hai này trong JPA, bạn cần sử dụng @JoinTablechú thích. Ví dụ: để triển khai liên kết một-nhiều-hướng, chúng ta có thể định nghĩa các thực thể của mình như sau:

Project thực thể:

@Entity
public class Project {

    @Id
    @GeneratedValue
    private Long pid;

    private String name;

    @JoinTable
    @OneToMany
    private List<Task> tasks;

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks = tasks;
    }
}

Task thực thể:

@Entity
public class Task {

    @Id
    @GeneratedValue
    private Long tid;

    private String name;

    public Long getTid() {
        return tid;
    }

    public void setTid(Long tid) {
        this.tid = tid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Điều này sẽ tạo ra cấu trúc cơ sở dữ liệu sau:

Sơ đồ ER 1

Các @JoinTablechú thích cũng cho phép bạn tùy chỉnh các khía cạnh khác nhau của bảng tham gia. Ví dụ: chúng tôi đã chú thích taskstài sản như thế này:

@JoinTable(
        name = "MY_JT",
        joinColumns = @JoinColumn(
                name = "PROJ_ID",
                referencedColumnName = "PID"
        ),
        inverseJoinColumns = @JoinColumn(
                name = "TASK_ID",
                referencedColumnName = "TID"
        )
)
@OneToMany
private List<Task> tasks;

Cơ sở dữ liệu kết quả sẽ trở thành:

Sơ đồ ER 2

Cuối cùng, nếu bạn muốn tạo một lược đồ cho liên kết nhiều-nhiều, sử dụng bảng tham gia là giải pháp khả dụng duy nhất.


1
sử dụng cách tiếp cận đầu tiên, tôi có Dự án của mình chứa đầy các nhiệm vụ của mình và mỗi Nhiệm vụ được điền vào Dự án mẹ trước khi hợp nhất và hoạt động nhưng tất cả các mục nhập của tôi được sao chép dựa trên số lượng nhiệm vụ của tôi. Một dự án với hai nhiệm vụ được lưu hai lần trong cơ sở dữ liệu của tôi. Tại sao ?
MaikoID

CẬP NHẬT Không có mục trùng lặp trong cơ sở dữ liệu của tôi, hibernate đang chọn với kết nối bên ngoài bên trái và tôi không biết tại sao ..
MaikoID

2
Tôi tin rằng @JoinTable/@JoinColumncó thể được chú thích trên cùng một lĩnh vực với mappedBy. Vì vậy, ví dụ chính xác nên được giữ mappedBytrong Project, và di chuyển @JoinColumnđến Task.project (hoặc ngược lại)
Adrian Shum

2
Đẹp! Nhưng tôi có một câu hỏi nữa: nếu tham gia bảng Project_Taskscần namecủa Tasklà tốt, mà sẽ trở thành ba cột: project_id, task_id, task_name, làm thế nào để đạt được điều này?
macemers

5
Tôi nghĩ rằng bạn không nên lập bản đồBy trên ví dụ sử dụng thứ hai của mình để ngăn chặn lỗi nàyCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m

14

Nó cũng sạch hơn @JoinTablekhi sử dụng khi một Thực thể có thể là đứa trẻ trong một số mối quan hệ cha mẹ / con cái với các loại cha mẹ khác nhau. Để theo dõi ví dụ của Behrang, hãy tưởng tượng một Nhiệm vụ có thể là con của Dự án, Người, Bộ phận, Nghiên cứu và Quy trình.

Nên các taskbảng có 5 nullablelĩnh vực trọng điểm nước ngoài? Tôi nghĩ là không ...


14

Đó là giải pháp duy nhất để ánh xạ liên kết ManyToMany: bạn cần một bảng nối giữa hai bảng thực thể để ánh xạ liên kết.

Nó cũng được sử dụng cho các hiệp hội OneToMany (thường là một chiều) khi bạn không muốn thêm khóa ngoại vào bảng của nhiều bên và do đó giữ cho nó độc lập với một bên.

Tìm kiếm @JoinTable trong tài liệu ngủ đông để giải thích và ví dụ.


4

Nó cho phép bạn xử lý nhiều mối quan hệ Nhiều. Thí dụ:

Table 1: post

post has following columns
____________________
|  ID     |  DATE   |
|_________|_________|
|         |         |
|_________|_________|

Table 2: user

user has the following columns:

____________________
|     ID  |NAME     |
|_________|_________|
|         |         |
|_________|_________|

Tham gia Bảng cho phép bạn tạo ánh xạ bằng cách sử dụng:

@JoinTable(
  name="USER_POST",
  joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
  inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))

sẽ tạo một bảng:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|

1
Câu hỏi: nếu tôi đã có bảng bổ sung này thì sao? JoinTable sẽ không ghi đè lên cái tồn tại phải không?
TheWandererr

@TheWandererr bạn đã tìm ra câu trả lời cho câu hỏi của mình chưa? Tôi đã có một bảng tham gia rồi
vào

Trong trường hợp của tôi, nó tạo ra một cột dư thừa trong bảng bên sở hữu. ví dụ POST_ID trong POST. Bạn có thể đề nghị tại sao nó xảy ra?
SPS

0

@ManyToMany hiệp hội

Thông thường, bạn sẽ cần sử dụng @JoinTablechú thích để chỉ định ánh xạ của mối quan hệ nhiều-nhiều bảng:

  • tên của bảng liên kết và
  • hai cột khóa ngoại

Vì vậy, giả sử bạn có các bảng cơ sở dữ liệu sau:

Mối quan hệ nhiều-nhiều

Trong Postthực thể, bạn sẽ ánh xạ mối quan hệ này, như thế này:

@ManyToMany(cascade = {
    CascadeType.PERSIST,
    CascadeType.MERGE
})
@JoinTable(
    name = "post_tag",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();

Các @JoinTablechú thích được sử dụng để chỉ định tên bảng thông qua namethuộc tính, cũng như cột Ngoại chính là tài liệu tham khảo các postbảng (ví dụ joinColumns) và cột Ngoại chính trong post_tagbảng liên kết tài liệu tham khảo các Tagthực thể thông qua các inverseJoinColumnsthuộc tính.

Lưu ý rằng thuộc tính tầng của @ManyToManychú thích được đặt thành PERSISTMERGEchỉ bởi vì xếp tầng REMOVElà một ý tưởng tồi vì chúng ta sẽ phát hành câu lệnh XÓA cho bản ghi cha khác, tagtrong trường hợp của chúng ta, không phải cho post_tagbản ghi. Để biết thêm chi tiết về chủ đề này, hãy xem bài viết này .

Đơn hướng @OneToManyHiệp hội

Các @OneToManyhiệp hội đơn hướng , thiếu một@JoinColumn ánh xạ, hoạt động giống như các mối quan hệ nhiều-nhiều, thay vì một-nhiều.

Vì vậy, giả sử bạn có ánh xạ thực thể sau:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @OneToMany(
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    //Constructors, getters and setters removed for brevity
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    //Constructors, getters and setters removed for brevity
}

Hibernate sẽ giả sử lược đồ cơ sở dữ liệu sau cho ánh xạ thực thể ở trên:

Các bảng cơ sở dữ liệu liên kết <code> @OneToMany </ code> JPA đơn hướng

Như đã giải thích, @OneToManyánh xạ JPA đơn hướng hoạt động giống như một hiệp hội nhiều-nhiều.

Để tùy chỉnh bảng liên kết, bạn cũng có thể sử dụng @JoinTablechú thích:

@OneToMany(
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
@JoinTable(
    name = "post_comment_ref",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();

Và bây giờ, bảng liên kết sẽ được gọi post_comment_refvà các cột Khóa ngoài sẽ post_id, cho postbảng và post_comment_id, cho post_commentbảng.

Các @OneToManyhiệp hội đơn hướng không hiệu quả, vì vậy bạn nên sử dụng các @OneToManyhiệp hội hai chiều hoặc chỉ là @ManyToOnephụ. Kiểm tra bài viết này để biết thêm chi tiết về chủ đề này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.