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.
Sugerujesz użyć atrybutu mappedBy z OneToMany zamiast JoinColumn adnotacji? –
Tak. Myślę, że zawsze będzie lepiej działać w ten sposób. –