2012-03-27 30 views
6

Tôi muốn tồn tại một thực thể thư có một số tài nguyên (nội tuyến hoặc tệp đính kèm). Trước tiên tôi liên quan đến chúng như là một mối quan hệ hai chiều:Hành vi khác nhau sử dụng quan hệ một chiều hoặc hai chiều

@Entity 
public class Mail extends BaseEntity { 

    @OneToMany(mappedBy = "mail", cascade = CascadeType.ALL, orphanRemoval = true) 
    private List<MailResource> resource; 

    private String receiver; 
    private String subject; 
    private String body; 

    @Temporal(TemporalType.TIMESTAMP) 
    private Date queued; 

    @Temporal(TemporalType.TIMESTAMP) 
    private Date sent; 

    public Mail(String receiver, String subject, String body) { 
     this.receiver = receiver; 
     this.subject = subject; 
     this.body = body; 
     this.queued = new Date(); 
     this.resource = new ArrayList<>(); 
    } 

    public void addResource(String name, MailResourceType type, byte[] content) { 
     resource.add(new MailResource(this, name, type, content)); 
    } 

} 

@Entity 
public class MailResource extends BaseEntity { 

    @ManyToOne(optional = false) 
    private Mail mail; 

    private String name; 
    private MailResourceType type; 
    private byte[] content; 
} 

Và khi tôi lưu chúng:

Mail mail = new Mail("[email protected]", "Hi!", "..."); 
mail.addResource("image", MailResourceType.INLINE, someBytes); 
mail.addResource("documentation.pdf", MailResourceType.ATTACHMENT, someOtherBytes); 
mailRepository.save(mail); 

Ba chèn được thực hiện:

INSERT INTO MAIL (ID, BODY, QUEUED, RECEIVER, SENT, SUBJECT) VALUES (?, ?, ?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE, MAIL_ID) VALUES (?, ?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE, MAIL_ID) VALUES (?, ?, ?, ?, ?) 

Sau đó, tôi nghĩ rằng nó sẽ được sử dụng tốt hơn chỉ một mối quan hệ OneToMany. Không cần phải lưu Thư nào trong mỗi MailResource:

@Entity 
public class Mail extends BaseEntity { 

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) 
    @JoinColumn(name = "mail_id") 
    private List<MailResource> resource; 

    ... 

    public void addResource(String name, MailResourceType type, byte[] content) { 
     resource.add(new MailResource(name, type, content)); 
    } 

} 

@Entity 
public class MailResource extends BaseEntity { 
    private String name; 
    private MailResourceType type; 
    private byte[] content; 
} 

Bảng đã tạo giống hệt nhau (MailResource có FK thành Thư). Vấn đề là SQL được thực thi:

INSERT INTO MAIL (ID, BODY, QUEUED, RECEIVER, SENT, SUBJECT) VALUES (?, ?, ?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE) VALUES (?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE) VALUES (?, ?, ?, ?) 
UPDATE MAILRESOURCE SET mail_id = ? WHERE (ID = ?) 
UPDATE MAILRESOURCE SET mail_id = ? WHERE (ID = ?) 

Tại sao hai cập nhật này lại xảy ra? Tôi đang sử dụng EclipseLink, hành vi này sẽ giống như vậy khi sử dụng một nhà cung cấp JPA khác như Hibernate? Giải pháp nào tốt hơn?

CẬP NHẬT: - Nếu tôi không sử dụng @JoinColumn EclipseLink sẽ tạo ba bảng: MAIL, MAILRESOURCE và MAIL_MAILRESOURCE. Tôi nghĩ điều này hoàn toàn logic. Nhưng với @JoinColumn nó có đủ thông tin để tạo chỉ có hai bảng và, theo ý kiến ​​của tôi, chỉ chèn, không có cập nhật.

Trả lời

0

Ánh xạ theo định nghĩa bên sở hữu của tàu quan hệ để JPA cung cấp cách tốt hơn để xử lý các liên kết. Tham gia Cột chỉ xác định cột mối quan hệ. Kể từ khi JPA là hoàn toàn dựa trên phản ánh khuôn khổ tôi có thể nghĩ về tối ưu hóa thực hiện cho ánh xạ bởi vì nó rất dễ dàng tìm thấy bên sở hữu theo cách này.

+0

Bạn có gợi ý để sử dụng thuộc tính mappedBy của OneToMany thay vì JoinColumn chú thích? –

+0

Có. Tôi nghĩ rằng nó sẽ luôn luôn làm việc tốt hơn theo cách đó. –

2

Khi bạn sử dụng @JoinColumn trong OneToMany, bạn định nghĩa một kiểu "một chiều", đây là một kiểu ánh xạ mới được thêm vào trong JPA 2.0, điều này không được hỗ trợ trong JPA 1.0.

Đây thường không phải là cách tốt nhất để xác định OneToMany, OneToMany bình thường được xác định bằng cách sử dụng một ánh xạ và có một ManyToOne trong đối tượng đích. Nếu không, đối tượng đích không có kiến ​​thức về khóa ngoại này, và do đó là bản cập nhật riêng cho nó.

Bạn cũng có thể sử dụng một JoinTable thay vì JoinColumn (đây là mặc định cho OneToMany), và sau đó không có khóa ngoại trong mục tiêu phải lo lắng.

Ngoài ra còn có tùy chọn thứ tư. Bạn có thể đánh dấu MailResource là một Embeddable thay vì Entity và sử dụng ElementCollection.

Xem, http://en.wikibooks.org/wiki/Java_Persistence/OneToMany

+0

Nhưng tôi muốn chỉ có hai bảng được tạo ra (Mail và MailResource). Nếu tôi sử dụng một ElementCollection của Embeddable tôi sẽ nhận được ba, woudn't tôi? – sinuhepop

Các vấn đề liên quan