You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 17:44:55