คุณใช้บันทึกย่อ JPA @JoinTable ในกรณีใด


คำตอบ:


351

EDIT 2017-04-29 : ตามที่ผู้วิจารณ์บางคนชี้ให้เห็นJoinTableตัวอย่างไม่จำเป็นต้องมีmappedByแอตทริบิวต์คำอธิบายประกอบ อันที่จริงไฮเบอร์เนตรุ่นล่าสุดปฏิเสธที่จะเริ่มต้นด้วยการพิมพ์ข้อผิดพลาดต่อไปนี้:

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

สมมติว่าคุณมีชื่อเอนทิตีProjectและอีกชื่อเอนทิตีTaskและแต่ละโครงการสามารถมีงานได้มากมาย

คุณสามารถออกแบบคีมาฐานข้อมูลสำหรับสถานการณ์นี้ได้สองวิธี

วิธีแก้ปัญหาแรกคือการสร้างชื่อตารางProjectและอีกชื่อหนึ่งTaskและเพิ่มคอลัมน์กุญแจต่างประเทศในตารางงานที่ชื่อว่าproject_id:

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

วิธีนี้จะเป็นไปได้ที่จะกำหนดโครงการสำหรับแต่ละแถวในตารางงาน ถ้าคุณใช้วิธีนี้ในคลาสเอนทิตี้ของคุณคุณไม่จำเป็นต้องเข้าร่วมตาราง:

@Entity
public class Project {

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

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

โซลูชันอื่นคือใช้ตารางที่สามเช่นProject_Tasksและเก็บความสัมพันธ์ระหว่างโครงการและงานในตารางนั้น:

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

Project_Tasksตารางที่เรียกว่า "เข้าร่วมโต๊ะ" ในการใช้โซลูชันที่สองนี้ใน JPA คุณต้องใช้@JoinTableคำอธิบายประกอบ ตัวอย่างเช่นในการดำเนินการเชื่อมโยงแบบหนึ่งต่อหลายกลุ่มเราสามารถกำหนดเอนทิตีของเราดังนี้:

Project นิติบุคคล:

@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 นิติบุคคล:

@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;
    }

}

สิ่งนี้จะสร้างโครงสร้างฐานข้อมูลต่อไปนี้:

แผนผัง ER 1

@JoinTableคำอธิบายประกอบยังช่วยให้คุณสามารถปรับแต่งด้านต่างๆของการเข้าร่วมโต๊ะ ตัวอย่างเช่นหากเราใส่คำอธิบายประกอบtasksคุณสมบัติเช่นนี้:

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

ฐานข้อมูลผลลัพธ์จะกลายเป็น:

แผนผัง ER 2

ท้ายที่สุดถ้าคุณต้องการสร้างสคีมาสำหรับการเชื่อมโยงแบบกลุ่มต่อกลุ่มการใช้ตารางการเข้าร่วมเป็นโซลูชันที่มีให้เท่านั้น


1
ใช้วิธีแรกที่ฉันมีโครงการของฉันเต็มไปด้วยงานของฉันและแต่ละงานที่เต็มไปด้วยโครงการหลักก่อนที่จะผสานและทำงาน แต่รายการของฉันทั้งหมดจะถูกทำซ้ำตามจำนวนงานของฉัน โปรเจ็กต์ที่มีสองงานจะบันทึกสองครั้งในฐานข้อมูลของฉัน ทำไม
MaikoID

อัปเดตไม่มีรายการที่ซ้ำกันในฐานข้อมูลของฉันจำศีลกำลังเลือกเข้าร่วมด้านนอกด้านซ้ายและฉันไม่รู้ว่าทำไม ..
MaikoID

2
ฉันเชื่อ @JoinTable/@JoinColumnmappedByสามารถข้อเขียนบนสนามเดียวกันกับ ดังนั้นตัวอย่างที่ถูกต้องควรจะเก็บไว้mappedByในProjectและย้าย@JoinColumnไปที่Task.project (หรือในทางกลับกัน)
Adrian Shum

2
ดี! แต่ฉันมีคำถามเพิ่มเติม: ถ้าเข้าร่วมตารางProject_Tasksความต้องการnameของTaskเช่นกันซึ่งจะกลายเป็นสามคอลัมน์: project_id, task_id, task_nameวิธีการเพื่อให้บรรลุนี้?
macemers

5
ฉันคิดว่าคุณไม่ควรแมปโดยในตัวอย่างการใช้ครั้งที่สองของคุณเพื่อป้องกันข้อผิดพลาดนี้ Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m

14

นอกจากนี้ยังสะอาดกว่าการใช้ @JoinTableเมื่อกิจการสามารถเป็นเด็กในความสัมพันธ์ของผู้ปกครอง / เด็กหลายคนกับผู้ปกครองประเภทต่างๆ ในการติดตามตัวอย่างของ Behrang ให้จินตนาการถึงภารกิจที่สามารถเป็นลูกของโครงการบุคคลแผนกการศึกษาและกระบวนการ

taskตารางควรมี 5 nullableเขตข้อมูลสำคัญต่างประเทศหรือไม่ ผมคิดว่าไม่...


14

มันเป็นทางออกเดียวที่จะจับคู่การเชื่อมโยง ManyToMany: คุณต้องเข้าร่วมตารางระหว่างตารางเอนทิตีสองแห่งเพื่อจับคู่การเชื่อมโยง

นอกจากนี้ยังใช้สำหรับการเชื่อมโยง OneToMany (โดยปกติเป็นทิศทางเดียว) เมื่อคุณไม่ต้องการเพิ่มคีย์ต่างประเทศในตารางของหลาย ๆ ด้านและทำให้เป็นอิสระจากด้านใดด้านหนึ่ง

ค้นหา @JoinTable ในเอกสาร hibernateสำหรับคำอธิบายและตัวอย่าง


4

มันช่วยให้คุณจัดการความสัมพันธ์แบบหลายต่อหลายคนได้ ตัวอย่าง:

Table 1: post

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

Table 2: user

user has the following columns:

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

ตารางเข้าร่วมให้คุณสร้างแผนที่โดยใช้:

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

จะสร้างตาราง:

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

1
คำถาม: ถ้าฉันมีตารางเพิ่มเติมนี้อยู่แล้ว JoinTable จะไม่เขียนทับสิ่งที่มีอยู่ใช่ไหม?
TheWandererr

@TheWandererr คุณหาคำตอบสำหรับคำถามของคุณ? ฉันมีโต๊ะเข้าร่วมแล้ว
2560

ในกรณีของฉันมันสร้างคอลัมน์ซ้ำซ้อนในตารางด้านข้างของตัวเอง สำหรับเช่น POST_ID ใน POST คุณช่วยแนะนำได้ไหมว่าทำไมมันถึงเกิดขึ้น
SPS

0

@ManyToMany สมาคม

บ่อยครั้งที่คุณจะต้องใช้@JoinTableคำอธิบายประกอบเพื่อระบุการแมปความสัมพันธ์ของตารางแบบหลายต่อหลายกลุ่ม:

  • ชื่อของตารางลิงก์และ
  • คอลัมน์ Foreign Key สองคอลัมน์

ดังนั้นสมมติว่าคุณมีตารางฐานข้อมูลต่อไปนี้:

ความสัมพันธ์หลายต่อหลายตาราง

ในPostเอนทิตีคุณจะแมปความสัมพันธ์นี้เช่นนี้

@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<>();

@JoinTableคำอธิบายประกอบถูกใช้เพื่อระบุชื่อตารางผ่านnameแอตทริบิวต์เช่นเดียวกับคอลัมน์ที่สำคัญต่างประเทศที่อ้างอิงpostตาราง (เช่นjoinColumns) และคอลัมน์ที่สำคัญต่างประเทศในpost_tagตารางการเชื่อมโยงที่อ้างอิงTagนิติบุคคลผ่านinverseJoinColumnsแอตทริบิวต์

โปรดสังเกตว่าแอตทริบิวต์ cascade ของ@ManyToManyคำอธิบายประกอบถูกตั้งค่าเป็นPERSISTและMERGEเนื่องจากการเรียงซ้อนREMOVEเป็นแนวคิดที่ไม่ดีเนื่องจากเราจะมีการใช้คำสั่ง DELETE สำหรับเร็กคอร์ดพาเรนต์อื่นtagในกรณีของเราไม่ใช่post_tagบันทึก สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อนี้ตรวจสอบบทความนี้

การ@OneToManyเชื่อมโยงทิศทางเดียว

การ@OneToManyเชื่อมโยงทิศทางเดียวที่ขาด@JoinColumnแมปจะทำงานเหมือนกับความสัมพันธ์ของตารางหลายต่อหลายกลุ่มมากกว่าหนึ่งต่อหลาย

ดังนั้นสมมติว่าคุณมีการแมปเอนทิตีต่อไปนี้:

@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
}

ไฮเบอร์เนตจะถือว่าสคีมาฐานข้อมูลต่อไปนี้สำหรับการจับคู่เอนทิตีด้านบน:

ทิศทางเดียว <code> @OneToMany </code> ตารางฐานข้อมูลการเชื่อมโยง JPA

ดังที่ได้อธิบายไปแล้วการจับคู่@OneToManyJPA แบบทิศทางเดียวมีลักษณะเหมือนการเชื่อมโยงแบบกลุ่มต่อกลุ่ม

หากต้องการกำหนดตารางลิงก์เองคุณสามารถใช้@JoinTableคำอธิบายประกอบได้:

@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<>();

และตอนนี้ตารางการเชื่อมโยงเป็นไปได้เรียกว่าpost_comment_refและคอลัมน์ที่สำคัญต่างประเทศจะเป็นpost_idสำหรับpostตารางและpost_comment_idสำหรับpost_commentตาราง

การ@OneToManyเชื่อมโยงแบบทิศทางเดียวนั้นไม่มีประสิทธิภาพดังนั้นคุณควรใช้การ@OneToManyเชื่อมโยงแบบสองทิศทางหรือ@ManyToOneด้านข้าง ลองอ่านบทความนี้สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.