【问题标题】:Using entity id as a reference in Hibernate在 Hibernate 中使用实体 id 作为参考
【发布时间】:2009-10-21 08:38:34
【问题描述】:

我想使用它的标识符来保存一些其他实体的引用。

我正在处理的对象模型非常庞大,相关实体包含大量数据。也很少有部分是循环的,我怀疑直接拿物体会是个问题。

因此考虑以下作为示例,假设有两个实体 A 和 B:

Class A
{
private String id;
private String name;
private String type;
private String b_id // A reference to instance of B without holding direct instance
}

Class B
{
private String id;
private String name;
private String type;
}

如何使用 Hibernate 框架实现这一点?

【问题讨论】:

    标签: java hibernate jakarta-ee orm


    【解决方案1】:

    您所写的是两个彼此没有关系的不同类,这就是 Hibernate 将如何对待它们。

    当您从 Hibernate 读取 A 的实例时,您必须返回到 Hibernate 以请求对应于 Ab_id 的 B 实例。

    我强烈建议您使用普通的对象引用并让 Hibernate 有机会完成它的工作。不要提前把它写下来,因为你认为它不会应付。

    【讨论】:

      【解决方案2】:

      你基本上忽略了 ORM 可以给你的力量。

      记住 a.getB().getId() 不会初始化 B 实例!只有当您实际访问 B 的其他方法和属性(或者您明确地查询它)时,像 Hibernate 这样的(好的)ORM 才会真正麻烦地获取 B。

      【讨论】:

      • 是的,但是我需要通过 SOAP 将这些对象发送给客户端,因此客户端需要有一种方法可以查询有关关联实体(即 B)的更多信息,因此我需要提供对 B 的引用(这意味着拥有另一个用于 SOAP 通信的对象模型)或“完整”对象 A,即对象 A 的所有字段加上“完整”对象 B
      • 听起来你需要一个 DAO(数据访问对象)。 Hibernate 和其他任何东西都不能在没有引用实例的情况下持久保存对象......您总是可以为一个类提供两个映射 - 一个 RealA 和一个 partialA 一个正确映射,另一个只是作为纯值。您可以使用 entity-name 为同一个类创建多个映射。
      【解决方案3】:

      我遇到了同样的问题,我希望能够设置密钥但不使用所有这些延迟加载的东西。我发现hibernate非常脆弱,到处都是错误和神秘的幽灵类。到目前为止,它并没有简化我的生活。考虑一下:

      模型(大体简化):

      Portfolio {
       ...
       @OneToMany(cascade = {CascadeType.ALL}, fetch=FetchType.EAGER )
       @MapKey(name="symbol")
       public Map<String,Position> positions;
      }
      Position {
          String symbol; int quantity;
      }
      Trade {
          String symbol; int quantity;  // price,etc
       ...
       @ManyToOne() // looser coupling here.
       public Portfolio portfolio;
      }
      

      测试用例(注意:所有 'enterTrade()' 所做的只是将交易粘贴在交易表中,并且 调整投资组合中的头寸大小,'undoTrade()' 只是恢复):

      Trade trade1 = new Trade("GOOG", 25, portfolio);
      Trade trade2 = new Trade("GOOG", 50, portfolio);
      portfolio = portfolioDAO.enterTrade(trade1);
      portfolio = portfolioDAO.enterTrade(trade2);
      
      portfolio = portfolioSource.undoTrade(trade1, user);
      portfolio = portfolioSource.undoTrade(trade2, user);
      Assert.assertFalse(portfolio.getPositions().containsKey("GOOG"),
              "after both trades net a position of zero");
      

      这个测试应该有效吗?错误的!不可思议的是,投资组合中还有 25 股 GOOG 股票。

      基本问题是 trade2 对象指向一个变得陈旧的投资组合对象。最后一个 undoTrade() 操作使用的投资组合仍然有 75 股 GOOG。请注意,Trade2 永远不会过时,因为仅对其执行了 persist()。但是当在 Portfolio 上执行 update() 时,会创建一个额外的副本,而不是更改原始对象(就像使用 'persist()' 所做的那样)。我很难理解为什么这种设计不会被认为是愚蠢和容易出错的。这是一个简单的例子。这不像我们在谈论并发访问问题或任何花哨的东西。哦,当我尝试刷新 trade2 时,得到完全令人困惑的 LazyInitializationException。

      所以,我的解决方案是使用键,以便每次需要刷新都是明确的:

      Trade {
          ...
          @ManyToOne(targetEntity=Portofio.class)
          @JoinColumn(referenceColumnName="id")
          public Long portfolio_id;
      }
      

      或其他。虽然我在这里表达了很多挫败感,但我很想听到关于如何处理这些问题的诚实解释。谢谢。

      【讨论】:

      • 我通过进入 Hibernate 级别(我正在使用 JPA)解决了我的核心问题: Session session = (Session) entityManager.getDelegate(); session.update(p); //return entityManager.merge(p);事实证明,Hibernate API 对于实际发生的事情要优越得多,也更清楚。我想我可能会完全改用那个。
      【解决方案4】:

      好的,所以我知道这是一个旧线程,但这是相当普遍的,大多数休眠的人似乎没有意识到对于我们这些无法在整个请求期间保持会话打开的人来说这是一个问题。

      做这样的事情(以注释为例,因为这是我熟悉的):

      
      Class A
      {
      private String id;
      private String name;
      private String type;
      
      @OneToOne(fetch=FetchType.LAZY)
      private B b;
      @Column(name="b_id", insertable=false, updatable=false)
      private String b_id;
      }
      

      这将导致默认情况下不获取 b,但即使在会话关闭并通过 dozer 或 gilead 之类的工具运行结果之后,b_id 仍然可用。 b_id 的映射设置为 insertable=false 和 updatable=false,因此它是只读的,并且在写回数据库时将被忽略。要更新关联,您必须设置实际的 b 字段。

      【讨论】:

        【解决方案5】:

        感谢您的回答。有几点我想注意的,

        • A 类和 B 类是相关的,但我知道告诉 Hibernate 这种关系的唯一方法是使用普通对象引用。我想知道是否有任何方法可以关联class Aclass B,而不必在A 中保留B 的实例。我可以在对象模型中的A 中有一个B 的ID 但是在数据库中有b_id 作为外键B --&gt; A

        • 我绝对希望 Hibernate 处理这种情况,但引用链的深度如此之大,以至于对一个对象的查询将导致大量信息流过,因为直接实例成员。因此,我认为避免这种情况的一种方法是仅在类中放置其他实体的引用,而不会通过外键放置关系来影响数据完整性。

        • 还有一些数据+关系形成了一个类似图的结构。持有直接实例引用也可能是一个问题。不过我不知道 Hibernate 在这种情况下是如何工作的。

        【讨论】:

        • 很抱歉将此作为 ans 发布,但 cmets 将您限制为 600 个字符。
        【解决方案6】:

        听起来您的用例不需要 Hibernate,而是需要其他东西,因为您基本上是在这里“不发明轮子”。您唯一的选择是延迟加载 B,以便在您调用 A 时,B 及其对象图在您显式调用 getB()被拉出。

        【讨论】:

          猜你喜欢
          • 2015-05-23
          • 1970-01-01
          • 1970-01-01
          • 2011-07-15
          • 2013-11-13
          • 1970-01-01
          • 2014-03-29
          • 2016-09-17
          • 1970-01-01
          相关资源
          最近更新 更多