Eug*_*ene 340 java spring jsp hibernate spring-mvc
我有这个问题:
org.hibernate.LazyInitializationException:懒得初始化角色集合:mvc3.model.Topic.comments,没有会话或会话被关闭
这是模型:
@Entity
@Table(name = "T_TOPIC")
public class Topic {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
@ManyToOne
@JoinColumn(name="USER_ID")
private User author;
@Enumerated(EnumType.STRING)
private Tag topicTag;
private String name;
private String text;
@OneToMany(mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
...
public Collection<Comment> getComments() {
return comments;
}
}
Run Code Online (Sandbox Code Playgroud)
调用模型的控制器如下所示:
@Controller
@RequestMapping(value = "/topic")
public class TopicController {
@Autowired
private TopicService service;
private static final Logger logger = LoggerFactory.getLogger(TopicController.class);
@RequestMapping(value = "/details/{topicId}", method = RequestMethod.GET)
public ModelAndView details(@PathVariable(value="topicId") int id)
{
Topic topicById = service.findTopicByID(id);
Collection<Comment> commentList = topicById.getComments();
Hashtable modelData = new Hashtable();
modelData.put("topic", topicById);
modelData.put("commentList", commentList);
return new ModelAndView("/topic/details", modelData);
}
}
Run Code Online (Sandbox Code Playgroud)
jsp页面看起来如下:
<%@page import="com.epam.mvc3.helpers.Utils"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>View Topic</title>
</head>
<body>
<ul>
<c:forEach items="${commentList}" var="item">
<jsp:useBean id="item" type="mvc3.model.Comment"/>
<li>${item.getText()}</li>
</c:forEach>
</ul>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
查看jsp时会出现例外情况.在c:forEach循环的行中
dar*_*man 197
如果您知道Comment每次检索时都要查看所有内容,Topic那么请将字段映射更改为comments:
@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
Run Code Online (Sandbox Code Playgroud)
默认情况下,集合是延迟加载的,如果您想了解更多,请查看此内容.
Bor*_*ris 173
根据我的经验,我有以下方法来解决着名的LazyInitializationException:
(1)使用Hibernate.initialize
Hibernate.initialize(topics.getComments());
Run Code Online (Sandbox Code Playgroud)
(2)使用JOIN FETCH
您可以在JPQL中使用JOIN FETCH语法来显式提取子集合.这有点像EAGER的提取方式.
(3)使用OpenSessionInViewFilter
LazyInitializationException经常出现在视图层中.如果使用Spring框架,则可以使用OpenSessionInViewFilter.但是,我不建议你这样做.如果不正确使用,可能会导致性能问题.
gan*_*alf 51
默认情况下,hibernate懒洋洋地加载集合(关系),这意味着当你collection在代码中使用它时(这里comments是Topic类中的字段),hibernate从数据库中获取,现在的问题是你在控制器中获取集合(JPA会话中)已关闭).这是导致异常的代码行(您加载comments集合的地方):
Collection<Comment> commentList = topicById.getComments();
Run Code Online (Sandbox Code Playgroud)
您正在控制器中获取"comments"集合(topic.getComments())(JPA session已结束)并导致异常.另外,如果您comments在jsp文件中有这样的集合(而不是在控制器中获取它):
<c:forEach items="topic.comments" var="item">
//some code
</c:forEach>
Run Code Online (Sandbox Code Playgroud)
出于同样的原因,你仍会有同样的例外.
因为你只能FetchType.Eager在Entity类中只有两个集合(eagerly fetched collection),并且因为延迟加载比急切加载更有效,我认为这种解决问题的方法比仅仅更改FetchType为eager 更好:
如果你想初始化集合延迟,并且还可以使这个工作,最好将这段代码添加到你的web.xml:
<filter>
<filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Run Code Online (Sandbox Code Playgroud)
这段代码的作用是它会增加你的长度,JPA session或者正如文档所说的那样,它的使用"to allow for lazy loading in web views despite the original transactions already being completed."方式使JPA会话打开的时间会更长,因此你可以在jsp文件和控制器类中懒得加载集合.
sar*_*pex 47
我知道这是一个老问题,但我想提供帮助.您可以将事务注释放在您需要的服务方法上,在这种情况下,findTopicByID(id)应该具有
@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)
Run Code Online (Sandbox Code Playgroud)
有关此注释的更多信息,请点击此处
关于其他解决方案:
fetch = FetchType.EAGER
Run Code Online (Sandbox Code Playgroud)
这不是一个好习惯,只有在必要时才应该使用它.
Hibernate.initialize(topics.getComments());
Run Code Online (Sandbox Code Playgroud)
hibernate初始化程序将您的类绑定到hibernate技术.如果你的目标是灵活不是一个好的方法.
希望能帮助到你
小智 21
@Controller
@RequestMapping(value = "/topic")
@Transactional
Run Code Online (Sandbox Code Playgroud)
我通过添加来解决这个问题@Transactional,我认为这可以使会话打开
abe*_*tan 21
该问题是由关闭hibernate会话访问属性引起的.您没有控制器中的休眠事务.
可能的解决方案:
在服务层中(使用@Transactional)执行所有这些逻辑,而不是在控制器中.应该有合适的位置,它是应用程序逻辑的一部分,而不是控制器(在这种情况下,是加载模型的接口).服务层中的所有操作都应该是事务性的.即:将此行移动到TopicService.findTopicByID方法:
集合commentList = topicById.getComments();
使用'渴望'而不是'懒惰'.现在你没有使用'懒惰'..它不是一个真正的解决方案,如果你想使用懒惰,就像一个临时(非常临时)的解决方法.
一般来说,最好的解决方案是1.
GMK*_*GMK 18
为了延迟加载集合,必须有一个活动的会话.在Web应用程序中,有两种方法可以执行此操作.您可以使用Open Session In View模式,您可以使用拦截器在请求开始时打开会话,并在结束时关闭它.存在这样的风险:您必须具有可靠的异常处理,或者您可以绑定所有会话,并且您的应用可能会挂起.
处理此问题的另一种方法是收集控制器中所需的所有数据,关闭会话,然后将数据填入模型中.我个人更喜欢这种方法,因为它似乎更接近MVC模式的精神.此外,如果您从数据库中收到错误,则可以比在视图渲染器中更好地处理它.在这种情况下,你的朋友是Hibernate.initialize(myTopic.getComments()).您还必须将对象重新附加到会话,因为您正在为每个请求创建一个新事务.使用session.lock(myTopic,LockMode.NONE).
use*_*932 13
在第二次执行生成 JWT 令牌的方法后,我收到此错误。
该行user.getUsersRole().stream().forEachOrdered((ur) -> Roles.add(ur.getRoleId())); 产生了错误。
// MyUserDetails.java
@Service
public class MyUserDetails implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String email) {
/* ERROR
/* org.hibernate.LazyInitializationException: failed to
/* lazily initialize a collection of role:
/* com.organizator.backend.model.User.usersRole,
/* could not initialize proxy - no Session */
user.getUsersRole().stream().forEachOrdered((ur) ->
roles.add(ur.getRoleId()));
Run Code Online (Sandbox Code Playgroud)
就我而言,@Transactional注释解决了这个问题,
// MyUserDetails.java
import org.springframework.transaction.annotation.Transactional;
@Service
public class MyUserDetails implements UserDetailsService {
@Override
@Transactional // <-- added
public UserDetails loadUserByUsername(String email) {
/* No Error */
user.getUsersRole().stream().forEachOrdered((ur) ->
roles.add(ur.getRoleId()));
Run Code Online (Sandbox Code Playgroud)
Vla*_*cea 12
正如我在本文中所解释的,处理的最佳方法LazyInitializationException是在查询时获取它,如下所示:
select t
from Topic t
left join fetch t.comments
Run Code Online (Sandbox Code Playgroud)
您应该始终避免使用以下反模式:
因此,请确保FetchType.LAZY在查询时或在用于辅助集合的原始@Transactional范围内初始化关联Hibernate.initialize。
小智 11
如果您尝试在实体和Collection或Java对象列表之间建立关系(例如Long类型),它会像这样:
@ElementCollection(fetch = FetchType.EAGER)
public List<Long> ids;
Run Code Online (Sandbox Code Playgroud)
小智 9
我发现,宣布@PersistenceContext为EXTENDED也解决了这个问题:
@PersistenceContext(type = PersistenceContextType.EXTENDED)
Run Code Online (Sandbox Code Playgroud)
小智 9
你应该有两件事fetch = FetchType.LAZY。
@Transactional
Run Code Online (Sandbox Code Playgroud)
和
Hibernate.initialize(topicById.getComments());
Run Code Online (Sandbox Code Playgroud)
最好的解决方案之一是在application.properties文件中添加以下内容: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
小智 7
为了解决我的问题,它只是缺少这一行
<tx:annotation-driven transaction-manager="myTxManager" />
Run Code Online (Sandbox Code Playgroud)
在应用程序上下文文件中。
@Transactional没有考虑方法上的注释。
希望答案能帮助某人
小智 7
导致该问题的原因是,当与数据库的“连接”关闭时,代码正在访问惰性 JPA 关系(持久性上下文是 Hibernate/JPA 方面的正确名称)。
在 Spring Boot 中解决这个问题的一个简单方法是定义服务层并使用注释@Transactional。方法中的此注释创建一个传播到存储库层的事务,并保持打开的持久性上下文,直到方法完成。如果您访问事务方法内的集合,Hibernate/JPA 将从数据库中获取数据。
在您的情况下,您只需要使用您的@Transactional方法进行注释,并强制在该方法中获取集合(例如,通过询问其大小):findTopicByID(id)TopicService
@Transactional(readOnly = true)
public Topic findTopicById(Long id) {
Topic topic = TopicRepository.findById(id).orElse(null);
topic.getComments().size();
return topic;
}
Run Code Online (Sandbox Code Playgroud)
小智 7
这个延迟初始化问题有多种解决方案 -
1) 将关联 Fetch 类型从 LAZY 更改为 EAGER 但这不是一个好的做法,因为这会降低性能。
2)在关联的对象上使用 FetchType.LAZY 并在您的服务层方法中使用事务注释,以便会话保持打开状态,并且当您调用 topicById.getComments() 时,将加载子对象(评论)。
3) 另外,请尝试在控制器层使用 DTO 对象而不是实体。在您的情况下,会话在控制器层关闭。所以最好在服务层将实体转换为 DTO。
缺少控制器上的@Transactional注释
@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
Run Code Online (Sandbox Code Playgroud)
您的列表是延迟加载,因此列表未加载.打电话到列表是不够的.在Hibernate.initialize中使用以初始化列表.如果dosnt工作在list元素上运行并为每个元素调用Hibernate.initialize.这需要在从事务范围返回之前.看看这篇文章.
搜索 -
Node n = // .. get the node
Hibernate.initialize(n); // initializes 'parent' similar to getParent.
Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session
Run Code Online (Sandbox Code Playgroud)
这是我最近遇到的问题,我用它解决了
<f:attribute name="collectionType" value="java.util.ArrayList" />
Run Code Online (Sandbox Code Playgroud)
更详细的解释在这里,这节省了我的一天.
这是一个老问题,但以下信息可能会帮助人们寻找答案。
@VladMihalcea 的答案很有用。您不能依赖FetchType.EAGER,而是应该在需要时将注释加载到Topic实体中。
如果您没有显式定义查询以便可以指定 a join fetch,则使用@NamedEntityGraph和您可以在运行时@EntityGraph 覆盖(关联默认情况下使用 LAZY )并仅在需要时同时加载注释。这意味着您将注释限制为仅加载那些真正需要的方法(查询)。JPA定义的实体图:FetchType.LAZY@OneToManyTopic
实体图可以与 find 方法一起使用或作为查询提示来覆盖或增强 FetchType 语义。
您可以根据此处的JPA 示例来使用它。或者,如果您使用 Spring Data JPA,那么您可以根据Spring 提供的示例来使用它。
| 归档时间: |
|
| 查看次数: |
462497 次 |
| 最近记录: |