การแม็พตารางการเชื่อมโยงแบบกลุ่มต่อกลุ่มด้วยคอลัมน์พิเศษ


131

ฐานข้อมูลของฉันมี 3 ตาราง: เอนทิตีผู้ใช้และบริการมีความสัมพันธ์แบบกลุ่มต่อกลุ่มและเข้าร่วมกับตาราง SERVICE_USER ดังนี้:

ผู้ใช้ - SERVICE_USER - บริการ

ตาราง SERVICE_USER มีคอลัมน์ BLOCKED เพิ่มเติม

วิธีใดที่ดีที่สุดในการทำแผนที่ดังกล่าว นี่คือคลาสเอนทิตีของฉัน

@Entity
@Table(name = "USERS")
public class User implements java.io.Serializable {

private String userid;
private String email;

@Id
@Column(name = "USERID", unique = true, nullable = false,)
public String getUserid() {
return this.userid;
}

.... some get/set methods
}

@Entity
@Table(name = "SERVICES")
public class CmsService implements java.io.Serializable {
private String serviceCode;

@Id
@Column(name = "SERVICE_CODE", unique = true, nullable = false, length = 100)
public String getServiceCode() {
return this.serviceCode;
}
.... some additional fields and get/set methods
}

ฉันทำตามตัวอย่างนี้http://giannigar.wordpress.com/2009/09/04/m ... using-jpa / นี่คือรหัสทดสอบ:

User user = new User();
user.setEmail("e2");
user.setUserid("ui2");
user.setPassword("p2");

CmsService service= new CmsService("cd2","name2");

List<UserService> userServiceList = new ArrayList<UserService>();

UserService userService = new UserService();
userService.setService(service);
userService.setUser(user);
userService.setBlocked(true);
service.getUserServices().add(userService);

userDAO.save(user);

ปัญหาคือไฮเบอร์เนตยังคงมีอ็อบเจ็กต์ผู้ใช้และ UserService หนึ่ง ไม่ประสบความสำเร็จกับวัตถุ CmsService

ฉันพยายามใช้ EAGER Fetch - ไม่มีความคืบหน้า

เป็นไปได้หรือไม่ที่จะบรรลุพฤติกรรมที่ฉันคาดหวังด้วยการทำแผนที่ที่ให้ไว้ข้างต้น

อาจจะมีวิธีที่หรูหรากว่าในการทำแผนที่หลายตารางเข้าร่วมกับคอลัมน์เพิ่มเติมหรือไม่?

คำตอบ:


192

เนื่องจากตาราง SERVICE_USER ไม่ใช่ตารางการรวมแบบแท้ แต่มีฟิลด์ที่ใช้งานได้เพิ่มเติม (ถูกบล็อก) คุณต้องแมปเป็นเอนทิตีและแยกการเชื่อมโยงระหว่างผู้ใช้และบริการออกเป็นสองการเชื่อมโยง OneToMany: ผู้ใช้รายหนึ่งมี UserServices จำนวนมาก และหนึ่งบริการมี UserServices จำนวนมาก

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

หากคุณสร้างความสัมพันธ์แบบสองทิศทางคุณควรมี

class User {
    @OneToMany(mappedBy = "user")
    private Set<UserService> userServices = new HashSet<UserService>();
}

class UserService {
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne
    @JoinColumn(name = "service_code")
    private Service service;

    @Column(name = "blocked")
    private boolean blocked;
}

class Service {
    @OneToMany(mappedBy = "service")
    private Set<UserService> userServices = new HashSet<UserService>();
}

หากคุณไม่ได้ใส่ความสัมพันธ์แบบเรียงซ้อนคุณต้องคงอยู่ / บันทึกเอนทิตีทั้งหมด แม้ว่าจะต้องเริ่มต้นเฉพาะด้านที่เป็นเจ้าของความสัมพันธ์ (ในที่นี้คือฝั่ง UserService) แต่ก็เป็นแนวทางปฏิบัติที่ดีเพื่อให้แน่ใจว่าทั้งสองฝ่ายสอดคล้องกัน

User user = new User();
Service service = new Service();
UserService userService = new UserService();

user.addUserService(userService);
userService.setUser(user);

service.addUserService(userService);
userService.setService(service);

session.save(user);
session.save(service);
session.save(userService);

2
เพียงแค่เพิ่ม .. แม้ว่านี่จะเป็นวิธีที่ดีที่สุดในความคิดของฉัน (ฉันมักชอบจับคู่สิ่งที่เป็นเจ้าของ FK เป็นเอนทิตีเพื่อเหตุผลด้านประสิทธิภาพ) แต่ในความเป็นจริงมันไม่ใช่วิธีเดียว คุณยังสามารถแมปค่าจากตาราง SERVICE_USER เป็นส่วนประกอบ (สิ่งที่ JPA เรียกว่าสามารถฝังได้) และใช้@ElementCollectionจากเอนทิตีผู้ใช้และบริการจาก (หรือทั้งสองอย่าง)
Steve Ebersole

6
แล้วคีย์หลักของตาราง UserService ล่ะ? ควรเป็นคีย์ต่างประเทศของผู้ใช้และบริการร่วมกัน ที่แมป?
Jonas Gröger

24
ฉันจะไม่เป็นอย่างนั้น คีย์คอมโพสิตนั้นเจ็บปวดไม่มีประสิทธิภาพและไฮเบอร์เนตไม่แนะนำให้ใช้คีย์คอมโพสิต เพียงใช้ ID ที่สร้างขึ้นโดยอัตโนมัติเช่นเดียวกับเอนทิตีอื่น ๆ ชีวิตจะง่ายขึ้นมาก เพื่อให้แน่ใจว่ามีความเป็นเอกภาพ[userFK, serviceFK]ให้ใช้ข้อ จำกัด เฉพาะ
JB Nizet

1
@GaryKephart: ถามคำถามของคุณเองด้วยรหัสของคุณเองและการทำแผนที่ของคุณเอง
JB Nizet

1
@gstackoverflow: Hibernate 4 ไม่เปลี่ยนแปลงอะไรในเรื่องนั้น ฉันไม่เห็นว่ามันไม่สง่างามแค่ไหน
JB Nizet

5

ฉันค้นหาวิธีแมปตารางการเชื่อมโยงแบบกลุ่มต่อกลุ่มกับคอลัมน์พิเศษที่มีการกำหนดค่าไฮเบอร์เนตในไฟล์ xml

สมมติว่ามีสองตาราง 'a' & 'c' ที่มีการเชื่อมโยงแบบ many to many กับคอลัมน์ชื่อ 'extra' สาเหตุที่ฉันไม่พบตัวอย่างที่สมบูรณ์นี่คือรหัสของฉัน หวังว่าจะช่วยได้ :).

อันดับแรกคือวัตถุ Java

public class A implements Serializable{  

    protected int id;
    // put some others fields if needed ...   
    private Set<AC> ac = new HashSet<AC>();

    public A(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Set<AC> getAC() {
        return ac;
    }

    public void setAC(Set<AC> ac) {
        this.ac = ac;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        final int prime = 97;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof A))
            return false;
        final A other = (A) obj;
        if (id != other.getId())
            return false;
        return true;
    }

}

public class C implements Serializable{

    protected int id;
    // put some others fields if needed ...    

    public C(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        final int prime = 98;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof C))
            return false;
        final C other = (C) obj;
        if (id != other.getId())
            return false;
        return true;
    }

}

ตอนนี้เราต้องสร้างตารางการเชื่อมโยง ขั้นตอนแรกคือการสร้างวัตถุแทนคีย์หลักที่ซับซ้อน (a.id, c.id)

public class ACId implements Serializable{

    private A a;
    private C c;

    public ACId() {
        super();
    }

    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
    public C getC() {
        return c;
    }
    public void setC(C c) {
        this.c = c;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((a == null) ? 0 : a.hashCode());
        result = prime * result
                + ((c == null) ? 0 : c.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ACId other = (ACId) obj;
        if (a == null) {
            if (other.a != null)
                return false;
        } else if (!a.equals(other.a))
            return false;
        if (c == null) {
            if (other.c != null)
                return false;
        } else if (!c.equals(other.c))
            return false;
        return true;
    }
}

ตอนนี้ขอสร้างวัตถุเชื่อมโยงเอง

public class AC implements java.io.Serializable{

    private ACId id = new ACId();
    private String extra;

    public AC(){

    }

    public ACId getId() {
        return id;
    }

    public void setId(ACId id) {
        this.id = id;
    }

    public A getA(){
        return getId().getA();
    }

    public C getC(){
        return getId().getC();
    }

    public void setC(C C){
        getId().setC(C);
    }

    public void setA(A A){
        getId().setA(A);
    }

    public String getExtra() {
        return extra;
    }

    public void setExtra(String extra) {
        this.extra = extra;
    }

    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        AC that = (AC) o;

        if (getId() != null ? !getId().equals(that.getId())
                : that.getId() != null)
            return false;

        return true;
    }

    public int hashCode() {
        return (getId() != null ? getId().hashCode() : 0);
    }
}

ณ จุดนี้ถึงเวลาแมปคลาสทั้งหมดของเราด้วยการกำหนดค่า hibernate xml

A.hbm.xml และ C.hxml.xml (เงียบเหมือนกัน)

<class name="A" table="a">
        <id name="id" column="id_a" unsaved-value="0">
            <generator class="identity">
                <param name="sequence">a_id_seq</param>
            </generator>
        </id>
<!-- here you should map all others table columns -->
<!-- <property name="otherprop" column="otherprop" type="string" access="field" /> -->
    <set name="ac" table="a_c" lazy="true" access="field" fetch="select" cascade="all">
        <key>
            <column name="id_a" not-null="true" />
        </key>
        <one-to-many class="AC" />
    </set>
</class>

<class name="C" table="c">
        <id name="id" column="id_c" unsaved-value="0">
            <generator class="identity">
                <param name="sequence">c_id_seq</param>
            </generator>
        </id>
</class>

จากนั้นเชื่อมโยงไฟล์การแม็พ a_c.hbm.xml

<class name="AC" table="a_c">
    <composite-id name="id" class="ACId">
        <key-many-to-one name="a" class="A" column="id_a" />
        <key-many-to-one name="c" class="C" column="id_c" />
    </composite-id>
    <property name="extra" type="string" column="extra" />
</class>

นี่คือตัวอย่างโค้ดที่จะทดสอบ

A = ADao.get(1);
C = CDao.get(1);

if(A != null && C != null){
    boolean exists = false;
            // just check if it's updated or not
    for(AC a : a.getAC()){
        if(a.getC().equals(c)){
            // update field
            a.setExtra("extra updated");
            exists = true;
            break;
        }
    }

    // add 
    if(!exists){
        ACId idAC = new ACId();
        idAC.setA(a);
        idAC.setC(c);

        AC AC = new AC();
        AC.setId(idAC);
        AC.setExtra("extra added"); 
        a.getAC().add(AC);
    }

    ADao.save(A);
}

1

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

ตัวอย่างเช่นโค้ดการใช้งานของคลาสเสริมควรมีลักษณะดังนี้:

@Entity
@Table(name = "USER_SERVICES")
public class UserService{

    // example of auto-generated ID
    @Id
    @Column(name = "USER_SERVICES_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long userServiceID;



    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "USER_ID")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "SERVICE_ID")
    private Service service;



    // example of extra column
    @Column(name="VISIBILITY")    
    private boolean visibility;



    public long getUserServiceID() {
        return userServiceID;
    }


    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Service getService() {
        return service;
    }

    public void setService(Service service) {
        this.service = service;
    }

    public boolean getVisibility() {
        return visibility;
    }

    public void setVisibility(boolean visibility) {
        this.visibility = visibility;
    }

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