Различное поведение с использованием однонаправленного или двунаправленного отношения - PullRequest
6 голосов
/ 28 марта 2012

Я хочу сохранить почтовый объект, который имеет некоторые ресурсы (встроенные или вложения). Сначала я связал их как двунаправленное отношение:

@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 у него достаточно информации для создания только двух таблиц и, на мой взгляд, только вставки, без обновлений.

Ответы [ 2 ]

2 голосов
/ 28 марта 2012

Когда вы используете @JoinColumn в OneToMany, вы определяете «однонаправленный» один-ко-многим, который является новым типом отображения, добавленным в JPA 2.0, это не поддерживалось в JPA 1.0.

Этообычно это не лучший способ определить OneToMany, обычный OneToMany определяется с использованием mappedBy и наличием ManyToOne в целевом объекте.В противном случае целевой объект не знает об этом внешнем ключе и, следовательно, об отдельном обновлении для него.

Вы также можете использовать JoinTable вместо JoinColumn (это значение по умолчанию для OneToMany), а затем нетВнешний ключ в целевом объекте, о котором следует беспокоиться.

Существует также четвертый вариант.Вы можете пометить MailResource как Embeddable вместо Entity и использовать ElementCollection.

См., http://en.wikibooks.org/wiki/Java_Persistence/OneToMany

0 голосов
/ 28 марта 2012

Отображается с помощью определяющей стороны корабля отношений, поэтому для JPA это дает лучший способ обработки ассоциаций.Столбец соединения определяет только столбец отношений.Поскольку JPA является полностью основанной на отражении средой, я мог бы подумать об оптимизации, выполненной для Mapped, поскольку таким образом легко найти свою сторону.

...