บางคนสามารถอธิบายแมปโดย JPA และไฮเบอร์เนต?


175

ฉันยังใหม่ต่อการจำศีลและจำเป็นต้องใช้ความสัมพันธ์แบบหนึ่งต่อหลายและแบบหนึ่งต่อหนึ่ง มันเป็นความสัมพันธ์แบบสองทิศทางในวัตถุของฉันเพื่อให้ฉันสามารถข้ามจากทิศทางใดก็ได้ mappedByเป็นวิธีที่แนะนำให้ไปเกี่ยวกับมันอย่างไรก็ตามฉันไม่เข้าใจ บางคนสามารถอธิบายได้:

  • วิธีที่แนะนำให้ใช้คืออะไร?
  • มันแก้จุดประสงค์อะไร?

เพื่อเป็นตัวอย่างของฉันนี่คือชั้นเรียนของฉันพร้อมคำอธิบายประกอบ:

  • Airline เป็นเจ้าของหลายคน AirlineFlights
  • หลายคน AirlineFlightsเป็นของONE Airline

สายการบิน :

@Entity 
@Table(name="Airline")
public class Airline {
    private Integer idAirline;
    private String name;

    private String code;

    private String aliasName;
    private Set<AirlineFlight> airlineFlights = new HashSet<AirlineFlight>(0);

    public Airline(){}

    public Airline(String name, String code, String aliasName, Set<AirlineFlight> flights) {
        setName(name);
        setCode(code);
        setAliasName(aliasName);
        setAirlineFlights(flights);
    }

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="IDAIRLINE", nullable=false)
    public Integer getIdAirline() {
        return idAirline;
    }

    private void setIdAirline(Integer idAirline) {
        this.idAirline = idAirline;
    }

    @Column(name="NAME", nullable=false)
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = DAOUtil.convertToDBString(name);
    }

    @Column(name="CODE", nullable=false, length=3)
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = DAOUtil.convertToDBString(code);
    }

    @Column(name="ALIAS", nullable=true)
    public String getAliasName() {
        return aliasName;
    }
    public void setAliasName(String aliasName) {
        if(aliasName != null)
            this.aliasName = DAOUtil.convertToDBString(aliasName);
    }

    @OneToMany(fetch=FetchType.LAZY, cascade = {CascadeType.ALL})
    @JoinColumn(name="IDAIRLINE")
    public Set<AirlineFlight> getAirlineFlights() {
        return airlineFlights;
    }

    public void setAirlineFlights(Set<AirlineFlight> flights) {
        this.airlineFlights = flights;
    }
}

AirlineFlights:

@Entity
@Table(name="AirlineFlight")
public class AirlineFlight {
    private Integer idAirlineFlight;
    private Airline airline;
    private String flightNumber;

    public AirlineFlight(){}

    public AirlineFlight(Airline airline, String flightNumber) {
        setAirline(airline);
        setFlightNumber(flightNumber);
    }

    @Id
    @GeneratedValue(generator="identity")
    @GenericGenerator(name="identity", strategy="identity")
    @Column(name="IDAIRLINEFLIGHT", nullable=false)
    public Integer getIdAirlineFlight() {
        return idAirlineFlight;
    }
    private void setIdAirlineFlight(Integer idAirlineFlight) {
        this.idAirlineFlight = idAirlineFlight;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="IDAIRLINE", nullable=false)
    public Airline getAirline() {
        return airline;
    }
    public void setAirline(Airline airline) {
        this.airline = airline;
    }

    @Column(name="FLIGHTNUMBER", nullable=false)
    public String getFlightNumber() {
        return flightNumber;
    }
    public void setFlightNumber(String flightNumber) {
        this.flightNumber = DAOUtil.convertToDBString(flightNumber);
    }
}

แก้ไข:

สคีมาฐานข้อมูล:

สายการบินมีสายการบิน idAirline เนื่องจาก ForeignKey และสายการบินไม่มีสายการบิน idAirline สิ่งนี้ทำให้ AirlineFlights เป็นเจ้าของ / ระบุนิติบุคคล?

ตามทฤษฎีแล้วฉันต้องการให้สายการบินเป็นเจ้าของสายการบินเที่ยวบิน

ป้อนคำอธิบายรูปภาพที่นี่

คำตอบ:


151

ด้วยการระบุ@JoinColumnทั้งสองแบบคุณไม่มีความสัมพันธ์แบบสองทาง คุณมีความสัมพันธ์แบบทางเดียวสองทางและการทำแผนที่แบบสับสนมากในตอนนั้น คุณกำลังบอกทั้งสองรุ่นว่าพวกเขา "เป็นเจ้าของ" คอลัมน์ IDAIRLINE จริง ๆ แล้วควรมีเพียงหนึ่งในนั้น! สิ่งที่ 'ปกติ' คือการใช้@JoinColumnออกจาก@OneToManyด้านข้างอย่างสิ้นเชิงและแทนที่จะเพิ่ม mappedBy @OneToManyไป

@OneToMany(cascade = CascadeType.ALL, mappedBy="airline")
public Set<AirlineFlight> getAirlineFlights() {
    return airlineFlights;
}

นั่นบอกไฮเบอร์เนต "ไปดูที่ถั่วที่มีชื่อว่า 'สายการบิน' ในสิ่งที่ฉันมีชุดของเพื่อค้นหาการกำหนดค่า"


2
ฉันสับสนเล็กน้อยตามคำอธิบายของคุณในที่สุดเกี่ยวกับแมปโดย มันมีความสำคัญอย่างไรในการจัดระเบียบสิ่งต่าง ๆ ใน db? @DB: AirlineFlights มี idAirline เป็น Foreign Key สายการบินมี idAirline เป็นคีย์หลักและไม่มีข้อมูลเกี่ยวกับ AirlineFlights @ DB
brainydexter

10
ใช่มันเป็นเรื่องสำคัญ ชื่อใน mappedBy กำลังบอก Hibernate ว่าจะค้นหาการกำหนดค่าสำหรับ JoinColumn ได้อย่างไร (ใน getAirline () วิธีการ AirlineFlight.) วิธีที่คุณแมปมันวาง JoinColumn สายการบินคุณจะบอกสายการบินว่ามันคือรับผิดชอบในการรักษาค่ากว่าในตารางอื่น ๆ เป็นไปได้ที่จะบอกเอนทิตีว่า "เป็นเจ้าของ" คอลัมน์ในตารางอื่นและรับผิดชอบในการอัพเดต ไม่ใช่สิ่งที่คุณต้องการทำและอาจทำให้เกิดปัญหากับคำสั่ง SQL ที่สั่งดำเนินการ
Affe

โปรดดูการแก้ไข ที่ระดับฐานข้อมูลตาราง AirlinesFlight เป็นเจ้าของ idAirline เป็นคอลัมน์คีย์ต่างประเทศ ดังนั้น JoinColumn ควรวางสายการบินชั้น / ตารางใน ManytoOne ที่เกี่ยวข้องเนื่องจากเป็นเจ้าของคอลัมน์นั้น
brainydexter

ใช่ฉันอยากจะแนะนำให้ทำอย่างนั้น มันเป็นตัวเลือกที่ซับซ้อนน้อยที่สุดและคุณไม่ต้องการอะไรอีกแล้ว
Affe

"ถอด @JoinColumn ออกจากฝั่ง @OneToMany โดยสิ้นเชิง" คุณหมายถึง@ManyToOneด้านข้างใช่มั้ย
nbro

284

สัญญาณที่แมปโดยจำศีลว่ากุญแจสำหรับความสัมพันธ์นั้นอยู่อีกด้านหนึ่ง

ซึ่งหมายความว่าแม้ว่าคุณจะเชื่อมโยงตาราง 2 ตารางเข้าด้วยกัน แต่เพียง 1 ตารางเท่านั้นที่มีข้อ จำกัด foreign key ไปยังอีกตารางหนึ่ง MappedBy ช่วยให้คุณสามารถเชื่อมโยงจากตารางที่ไม่มีข้อ จำกัด ไปยังตารางอื่น


3
คุณช่วยอธิบายเพิ่มเติมอีกหน่อยได้ไหม
Alexander Suraphel

1
@ Kurt Du Bois ทำไมคุณถึงใช้mappedByมากกว่ากำหนดสองทิศทาง (พร้อมกับข้อ จำกัด foreign_key ในแต่ละด้าน)
เควินเมเรดิ ธ

6
เพราะบางครั้งมันก็ไม่สมเหตุสมผลที่จะวางกุญแจทั้งสองข้าง พูดเช่นคุณมี บริษัท และพกพา อุปกรณ์พกพาจะเป็นของ บริษัท เดียวเท่านั้น แต่ บริษัท จะมีหลายพอร์ต
Kurt Du Bois

ขออภัยที่มีคนแก้ไขเพื่อย้อนคำตอบของฉัน แต่จริงๆแล้วไม่มีมูลค่าเพิ่มในการแก้ไขของคุณ ประโยคสุดท้ายไม่สมเหตุสมผล
Kurt Du Bois

@ KurtDuBois ถูกแมปโดยเข้ามาในรูปภาพเท่านั้นว่าจะสร้างฐานข้อมูลของคุณอย่างไรเช่นคุณใช้ mappedby หรือไม่จำศีลที่ฝั่ง java มีวิธีการทำงานที่คล้ายกันหรือไม่

22

mappedbyพูดเพื่อตัวเองมันบอกให้จำศีลไม่ให้แมปฟิลด์นี้ มันถูกแมปแล้วโดยฟิลด์นี้ [name = "field"]
ฟิลด์อยู่ในเอนทิตีอื่น(name of the variable in the class not the table in the database)..

หากคุณไม่ทำเช่นนั้นไฮเบอร์เนตจะจับคู่ความสัมพันธ์ทั้งสองนี้เนื่องจากมันไม่ได้มีความสัมพันธ์แบบเดียวกัน

ดังนั้นเราจำเป็นต้องบอกให้จำศีลเพื่อทำแผนที่ในด้านหนึ่งเท่านั้นและประสานระหว่างพวกเขา


mappedBy เป็นตัวเลือกหรือไม่ เพราะโดยไม่ใช้ mapped โดยฉันได้รับผลลัพธ์เดียวกันเช่นการทำแผนที่วัตถุสองทิศทาง
Freelancer

คุณไม่สามารถใช้ on2many และ many2one โดยไม่ใช้ mapped โดยหนึ่งในสิ่งเดียวกันด้านข้างสำหรับหลาย ๆ 2many คุณต้องใช้ mappedBy ในด้านหนึ่ง
Charif DZ

ขอบคุณที่ชี้ให้เห็นว่าค่าของแอตทริบิวต์หมายถึงอะไรซึ่งเป็นชื่อของเขตข้อมูลในตารางอื่น ๆ
Gab 是好人

2
บางทีไฮเบอร์เนตอาจไม่ได้พูดเพื่อตัวของมันเอง แต่อย่างน้อยก็ใช้เครื่องหมายวรรคตอน
Amalgovinus

1
สำหรับฉันมันไม่ได้พูดเพื่อตัวเอง; ตรงกันข้ามมันสับสนมาก เพียงแค่ดูที่จำนวนของคำถามเกี่ยวกับสิ่งที่เป็นจริงและmappedBy inversedByORM อื่น ๆ ใช้คุณสมบัติที่ชาญฉลาดbelongsToManyกว่าhasManyนี้มาก
Jan Bodnar

12

mappedby = "วัตถุของเอนทิตีของคลาสเดียวกันที่สร้างในคลาสอื่น”

หมายเหตุ: - แม็พโดยสามารถใช้ได้เฉพาะในหนึ่งคลาสเนื่องจากหนึ่งตารางต้องมีข้อ จำกัด foreign key หากแมปโดยสามารถนำไปใช้กับทั้งสองด้านแล้วมันจะลบต่างประเทศที่สำคัญจากตารางทั้งสองและไม่มีต่างประเทศที่สำคัญไม่มีความสัมพันธ์ b / w สองตาราง

หมายเหตุ: - สามารถใช้สำหรับคำอธิบายประกอบต่อไปนี้: - 1. @ OneTone 2. @ OneToMany 3. @ ManyToMany

หมายเหตุ --- ไม่สามารถใช้สำหรับคำอธิบายประกอบต่อไปนี้: - 1. @ ManyToOne

ในแบบหนึ่งต่อหนึ่ง: - ดำเนินการที่ด้านใด ๆ ของการแมป แต่ดำเนินการที่ด้านเดียวเท่านั้น มันจะลบคอลัมน์พิเศษของข้อ จำกัด ที่สำคัญต่างประเทศบนตารางที่มันถูกนำไปใช้ในชั้นเรียน

สำหรับเช่น ถ้าเราใช้แมปโดยในระดับพนักงานบนวัตถุพนักงานแล้วรหัสต่างประเทศจากตารางพนักงานจะถูกลบออก


12

ความสัมพันธ์ของตารางเทียบกับความสัมพันธ์เอนทิตี

ในระบบฐานข้อมูลเชิงสัมพันธ์ความสัมพันธ์ของone-to-manyตารางจะมีลักษณะดังนี้:

<code> ความสัมพันธ์ของตารางแบบตัวต่อตัว </code>

โปรดทราบว่าความสัมพันธ์จะขึ้นอยู่กับคอลัมน์ Foreign Key (เช่นpost_id) ในตารางลูก

ดังนั้นจึงมีแหล่งความจริงเดียวเมื่อพูดถึงการจัดการ one-to-manyความสัมพันธ์ของตาราง

ตอนนี้ถ้าคุณใช้ความสัมพันธ์เอนทิตีแบบสองทิศทางที่แมปกับone-to-manyความสัมพันธ์ของตารางที่เราเห็นก่อนหน้านี้:

<code> การเชื่อมโยงเอนทิตีแบบ One-To-Many </code> แบบสองทิศทาง

หากคุณดูที่แผนภาพด้านบนคุณจะเห็นว่ามีสองวิธีในการจัดการความสัมพันธ์นี้

ในPostนิติบุคคลคุณมีการcommentsรวบรวม:

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

และในPostCommentการpostเชื่อมโยงเป็นแมปดังต่อไปนี้:

@ManyToOne(
    fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;

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

MappedBy

mappedByแอตทริบิวต์บอกว่า@ManyToOneด้านเป็นค่าใช้จ่ายในการจัดการคอลัมน์ต่างประเทศที่สำคัญและคอลเลกชันจะใช้เพื่อดึงข้อมูลหน่วยงานของเด็กและการเปลี่ยนแปลงสถานะน้ำตกปกครองนิติบุคคลให้กับเด็ก ๆ (เช่นการเอาผู้ปกครองก็ควรเอาหน่วยงานเด็ก)

ประสานทั้งสองด้านของความสัมพันธ์แบบสองทิศทาง

ตอนนี้แม้ว่าคุณจะกำหนดmappedByแอตทริบิวต์และการ@ManyToOneเชื่อมโยงฝั่งเด็กจัดการคอลัมน์ Foreign Key คุณยังคงต้องซิงโครไนซ์ทั้งสองด้านของการเชื่อมโยงแบบสองทิศทาง

วิธีที่ดีที่สุดในการทำเช่นนั้นคือการเพิ่มวิธีการอรรถประโยชน์ทั้งสองนี้:

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

กระบวนการaddCommentและremoveCommentวิธีการทำให้มั่นใจได้ว่าทั้งสองฝ่ายได้รับการทำข้อมูลให้ตรงกัน ดังนั้นถ้าเราเพิ่มเอนทิตีลูกเอนทิตีลูกต้องชี้ไปที่พาเรนต์และเอนทิตีพาเรนต์ควรมีลูกที่อยู่ในคอลเลกชันลูก

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่ดีที่สุดในการประสานทุกประเภทสองทิศทางสมาคมนิติบุคคลตรวจสอบบทความนี้


0

คุณเริ่มต้นด้วยการแมป ManyToOne จากนั้นคุณทำการแมป OneToMany เช่นกันสำหรับวิธี BiDirectional จากนั้นที่ด้าน OneToMany (โดยปกติจะเป็นตารางหลัก / คลาสของคุณ) คุณต้องพูดถึง "mappedBy" (การแมปทำได้โดยและในตารางลูก / คลาส) ดังนั้นไฮเบอร์เนตจะไม่สร้างตารางการแมปพิเศษใน DB (เช่น TableName = parent_child)

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