Java 使用 hibernate.enable_lazy_load_no_trans 解决 Hibernate Lazy-Init 问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25362831/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Solve Hibernate Lazy-Init issue with hibernate.enable_lazy_load_no_trans
提问by Sachin Verma
I have been suffering from infamous hibernate exception
我一直饱受臭名昭著的休眠异常之苦
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Now the community is cheering over
现在社区正在欢呼
<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
saying it solves the problem but USE IT WITH CAUTION.
说它解决了问题,但请谨慎使用。
What they mean by use it with caution? What this property actually does?
谨慎使用是什么意思?这个属性实际上是做什么的?
Please give me any insights. Thanks in advance.
请给我任何见解。提前致谢。
回答by uaiHebert
The problem with this approach is that you can have the N+1 effect.
这种方法的问题在于您可能会产生 N+1 效果。
Imagine that you have the following entity:
假设您有以下实体:
public class Person{
@OneToMany // default to lazy
private List<Order> orderList;
}
If you have a report that returns 10K of persons, and if in this report you execute the code person.getOrderList()
the JPA/Hibernate will execute 10K of queries. This is the N+1 effect, you will have no control about all the queries that will be executed.
如果您有一个返回 10K 人的报告,并且如果您在此报告中执行代码,person.getOrderList()
则 JPA/Hibernate 将执行 10K 查询。这就是 N+1 效应,您将无法控制将要执行的所有查询。
Imagine now that Order is like below:
现在想象一下,订单如下所示:
public class Order{
@OneToMany // default to lazy
private List<EmailSent> emailSentList;
}
Imagine now that you have a iteration with the person.getOrderList()
and for every Order order
you will do a order.getEmailSentList()
. Can you see the problem now?
想象一下,现在您对 进行了迭代,person.getOrderList()
并且对于每个Order order
您将执行order.getEmailSentList()
. 你现在能看出问题吗?
For LazyInitializationException you can have some solutions:
对于 LazyInitializationException 您可以有一些解决方案:
- Use the OpenInSessionInView approach. You will need to create a WebFilter that will open and close the transaction. The problem with is the N+1 effect.
- Use the hibernate.enable_lazy_load_no_trans configuration, that is a hibernate and you will not be able to port your project to other JPA provider if needed. You also can have the N+1 effect.
- Use the EJB feature named PersistenceContext Extended. With this you will keep the context opened of several transactions. The problems are: N+1 effect can happen, use a lot of server memory (entities will stay managed)
- Use the FETCH in the query. With this approach you could do a JPQL/HQL like:
select p from Person p join fetch p.orderList
. With this query you will have your list loaded from the database and will not have the N+1 effect. The problem is that you will need to write a JPQL for each case.
- 使用 OpenInSessionInView 方法。您需要创建一个 WebFilter 来打开和关闭事务。问题在于 N+1 效应。
- 使用 hibernate.enable_lazy_load_no_trans 配置,这是一个休眠状态,如果需要,您将无法将您的项目移植到其他 JPA 提供程序。您也可以拥有 N+1 效果。
- 使用名为 PersistenceContext Extended 的 EJB 特性。有了这个,您将保持多个事务的上下文打开。问题是:可能发生 N+1 效应,使用大量服务器内存(实体将保持管理)
- 在查询中使用 FETCH。通过这种方法,你可以做一个JPQL / HQL,如:
select p from Person p join fetch p.orderList
。使用此查询,您将从数据库加载您的列表,并且不会有 N+1 效果。问题是您需要为每种情况编写一个 JPQL。
If you still have any problem, check these links:
如果仍有问题,请查看以下链接:
回答by James Watkins
Probably because there are better solutions, like @Transactional, where opening and closing sessions follows a very common pattern of "open a session then wrap everything in a try-catch-finally; catch rolls back and finally closes the session." This annotation is typically at the request-level for web apps and services.
可能是因为有更好的解决方案,比如@Transactional,其中打开和关闭会话遵循一个非常常见的模式:“打开一个会话,然后将所有内容包装在 try-catch-finally 中;catch 回滚并最终关闭会话。” 此注释通常位于 Web 应用程序和服务的请求级别。
Or if you need more granular control you can open sessions manually using a SessionFactory.
或者,如果您需要更精细的控制,您可以使用 SessionFactory 手动打开会话。
And as others have mentioned, lazy-loading is something you need to be aware of. It's not a silver bullet but it can be very helpful. Generally, if your apps are designed to have many small requests then its ok.
正如其他人所提到的,延迟加载是您需要注意的。这不是灵丹妙药,但它可以非常有帮助。通常,如果您的应用程序设计为具有许多小请求,则可以。
Eager loading can also be very bad. For example, when your object model has lots of many-to-many relationships but your requests don't use data more than one level deep.
急切加载也可能非常糟糕。例如,当您的对象模型具有许多多对多关系但您的请求使用的数据不超过一层时。
Or you can just forget the whole thing for now. Use lazy loading until it becomes an issue. And if it does, you would have been better of with Mybatis anyway.
或者你可以暂时忘记整件事。使用延迟加载直到它成为问题。如果是这样,无论如何你都会更好地使用 Mybatis。
回答by codedabbler
This goes against how we can take advantage of Hibernate's enforcement of repeatable read semantics with the Session concept. When an object is first loaded and if the object is referenced again within the life of the session, then the same object is returned IRRESPECTIVE of whether this object has changed in the DB. This is the repeatable read semantics provided automatically by hibernate.
这与我们如何利用 Hibernate 对 Session 概念的可重复读取语义的实施相违背。当一个对象第一次被加载并且如果该对象在会话的生命周期内再次被引用,那么这个对象在 DB 中是否已经改变,则返回同一个对象 IRRESPECTIVE。这是 hibernate 自动提供的可重复读语义。
With this setting, you have no session providing this guarantee, so if you now access this data you will be getting the latest version of the data.
使用此设置,您没有会话提供此保证,因此如果您现在访问此数据,您将获得最新版本的数据。
This might be fine. But consider the scenario where this object is held in some place for a long time and the data has changed considerably, so that the lazily fetched data is much different that the data already loaded when the session was alive. This is what you need to be concerned about.
这可能没问题。但是考虑这个对象在某个地方长期持有并且数据发生了很大变化的场景,因此延迟获取的数据与会话处于活动状态时已经加载的数据有很大不同。这是你需要关心的。
To put it simple you can safely use this setting if your program is not affected by: How stale the data that was already fetched when in session to the data that will be fetched lazily out of session
简而言之,如果您的程序不受以下因素的影响,您可以安全地使用此设置:会话中已获取的数据与将在会话中延迟获取的数据之间的陈旧程度
But if this (your program is exposed to timing issues, when it might work fine one time and fail another time) is a concern, then fetch all the necessary data while in session.
但是,如果这(您的程序面临计时问题,它可能一次正常工作而另一次失败)是一个问题,那么在会话期间获取所有必要的数据。
回答by Vlad Mihalcea
The best way to solve the LazyInitializationExceptionis to use the JOIN FETCH directive in your entity queries.
解决 LazyInitializationException 的最佳方法是在实体查询中使用 JOIN FETCH 指令。
EAGER loadingis bad for performance. Also, there are anti-patterns such as:
EAGER 加载对性能不利。此外,还有一些反模式,例如:
Which you should never use since they either require the database connection to be open for the UI rendering (Open Session in View), or a database connection is needed for every lazy association that is fetched outside of the initial Persistence Context (hibernate.enable_lazy_load_no_trans
).
您永远不应该使用它,因为它们要么需要为 UI 呈现打开数据库连接(在视图中打开会话),要么需要为在初始持久性上下文 ( hibernate.enable_lazy_load_no_trans
)之外获取的每个延迟关联都需要一个数据库连接。
Sometimes, you don't even need entities, and a DTO projection is even better.
有时,您甚至不需要实体,而DTO 投影甚至更好。