使用单向或双向关系的不同行为

时间:2012-03-27 23:27:42

标签: java hibernate jpa orm eclipselink

我想要保留一个拥有一些资源(内联或附件)的邮件实体。首先,我将它们视为双向关系:

@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中保存哪个Mail:

@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到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,这种行为是否相同?哪种解决方案更好?

更新   - 如果我不使用@JoinColumn,EclipseLink会创建三个表:MAIL,MAILRESOURCE和MAIL_MAILRESOURCE。我认为这是完全逻辑的。但是对于@JoinColumn,它只有足够的信息来创建两个表,在我看来,只进行插入,没有更新。

2 个答案:

答案 0 :(得分:2)

当您在OneToMany中使用@JoinColumn时,您正在定义一对多的“单向”,这是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

答案 1 :(得分:0)

映射为定义关系船的拥有方,因此对于JPA,它提供了更好的方法来处理关联。 Join Column仅定义关系列。由于JPA是完全基于反射的框架,我可以想到为Mapped所做的优化,因为这样很容易找到拥有方。