在构造hibernate实体时如何处理双向关系?

时间:2011-07-06 13:16:34

标签: java hibernate constructor side-effects bidirectional-relation

我想用JPA / Hibernate建模两个实体,一个组和一个帐户之间的关系。一个帐户可以有多个组,但反之亦然,因此我们在帐户和组之间存在OneToMany关系。 我的同事建议对实体AccountGroup进行建模,如

public class Account {
    private List<Group> groups = new ArrayList<Group>();

    public Account() {}

    public void setGroups(List<Group> usergroups) {
        this.groups = groups;
    }

    @OneToMany(mappedBy = "account")
    public List<Group> getGroups() {
        return groups;
    }
}

public class Group {
    private String name;
    private Account account;

    public Group() {}

    public Group(String name, Account account) {
        this.name = name;
        addToAccount(account);
    }

    public void addToAccount(Account account) {
        setAccount(account);
        List<Group> accountGroups = account.getGroups();
        accountGroups.add(this);
    }

    @ManyToOne
    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }
}

我现在的问题是在addToAccount的构造函数中使用辅助方法Group。根据我的工作同事的说法,这种方法是必要的,因为我们需要更新两个实体之间的双向关系,以确保两个实体的内存模型一致。

但是我相信在构造函数中调用方法addToAccount并不是一个好主意,因为

  1. List的{​​{1}}是懒惰的 抓取,所以调用方法 Group需要开放 交易。所以构造函数 addToAccount只能在一个内部调用 开放交易。在我看来,这是一个非常烦人的限制。

  2. 作为参数给出的Group对象     到Account的构造函数是     由构造函数更改。在我看来,这是一个     Group令人惊讶的副作用     构造函数,不应该发生。

  3. 我的建议是更好地使用像

    这样的简单构造函数
    Group

    手动处理双向关系。但也许我错了。在构造hibernate实体时,是否应该如何处理双向关系?

2 个答案:

答案 0 :(得分:3)

在我们的项目中,我们通常会尽量避免双向关联。

一个原因是你的模型中有一个循环可能会产生问题,如果你想以某种方式序列化它,例如假设你想要序列化Account而你的序列化算法不够聪明你就结束了无限循环(因为Group有一个返回Account的引用)。

第二个原因是我发现只有一种方法来导航模型更清楚。我通常做的是删除帐户实体中的OneToMany关联,并在需要收集特定Group的所有Account时使用存储库调用(但这可能取决于您的用例和个人品味)。

第三,如果你摆脱addToAccount方法并使用字段访问权限,你可以使你的类不可变,这是一件好事。

答案 1 :(得分:0)

根据我的经验,你完全按照惯例做到这一点。我的问题更多的是关于结构(我希望还有比上面提供的示例更多)关于你为什么要直接从集团操纵账户的问题。

我还怀疑这是OneToMany还是ManyToMany情况(通常多个帐户可以属于一个组,多个组可以属于一个帐户,但它们都属于您的特定会计方案的语义...)无论如何:你做得对,虽然我质疑(在这个确切的情况下)为什么你想直接操纵帐户(除非它是懒惰加载)这是完全没问题的。

[您可能需要添加一些级联规则,以便根据您的配置保持正常。]

您应该注意到,通过映射到帐户,您已将其有效地添加到帐户列表中。当数据库下一次查询以创建该列表时,它将通过查找来自Account实体的引用来填充列表。

简而言之 - &gt;

public void Group.setAccounts(Account a)
{
  this.account = a;
}

实际上与您上面所做的相同。数据库将使用类似于:

的内容查询和填充List
//Pseudo SQL
    SELECT g.id FROM Group g WHERE g.account_id = :account_id

因此,除了延迟加载(您可能想要或可能不想要的东西)之外,添加到组是不必要的,因为List是由查询定义的。

(不要太难,看起来很简单。我希望通过长篇解释让您了解JPA中发生的事情)