如何在使用 Hibernate 映射的类中实现 toString()?

Posted

技术标签:

【中文标题】如何在使用 Hibernate 映射的类中实现 toString()?【英文标题】:How do I implement toString() in a class that is mapped with Hibernate? 【发布时间】: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 方法调用上关联对象。 我的意思是这些信息必须在某处可用(请参阅下面的答案):) 【参考方案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:我必须同意托马斯的观点。

以上是关于如何在使用 Hibernate 映射的类中实现 toString()?的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis框架中实现一对多关系映射

如何使用hibernate在spring boot中实现分页

如何在batmanjs中实现导航菜单

如何在hibernate中实现继承?

如何使用 jOOQ fetchInto() 映射到现有的 Hibernate 模型?

在 Hibernate 中实现基于条件的搜索页面的优雅方式