【问题标题】:How to handle bidirectional relationships when constructing hibernate entities?构建hibernate实体时如何处理双向关系?
【发布时间】:2011-09-29 15:12:44
【问题描述】:

我想用 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;
    }
}

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

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

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

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

我的建议是更好地使用简单的构造函数,例如

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

并手动处理双向关系。但也许我错了。在构建休眠实体时,是否有一种常见的方式来处理双向关系?

【问题讨论】:

  • 我相信你们大学的提议是不好的做法。不仅关于 1 或 2(特别是 2!),在此之上,您应该在构造函数中使用可覆盖的方法,因为它可以在子类中有效地被覆盖并在子对象构造函数 get 被调用之前被调用!我确实相信你应该保持一个一致的对象图(内存模型),我正在研究如何最好地做到这一点并解决这个问题.. 只是无法静音!你终于得到答案了吗?

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


【解决方案1】:

根据我的经验,您正在按照通常的做法进行操作。我的问题更多是关于结构(我希望比上面提供的示例更多)关于您为什么要直接从集团操纵帐户。

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

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

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

总之->

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

实际上等同于您在上面所做的。数据库将使用类似于以下内容的内容查询和填充列表:

//Pseudo SQL
    SELECT g.id FROM Group g WHERE g.account_id = :account_id

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

(不要太难,它看起来很简单。我希望冗长的解释能让您对 JPA 中发生的事情有所了解)

【讨论】:

    【解决方案2】:

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

    一个原因是你的模型中有一个循环,如果你想以某种方式对其进行序列化,可能会产生问题,例如,假设你想序列化一个Account,而你的序列化算法不够聪明,你最终会得到一个无限循环(因为Group 有一个对Account 的引用)。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多