我有 hibernate pojo 类 A { B b ;一些其他属性},对于 B 类,lazy=true。
当我获取对象 A 时,B 未加载,并且 hibernate 返回其代理。当我将此对象传递给另一个模块时,该模块会遍历 A 中的每个对象,当遇到 B.getXXX 时,它会抛出 LazyInitialization 异常。在这种特殊情况下,我不想加载类 B,因为它不是必需的。当我调用 B 上的方法时,有什么方法可以返回 null 或将 B 的代理转换为真实对象 B,以便模块不会抛出 LazyInitialization 错误。我无法更改 B 类 getter、setter,因为它是公共类并被许多其他类使用。
I have hibernate pojo class A { B b ; some other properies} with lazy= true for class B.
When i get object A, B is not loaded and hibernate returns its proxy. When i pass this object to another module, that module traverse each and every objects in A and when it encounter B.getXXX it throws LazyInitialization exception. In this particular case, I do not want to load class B as it is not required. Is there any way when i call methods on B it either return null or turn proxy of B into real object B so that module doesn't throw LazyInitialization error. I cannot change class B getter,setter as it common class and use by many other classes.
发布评论
评论(4)
如果我理解你的问题,你正在检索一个与 B 具有惰性关联的对象 A。但是,该关联未初始化,并且你发现其他模块抛出异常,因为实际上使用了 B。所以它在某种程度上是必需的。
您想要
B
的调用中Return
null
(据我所知,这是不可能的,除非这些模块上有一些特定于您的应用程序的行为)可以知道)或初始化
B
当此类调用发生时。我将尽力帮助您实现这一点。您收到
LazyInitializationExceptions
的原因是获取B
(并且未初始化它)的会话已经关闭,因此此时,B
根本没有用。您可以在此处应用的一种解决方法是使用 OSIV 模式,以便您拥有相同的 Hibernate 会话在所有请求范围内打开。该会话将使用惰性B
获取A
,并且将在需要时初始化B
。您可以应用的另一个选项是在另一个会话中初始化 B(仅当这些异常发生在另一个事务的上下文中时才有效,即打开另一个 Hibernate 会话,与获取的会话不同)
A
)。例如:当然,您始终可以使用
fetchMode.EAGER
或Hibernate.initialize(a.getB())
强制初始化B
。但这将无条件加载实例,即使它根本不会被使用。另外,您可能会发现此问题的答案可能有用: hibernate: LazyInitializationException: Could not初始化代理
If I understand your question, you're retrieving an object A with a lazy association to B. However, this association is not initialized, and you find that other modules are throwing exceptions because B is actually used. So it is required in some way.
You want to either
Return
null
from calls toB
(not possible, as far as I know, unless there's some application-specific behavior on those modules that only you can be aware of) orInitialize
B
when such calls happen. I'll try to help you implement this one.The reason why you're getting
LazyInitializationExceptions
is that the session that fetchedB
(and didn't initialize it) has already been closed, so at this point, the instance ofB
is of no use at all. One workaround you could apply here is to use the OSIV pattern so that you have the same Hibernate session open in all the request scope. This is the session that will fetchA
with lazyB
and will initializeB
when there is the need.Another option you could apply would be to initialize
B
in another session (only valid if those exceptions are occurring in the context of another transaction, that is, with another Hibernate session open, different from the one that fetchedA
). For instance:Of course, you could always force initialization of
B
withfetchMode.EAGER
orHibernate.initialize(a.getB())
. But that would be loading the instance unconditionally, even if it won't be used at all.Also, you may find the answers to this question may be useful: hibernate: LazyInitializationException: could not initialize proxy
事实上,你有几个选择。
1)使A→B关系变得EAGER。
2) 当您在休眠会话已关闭时尝试启动代理时,您会收到
LazyInitializationExceptions
。所以他可能的解决方案是保持会话打开,直到所有 A、B、C...等对象操作未完成。3)如果你关注WEB环境,可以看到有一种模式叫Open Session。这将使您的 Hibernate 会话保持打开状态,直到您的 HTTP 请求处于活动状态。
您可以在此处阅读更多相关信息。我认为阅读它会对您有所帮助。
Actually, you have a few option.
1) Make A->B relation EAGER.
2) You are getting
LazyInitializationExceptions
when you try to initiate proxy while you hibernate session is lready closed. so he possible solution would be to keep Session open till all your A,B,C...etc object manipulation are not completed.3) If you are talling about WEB environment, there are a pattern called Open Session in view. which keeps your Hibernate session open till your HTTP Request is alive.
I you can read more about it here. I think it will be useful for you to read it.
会话关闭时不要将实体发送到其他模块。
如果这些其他模块与会话在同一应用程序域中执行,则在调用模块时保持会话打开,并在返回时将其关闭。
如果这些模块不在同一个 AppDomain 中,如果您需要某种序列化来发送对象或者异步调用,我将使用 DTO。由于多种原因,将实体暴露在服务器之外(我不知道这里是否属于这种情况)是一种不好的做法。 Ayende Rahien 将其称为脱衣舞图案。
Don't send the entities to other modules when the session is closed.
If these other module is executed in the same Application Domain as the session, keep the session open when calling the module and close it when it returns.
If these module is not in the same AppDomain, if you need some kind of serialization to send the objects or if it is called asynchronously, I would use a DTO. Exposing the entities outside of the server (I don't know if this is the case here) is a bad practice for several reasons. Ayende Rahien calls it the Stripper Pattern.
感谢您的所有建议。
我的应用程序具有分层架构。服务->管理器->Dao。 Hibernate 会话在管理器之后关闭。其他模块只能通过Service进行交互。打开休眠会话直到请求完成对我来说不是一个选择。我也不想访问数据库,因为不需要填充 B 的属性。我只是想用真实的对象替换休眠代理,以便任何使用服务的人都不会遇到任何问题。我找到了一个实用程序
http://svn.rhq-project.org/repos/rhq/branches/HEIKO-EXP/modules/enterprise/server/safe-invoker/src/main/java/org/rhq/enterprise/服务器/util/HibernateDetachUtility.java
这正是我想要的。它检查对象并用真实对象替换休眠代理。我需要在上面的实用程序中自定义以下内容
1. 将类名实例从 org.rhq 更改为我的包结构。
2.他们期望pojo中身份字段的名称是“id”。我将其更改为使用具有 javax.persistence.Id 注释的那些属性。
上述更改的基本测试已完成,并且运行良好。我只需要使用各种场景测试整个应用程序,以便它可以在所有场景中工作。
Thanks for all your suggestion.
My application have layered architecture. Service->Manager->Dao. Hibernate session closes after manager. Other module interacts only through Service. Opening hibernate session till request complete is not an option for me. I also do not want to hit database as it is not necessary that properties of B are populated. I just want to replace hibernate proxy with real object so that anyone who is using service do not face any problem. I found a utility at
http://svn.rhq-project.org/repos/rhq/branches/HEIKO-EXP/modules/enterprise/server/safe-invoker/src/main/java/org/rhq/enterprise/server/util/HibernateDetachUtility.java
which exactly does what i want. It inspect object and replace hibernate proxy with real object. I need to customize following things in above utility
1. Change instances of classname from org.rhq to my package structure.
2. They expect name of identity field in pojo is "id". I change it to use those property which has annotation of javax.persistence.Id.
Basic testing with above changes is done and it is working fine. I just need to test whole application with various scenario so that it is working in all scenario.