如何在使用 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()?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用hibernate在spring boot中实现分页