Ánh xạ bảng kết hợp nhiều-nhiều với cột thêm


130

Cơ sở dữ liệu của tôi chứa 3 bảng: Các thực thể Người dùng và Dịch vụ có mối quan hệ nhiều-nhiều và được kết hợp với bảng SERVICE_USER như sau:

NGƯỜI DÙNG - DỊCH VỤ_USER - DỊCH VỤ

Bảng SERVICE_USER chứa cột BLOCKED bổ sung.

Cách tốt nhất để thực hiện một ánh xạ như vậy là gì? Đây là các lớp Thực thể của tôi

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

Tôi đã làm theo ví dụ này http://giannigar.wordpress.com/2009/09/04/m ... bằng cách sử dụng-jpa / Đây là một số mã kiểm tra:

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

Vấn đề là ngủ đông vẫn tồn tại đối tượng Người dùng và Dịch vụ người dùng. Không thành công với đối tượng CmsService

Tôi đã thử sử dụng tìm nạp EAGER - không có tiến triển

Có thể đạt được hành vi mà tôi mong đợi với ánh xạ được cung cấp ở trên không?

Có lẽ có một số cách thanh lịch hơn để ánh xạ nhiều đến nhiều bảng tham gia với cột bổ sung?

Câu trả lời:


192

Vì bảng SERVICE_USER không phải là bảng tham gia thuần túy, nhưng có các trường chức năng bổ sung (bị chặn), bạn phải ánh xạ nó như một thực thể và phân tách nhiều thành nhiều liên kết giữa Người dùng và Dịch vụ thành hai liên kết OneToMany: Một Người dùng có nhiều Dịch vụ Người dùng, và một Dịch vụ có nhiều Dịch vụ Người dùng.

Bạn chưa chỉ cho chúng tôi phần quan trọng nhất: ánh xạ và khởi tạo các mối quan hệ giữa các thực thể của bạn (tức là phần bạn gặp vấn đề). Vì vậy, tôi sẽ cho bạn thấy nó sẽ trông như thế nào.

Nếu bạn thực hiện các mối quan hệ hai chiều, do đó bạn nên có

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

Nếu bạn không đặt bất kỳ tầng nào vào các mối quan hệ của mình, thì bạn phải kiên trì / lưu tất cả các thực thể. Mặc dù chỉ có phía sở hữu của mối quan hệ (ở đây, phía Dịch vụ người dùng) phải được khởi tạo, đó cũng là một cách thực hành tốt để đảm bảo cả hai bên đều gắn kết.

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
Chỉ cần thêm .. Mặc dù theo ý kiến ​​của tôi, đây là cách tốt nhất (tôi luôn thích lập bản đồ sở hữu FK như một thực thể vì lý do hiệu suất), thực tế nó không phải là cách duy nhất. Bạn cũng có thể ánh xạ các giá trị từ bảng SERVICE_USER dưới dạng một thành phần (cái mà JPA gọi là có thể nhúng) và sử dụng một @ElementCollection(hoặc cả hai) các thực thể Người dùng và Dịch vụ.
Steve Ebersole

6
Còn khóa chính của bảng UserService thì sao? Nó nên là sự kết hợp của khóa ngoài của người dùng và dịch vụ. Là bản đồ?
Jonas Gröger

24
Tôi sẽ không như vậy. Khóa tổng hợp gây đau đớn, không hiệu quả và Hibernate khuyên bạn không nên sử dụng khóa tổng hợp. Chỉ cần sử dụng ID được tạo tự động như đối với bất kỳ thực thể nào khác và cuộc sống sẽ đơn giản hơn nhiều. Để đảm bảo tính duy nhất của [userFK, serviceFK], sử dụng một ràng buộc duy nhất.
JB Nizet

1
@GaryKephart: đặt câu hỏi của riêng bạn, với mã của riêng bạn và ánh xạ của riêng bạn.
JB Nizet

1
@gstackoverflow: Hibernate 4 không thay đổi bất cứ điều gì về vấn đề đó. Tôi thực sự không thấy điều đó là không phù hợp.
JB Nizet

5

Tôi tìm kiếm một cách để ánh xạ một bảng kết hợp nhiều-nhiều với các cột bổ sung với hibernate trong cấu hình tệp xml.

Giả sử có hai bảng 'a' & 'c' với nhiều liên kết với nhiều cột có tên là 'phụ'. Vì tôi không tìm thấy bất kỳ ví dụ hoàn chỉnh nào, đây là mã của tôi. Hy vọng nó sẽ giúp :).

Đầu tiên ở đây là các đối tượng 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;
    }

}

Bây giờ, chúng ta phải tạo bảng kết hợp. Bước đầu tiên là tạo một đối tượng đại diện cho khóa chính phức tạp (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;
    }
}

Bây giờ hãy tạo chính đối tượng kết hợp.

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

Tại thời điểm này, đã đến lúc ánh xạ tất cả các lớp của chúng tôi với cấu hình xml ngủ đông.

A.hbm.xml và C.hxml.xml (im lặng giống nhau).

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

Và sau đó liên kết tệp ánh xạ, 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>

Dưới đây là mẫu mã để kiểm tra.

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

2

Như đã nói trước đây, với JPA, để có cơ hội có thêm các cột, bạn cần sử dụng hai liên kết OneToMany, thay vì một mối quan hệ ManyToMany. Bạn cũng có thể thêm một cột với các giá trị được tạo tự động; bằng cách này, nó có thể hoạt động như khóa chính của bảng, nếu hữu ích.

Ví dụ, mã triển khai của lớp bổ sung sẽ trông như thế:

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

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