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

如何修复Spring Boot中因实体toString/equals方法引发的LazyInitializationException?

解决Hibernate LazyInitializationException(关联单元测试需求)

你的问题核心很明确:toString()和equals()方法中直接引用了懒加载的transfers集合,当你从仓库获取User对象后,Hibernate Session已经关闭,此时调用这两个方法访问未初始化的懒加载集合,就触发了LazyInitializationException。而且这两个方法是单元测试必需的,下面给你几个针对性的修复方案:


方案1:给单元测试添加事务注解(最省心的测试场景解决方案)

如果只是单元测试中出现这个问题,最简单的办法是给测试类或测试方法加上@Transactional注解,让Hibernate Session在整个测试方法执行期间保持打开状态:

@SpringBootTest
@Transactional
public class UserServiceTest {
    @Test
    public void testTransferToFriend() {
        // 你的测试逻辑
        // 此时调用User的toString/equals,懒加载的transfers会被正常初始化
    }
}

这个方案不需要修改业务代码,完全适配测试场景的需求。

方案2:修改toString/equals,排除懒加载集合

如果测试中不需要对比transfers集合的内容,可以直接把它从toString()和equals()中移除,保留核心字段的输出和对比:

@Override
public String toString() {
    return "User{" +
            "id=" + id +
            ", email='" + email + '\'' +
            ", password='" + password + '\'' +
            ", firstName='" + firstName + '\'' +
            ", lastName='" + lastName + '\'' +
            ", ibanCode=" + ibanCode +
            ", bicCode=" + bicCode +
            ", balance=" + balance +
            ", friendsList='" + friendsList + '\'' +
            // 移除transfers的引用
            '}';
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    User user = (User) o;
    return ibanCode == user.ibanCode &&
            bicCode == user.bicCode &&
            Double.compare(user.balance, balance) == 0 &&
            Objects.equals(id, user.id) &&
            Objects.equals(email, user.email) &&
            Objects.equals(password, user.password) &&
            Objects.equals(firstName, user.firstName) &&
            Objects.equals(lastName, user.lastName) &&
            Objects.equals(friendsList, user.friendsList);
            // 移除transfers的对比
}

如果测试中确实需要验证transfers集合,可以单独编写断言逻辑,不用依赖equals()方法。

方案3:查询时主动加载transfers集合

如果你不想修改toString()和equals(),可以在查询User的时候主动初始化transfers集合,确保Session关闭前就完成加载:

方式A:自定义Repository查询方法(推荐)

在UserRepository中添加带JOIN FETCH的查询方法,主动关联加载transfers:

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u JOIN FETCH u.transfers WHERE u.id = :userId")
    Optional<User> findByIdWithTransfers(@Param("userId") Long userId);
}

然后在业务方法中调用这个方法代替findById(),这样获取到的User对象的transfers已经被初始化,后续调用toString()/equals()就不会触发异常。

方式B:设置EAGER加载(谨慎使用)

你之前尝试过的fetch="EAGER"其实也能解决问题,但要注意:EAGER加载会让每次查询User都自动加载transfers集合,可能会带来不必要的性能开销,只在确实需要全局加载的场景使用。


为什么你之前的尝试没完全生效?

你设置spring.jpa.properties.hibernate.enable_lazy_load_no_trans=false后仍然报错,大概率是因为toString()/equals()调用时Session已经彻底关闭,这个配置只适用于Session还处于"软关闭"状态的场景;而fetch="EAGER"可能是配置没有正确生效(比如注解写法错误,应该是fetch = FetchType.EAGER),或者你在测试中还是用了旧的查询方法。

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 10:22:27