Я хочу сохранить почтовый объект, который имеет некоторые ресурсы (встроенные или вложения). Сначала я связал их как двунаправленное отношение:
@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;
}
И когда я их сохранил:
Mail mail = new Mail("asdasd@asd.com", "Hi!", "...");
mail.addResource("image", MailResourceType.INLINE, someBytes);
mail.addResource("documentation.pdf", MailResourceType.ATTACHMENT, someOtherBytes);
mailRepository.save(mail);
Выполнено три вставки:
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 (?, ?, ?, ?, ?)
Тогда я подумал, что будет лучше использовать только отношение OneToMany. Нет необходимости сохранять, какая Почта есть в каждом 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;
}
Сгенерированные таблицы абсолютно одинаковы (у MailResource есть FK to Mail). Проблема заключается в выполнении 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 = ?)
Почему это два обновления? Я использую EclipseLink, будет ли это поведение таким же при использовании другого провайдера JPA, как Hibernate? Какое решение лучше?
UPDATE:
- Если я не использую @JoinColumn, EclipseLink создает три таблицы: MAIL, MAILRESOURCE и MAIL_MAILRESOURCE. Я думаю, что это совершенно логично. Но с @JoinColumn у него достаточно информации для создания только двух таблиц и, на мой взгляд, только вставки, без обновлений.