使用Spring-Boot(v2.0.0.RELEASE)+Spring-Data-JPA开发MySQL CRUD Demo遇懒加载异常
嘿,这个LazyInitializationException: could not initialize proxy - no Session异常我太熟了!本质就是懒加载的对象在Hibernate Session已经关闭之后才被访问,导致没法初始化代理对象。结合你用的Spring Boot 2.0.0.RELEASE + Spring Data JPA的场景,给你几个靠谱的解决办法:
1. 让事务覆盖到属性访问的全流程
Spring Data JPA的Repository方法默认是事务性的,但事务只在Repository方法执行期间存在——也就是说,你调用userRepository.findById()拿到User对象后,Session就已经关闭了。如果这时候再去访问User的懒加载关联属性(比如角色、订单这类字段,虽然你贴的User代码里没写,但大概率是有这类关联),自然会报错。
解决办法很简单:在测试方法或者Service层的业务方法上加上@Transactional注解,让整个方法执行期间Session保持打开:
@RunWith(SpringRunner.class) @SpringBootTest @Transactional // 加上这个注解,事务会覆盖整个测试方法 public class UserServiceTest { @Autowired private UserRepository userRepository; @Test public void testUserQuery() { User user = userRepository.findById(1).orElse(null); // 这里访问懒加载属性就不会报错了 System.out.println(user.getRoles()); // 假设roles是懒加载的关联字段 } }
2. 把懒加载改成急加载(按需使用)
如果某个关联属性你每次查询User都必须用到,直接把它改成急加载就行。在User类的关联字段上修改fetch属性:
@Entity public class User implements Serializable { // ...其他字段 @OneToMany(fetch = FetchType.EAGER) // 改成急加载 private List<Role> roles; // ...getter&setter }
⚠️ 注意:急加载会导致查询User时一次性拉取所有关联数据,可能引发性能问题,只适合确实每次都需要该属性的场景。
3. 自定义查询用JOIN FETCH提前加载
这种方式更灵活,只在需要的时候主动加载指定的懒加载属性。在UserRepository里写自定义查询:
public interface UserRepository extends JpaRepository<User, Integer> { // 用JOIN FETCH提前加载roles属性 @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id") Optional<User> findByIdWithRoles(@Param("id") Integer id); }
调用这个方法查询出来的User对象,它的roles属性已经被初始化完成,哪怕在事务外访问也不会触发懒加载异常。
4. 开启Open Session In View(仅推荐测试/快速开发)
这个配置会让Session在整个请求周期内保持打开,直到视图渲染完成。在Spring Boot的application.properties里添加:
spring.jpa.open-in-view=true
⚠️ 生产环境不推荐用:Session长时间打开可能耗尽数据库连接池,还容易引发性能问题,只适合快速验证功能的场景。
如果你的User类里没有任何关联属性却还是报这个错,那大概率是对象已经脱离了持久化上下文(比如被序列化后再访问),这时候可以尝试重新查询一次对象,或者确保在事务内完成所有属性访问。
内容的提问来源于stack exchange,提问作者Shuai Junlan

