Spring Boot+Hibernate懒加载异常求助:Failed to execute CommandLineRunner
问题描述
使用Spring Boot、JPA和Hibernate开发Java应用,Project与Activity为一对多关联关系,运行时抛出LazyInitializationException,错误堆栈如下:
java.lang.IllegalStateException: Failed to execute CommandLineRunner at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:773) ~[spring-boot-3.1.0.jar:3.1.0] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:754) ~[spring-boot-3.1.0.jar:3.1.0] at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-3.1.0.jar:3.1.0] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1305) ~[spring-boot-3.1.0.jar:3.1.0] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1294) ~[spring-boot-3.1.0.jar:3.1.0] at org.classroomcreative.ClassRoomCreativeStart.main(ClassRoomCreativeStart.java:25) ~[classes/:na] Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.classroomcreative.domain.Project.activities: could not initialize proxy - no Session at org.hibernate.collection.spi.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:635) ~[hibernate-core-6.2.2.Final.jar:6.2.2.Final] at org.hibernate.collection.spi.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218) ~[hibernate-core-6.2.2.Final.jar:6.2.2.Final] at org.hibernate.collection.spi.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:615) ~[hibernate-core-6.2.2.Final.jar:6.2.2.Final] at org.hibernate.collection.spi.AbstractPersistentCollection.read(AbstractPersistentCollection.java:136) ~[hibernate-core-6.2.2.Final.jar:6.2.2.Final] at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:610) ~[hibernate-core-6.2.2.Final.jar:6.2.2.Final] at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na] at org.classroomcreative.domain.Project.toString(Project.java:12) ~[classes/:na] at org.classroomcreative.ClassRoomCreativeStart.lambda$demo$0(ClassRoomCreativeStart.java:48) ~[classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:770) ~[spring-boot-3.1.0.jar:3.1.0] ... 5 common frames omitted
实体类代码
Project类
@Data @Entity @NoArgsConstructor @AllArgsConstructor public class Project { @Id @GeneratedValue(strategy = GenerationType.AUTO) Long id; String target; List<String> images; @OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true) List<Activity> activities; }
Activity类
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Activity { @Id @GeneratedValue(strategy = GenerationType.AUTO) Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "project_id") Project project; String development; String targets; String basicKnowledge; String assessment; }
测试代码
@SpringBootApplication @Slf4j public class ClassRoomCreativeStart { public static void main(String[] args) { SpringApplication.run(ClassRoomCreativeStart.class, args); } @Bean public CommandLineRunner demo(ProjectRepository projectRepository, ActivityRepository activityRepository) { return (args) -> { ProjectServiceImpl service = new ProjectServiceImpl(projectRepository); Activity activity1 = new Activity(); activity1.setTargets("Target for Activity 1"); Project project = new Project(); project.setTarget("Sample Project"); project.setActivities(List.of(activity1)); service.saveProject(project); // Retrieve projects and activities log.info("Projects found with findAll():"); log.info("-------------------------------"); for (Project p : service.getProjects()) { log.info(p.toString()); } }; } }
服务层代码
@Service public class ProjectServiceImpl { private final ProjectRepository repository; public ProjectServiceImpl(ProjectRepository repository){ this.repository = repository; } @Transactional public Project saveProject(Project project){ return repository.save(project); } @Transactional(readOnly = true) public List<Project> getProjects(){ return repository.findAll(); } }
已尝试过相关解决方案,但问题仍未解决,寻求帮助。
解决方案
核心原因
错误触发点在Project.toString()调用时,懒加载的activities集合尝试初始化,但此时Hibernate Session已经关闭(getProjects()方法的@Transactional作用域已结束)。
具体解决办法
方法1:修改Project类的toString方法,排除activities集合
@Data注解自动生成的toString会包含所有字段,包括懒加载集合。可以手动重写toString,或者用@ToString(exclude = "activities")排除该字段:
@Data @Entity @NoArgsConstructor @AllArgsConstructor @ToString(exclude = "activities") // 排除懒加载集合 public class Project { // ... 原有代码 }
方法2:在查询时主动加载关联集合(Fetch Join)
在ProjectRepository中定义查询方法,使用Fetch Join一次性加载关联的activities,避免懒加载:
public interface ProjectRepository extends JpaRepository<Project, Long> { @Query("SELECT p FROM Project p JOIN FETCH p.activities") List<Project> findAllWithActivities(); }
修改服务层的getProjects()方法调用该查询:
@Transactional(readOnly = true) public List<Project> getProjects(){ return repository.findAllWithActivities(); }
方法3:修改关联的Fetch模式为EAGER(不推荐)
将Project类的@OneToMany注解添加fetch = FetchType.EAGER,强制加载关联集合,但这种方式会增加数据库查询开销,仅适合小数据量场景:
@OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) List<Activity> activities;
方法4:确保Session在toString调用时仍处于打开状态
将日志输出的逻辑移到@Transactional作用域内,比如在服务层添加一个方法专门处理查询并打印:
@Service public class ProjectServiceImpl { private final ProjectRepository repository; private final Logger log = LoggerFactory.getLogger(ProjectServiceImpl.class); public ProjectServiceImpl(ProjectRepository repository){ this.repository = repository; } @Transactional public Project saveProject(Project project){ return repository.save(project); } @Transactional(readOnly = true) public void printAllProjects(){ List<Project> projects = repository.findAll(); log.info("Projects found with findAll():"); log.info("-------------------------------"); for (Project p : projects) { log.info(p.toString()); } } }
在测试代码中调用这个方法替换原有的循环打印逻辑:
// 替换原有的循环打印逻辑 service.printAllProjects();
内容的提问来源于stack exchange,提问作者Fjgonmir
相关产品推荐
相关产品推荐

