Spring Boot中Hibernate懒加载初始化异常(LazyInitializationException)解决
Hibernate懒加载集合抛出
LazyInitializationException的解决方法 场景与代码
系统中存在User和UserRole两个实体,二者为一对多关联关系。尝试从User实体懒加载UserRole集合时出现异常,相关代码如下:
实体类代码
public class User { @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List<UserRole> userRoles; } public class UserRole { @ManyToOne User user; String name; }
业务方法代码
public String getUserRoleName(Long userId) { User user = repository.findById(userId); List<UserRole> userRoles = user.getUserRoles(); String name = userRoles.stream().map(obj -> obj.getName()).collect(Collectors.joining(", ")); return name; }
异常信息
运行上述代码时抛出如下异常:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: co.entity.User.userRoles, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606) at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218) at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387) at java.base/java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821) at java.base/java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) at co.service.UserService.getUserRoleName(UserService.java:20) at co.controller.UserController.getUserRoleName(UserController.java:10) at jdk.internal.reflect.GeneratedMethodAccessor193.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1597) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:829)
问题原因
Hibernate默认对关联集合采用懒加载策略,即仅在实际访问集合时才会从数据库加载数据。但repository.findById(userId)执行完成后,Hibernate会话(Session)已经关闭,此时尝试访问user.getUserRoles()触发懒加载时,因没有可用Session导致无法初始化代理集合,从而抛出LazyInitializationException。
解决方法
方法1:添加@Transactional注解(已验证有效)
在业务方法上添加@Transactional注解,确保整个方法执行过程中Hibernate会话保持打开状态,懒加载操作可正常进行:
import org.springframework.transaction.annotation.Transactional; @Transactional public String getUserRoleName(Long userId) { User user = repository.findById(userId); List<UserRole> userRoles = user.getUserRoles(); String name = userRoles.stream().map(obj -> obj.getName()).collect(Collectors.joining(", ")); return name; }
方法2:改为急加载
修改User实体的@OneToMany注解,设置fetch = FetchType.EAGER,在获取User时同时加载关联的UserRole集合。注意该方式会增加数据库查询开销,仅在确定每次获取User都需要用到UserRole时使用:
public class User { @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) private List<UserRole> userRoles; }
方法3:Repository层使用JOIN FETCH查询
在Repository接口中自定义查询方法,通过JPQL的JOIN FETCH语法一次性加载User和关联的UserRole集合,避免后续懒加载:
import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; public interface UserRepository extends JpaRepository<User, Long> { @Query("SELECT u FROM User u JOIN FETCH u.userRoles WHERE u.id = :userId") User findByIdWithRoles(@Param("userId") Long userId); }
之后业务方法调用该自定义方法即可:
public String getUserRoleName(Long userId) { User user = repository.findByIdWithRoles(userId); List<UserRole> userRoles = user.getUserRoles(); String name = userRoles.stream().map(obj -> obj.getName()).collect(Collectors.joining(", ")); return name; }
内容的提问来源于stack exchange,提问作者Akbarhussain Aghariya
相关产品推荐
相关产品推荐

