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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 17:34:51