2012-03-27 7 views
6

Chcę utrzy- mać encję pocztową, która ma pewne zasoby (wbudowany lub załącznik). Najpierw związanych je jako dwukierunkowej relacji:Różne zachowania przy użyciu relacji jednokierunkowej lub dwukierunkowej

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

I kiedy je zapisać:

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

Trzy wkładki zostały wykonane:

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 (?, ?, ?, ?, ?) 

Wtedy pomyślałem, że byłoby lepiej używając tylko relacja OneToMany. Nie ma potrzeby, aby zapisać która Mail jest w każdym 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; 
} 

Wygenerowane tabele są dokładnie takie same (MailResource ma FK do poczty). Problem polega na wykonaniu SQL:

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 = ?) 

Dlaczego te dwie aktualizacje? Używam EclipseLink, czy to zachowanie będzie takie samo przy użyciu innego dostawcy JPA jak Hibernacja? Które rozwiązanie jest lepsze?

UPDATE: - Jeśli nie używam @JoinColumn EclipseLink tworzy trzy tabele: poczta, MAILRESOURCE i MAIL_MAILRESOURCE. Myślę, że jest to całkowicie logiczne. Ale z @JoinColumn ma wystarczającą ilość informacji do stworzenia tylko dwóch tabel i, moim zdaniem, tylko wstawia, bez aktualizacji.

Odpowiedz

0

Zmapowany przez definiuje posiadaną stronę statku relacji, więc dla JPA daje lepszy sposób obsługi powiązań. Opcja Dołącz kolumnę definiuje tylko kolumnę relacji. Ponieważ JPA jest całkowicie oparte na odbiciu, mogłem pomyśleć o optymalizacji wykonanej dla Mapped by, ponieważ łatwo jest znaleźć własną stronę w ten sposób.

+0

Sugerujesz użyć atrybutu mappedBy z OneToMany zamiast JoinColumn adnotacji? –

+0

Tak. Myślę, że zawsze będzie lepiej działać w ten sposób. –

2

Podczas korzystania z @JoinColumn w OneToMany definiujesz "jednokierunkowy" jeden do wielu, co jest nowym typem mapowania dodanym w JPA 2.0, nie było to obsługiwane w JPA 1.0.

Zazwyczaj nie jest to najlepszy sposób definiowania OneToMany, normalny OneToMany jest definiowany przy użyciu mapowanego obiektu i posiadania ManyToOne w obiekcie docelowym. W przeciwnym razie obiekt docelowy nie ma wiedzy o tym kluczu obcym, a tym samym oddzielnej aktualizacji dla niego.

Możesz także użyć JoinTable zamiast JoinColumn (jest to ustawienie domyślne dla OneToMany), a następnie nie ma żadnego obcego klucza w celu, o który się martwisz.

Istnieje również czwarta opcja. Możesz oznaczyć MailResource jako Osadzony zamiast Encji i użyć ElementCollection.

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

+0

Ale chcę generować tylko dwie tabele (Mail i MailResource). Jeśli użyję ElementCollection of Embedable, dostanę trzy, czyż nie? – sinuhepop

Powiązane problemy