【问题标题】:How do I implement toString() in a class that is mapped with Hibernate?如何在使用 Hibernate 映射的类中实现 toString()?
【发布时间】:2009-11-12 13:01:06
【问题描述】:

我有一个从 Hibernate 会话中获得的类的实例。那个会议早就过去了。现在,我正在调用 toString() 并得到预期的 LazyInitializationException: could not initialize proxy - no Session,因为我正在尝试访问 Hibernate 在加载实例期间未解析的引用(延迟加载)。

我真的不想让加载变得急切,因为它会将查询从大约 120 个字符更改为超过 4KB(有 8 个连接)。而且我不必:我只想在toString() 中显示被引用对象的ID;即 Hibernate 在这个时间点需要知道的东西(或者它无法进行延迟加载)。

所以我的问题是:你如何处理这个案子?永远不要尝试在toString() 中使用引用?或者您是否在加载代码中调用toString() 以防万一?或者 Hibernate 中是否有一些实用函数,当我向它传递一个 可能 是惰性的引用时,它会返回一些有用的东西?还是您完全避免在toString() 中引用?

【问题讨论】:

  • 如果 Java 有闭包,你可以这样做: String x = lazyToString({ => this.getY() }) + lazyToString({ => this.getZ() });并在lazyToString 方法中捕捉到期望。内部类(或 try/catch)的开销太高,无法做到这一点。
  • 是的,但这也不会给我一个会话。
  • 没错。您不仅可以打印该值未加载。我以为这就是意图。您将无法启动会话并在 toString 方法调用上关联对象。
  • 我的意思是这些信息必须在某处可用(请参阅下面的答案):)

标签: hibernate lazy-loading tostring


【解决方案1】:

可以通过将 ID 字段的 accesstype 设置为“property”来做到这一点。喜欢:

@Entity
public class Foo {
    // the id field is set to be property accessed
    @Id @GeneratedValue @AccessType("property")
    private long id;
    // other fields can use the field access type
    @Column private String stuff;
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }
    String getStuff() { return stuff; }
    // NOTE: we don't need a setStuff method
}

解释了here。 这样,在创建代理时总是会填充 id 字段。

【讨论】:

  • +1 我喜欢;有一个小问题:我使用的是 DSL 语法,所以我的 getter 被称为“id()”,而不是“getId()”。我想我可以为这种特殊情况添加第二个吸气剂,但也许可以告诉 Hibernate 吸气剂的名称?
  • 好吧,可以通过创建自己的 org.hibernate.property.PropertyAccessor 实现,并将完全限定名称声明为 @AccessType 的值。另一方面,您可以创建 setter(您也需要)和 getter 并将它们设为私有,这样您就不会从应用的其余部分看到它们。
  • @EJB :真的有用吗?我有一种情况,A 类 ----->(具有一对多 rel)与 B 类。A 和 B 都有多个属性。因此,当我为 A 类执行 toString() 方法调用时,尽管为 A 类的 Id 字段设置了 @AccessType("property"),但它会因 LazyInitialization 异常(与上述相同)而失败。
【解决方案2】:

我找到了解决方法:

public static String getId (DBObject dbo)
{
    if (dbo == null)
        return "null";

    if (dbo instanceof HibernateProxy)
    {
        HibernateProxy proxy = (HibernateProxy)dbo;
        LazyInitializer li = proxy.getHibernateLazyInitializer();
        return li.getIdentifier ().toString ();
    }

    try
    {
        return Long.toString (dbo.id ());
    }
    catch (RuntimeException e)
    {
        return "???";
    }
}

所以这段代码的作用是从对象中获取 ID(一个 64 位数字)。 DBObject 是定义long id() 的接口。如果该对象是一个 Hibernate 代理,那么我会与它的LazyInitializer 交谈以获取 ID。否则,我打电话给id()。用法:

class Parent {
    DBObject child;
    public String toString () {
        return "Parent (id=..., child=" + getId(child)+")");
    }
}

【讨论】:

    【解决方案3】:

    我发现最适合良好实践的方法是修改此博客上的解决方案:http://www.nonfunc.com/2016/02/05/jpa-performance-gotcha-tostring-really/。它需要对可空字段和 Collections 对象进行修改。

    public toString() {
      return String.format("FuBar [id=%d" +  
          + ", fu=%s" // fu is a lazy non-nullable field
          + ", bar=%s" // bar is a nullable lazy field
          + ", borks=%s]", // borks is a lazy collection of Bork objects
          id,
          fu instanceof HibernateProxy ? "[null]" : bar.toString(),
          bar == null || bar instanceof HibernateProxy ? "[null]" : bar.toString(),
          borks instanceof PersistentSet ? "[null]" : borks.toString());
    }
    

    【讨论】:

    • 我知道你在做什么。一些 cmets:混合使用 String.format()+ 对性能不利。您还应该将[null] 替换为<lazy>,因为代理不是空的——它只是没有加载。更好的方法是返回<lazy TYPE ID>,其中TYPE 是实体类型,ID 是主键。
    • 已编辑代码以提高性能。这个例子并不完全是我所拥有的,变量和字符串的连接确实会影响性能(格式字符串应该编译成静态字符串),但我认为对 toString 的性能出汗并不是非常重要,因为它将用于调试或错误报告,并且不应该在生产环境中被超级频繁地调用。不过,我喜欢指定惰性与空值的建议。
    【解决方案4】:

    如果您想要返回的只是对象的 ID,我想调用 getID(),然后在您希望它显示的那一刻将 int/long 解析为字符串值就可以了。至少从这个问题来看是这样的。

    编辑

    How to solve the LazyInitializationException using JPA and Hibernate

    查看评论并进行一些搜索后,我相信这可能对您的方案最有利。

    【讨论】:

    • 这将抛出 LazyInitializationException 因为引用尚未解决。
    • Aaron,在阅读此评论后,我编辑了我的帖子。请查看新信息,如果这能解决问题,请告诉我。
    • @Woot - 答案无济于事。永远不会读取惰性初始化值。 tx 已提交。连接已关闭。
    • 是的,我看到事务已经提交。但是,在帖子中,可以重构现有代码以使用此处找到的 OpenSessionInView 模式:hibernate.org/43.html 如果这在 Aaron 的环境中不可行,那就这样吧。
    • @Woot:我必须同意托马斯的观点。
    猜你喜欢
    • 2013-12-01
    • 2015-04-11
    • 2018-09-19
    • 2012-04-16
    • 2021-11-22
    • 2010-11-27
    • 1970-01-01
    • 2018-12-10
    • 1970-01-01
    相关资源
    最近更新 更多