编写完一个拥有jwt认证的项目后,在进行测试时,发现已经登录获得token后,调用用户接口,仍然是401未授权状态
查看Spring日志发现
failed to lazily initialize a collection of role: com.test.simpleLoginRegister.model.User.roles: could not initialize proxy - no Session
分析原因在FetchType.LAZY上:
在查询出该实体对象时想使用其关联对象时比如get()等操作,就会报标题错误,主要原因是不在同一会话中,FetchType.LAZY只在实体对象想使用其关联对象时进行才进行查询数据库加载,可是你查询实体对象语句结束后会话就失效了,从而加载不了关联对象
解决:
本项目因为内容不多,所以直接改为FetchType.EAGER
其他方法参考:
一、调整Fetch加载策略
- 将延迟加载改为立即加载
在关联属性(如roles
)的注解中,将fetch
属性设置为FetchType.EAGER
,强制在加载主实体时直接加载关联集合。
@ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles;
适用场景:关联数据较小且频繁访问时。但需注意,全局改为EAGER会导致查询性能下降(N+1问题)。
- 通过JPQL或HQL显式预加载
在查询时使用JOIN FETCH
语句,主动加载关联集合:
@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
User findUserWithRolesById(@Param("id") Long id);
优势:避免全局EAGER的副作用,按需加载。
二、管理Session生命周期
- 确保操作在事务范围内
在Service层方法上添加@Transactional
注解,确保整个方法执行期间Session处于打开状态:
@Transactional
public User getUserWithRoles(Long id) {
User user = userRepository.findById(id);
user.getRoles().size(); // 触发加载
return user;
}
注意:事务边界需覆盖到数据访问和业务逻辑处理。
- 使用Open Session in View模式(OSIV)
在Web应用中,通过配置OpenSessionInViewFilter
或OpenSessionInViewInterceptor
,将Session生命周期延长到请求处理结束后。例如在Spring Boot中:
spring.jpa.open-in-view=true
适用场景:视图层(如Controller或JSP)需要访问延迟加载的数据。但需注意,此模式可能因长Session占用连接池资源,高并发场景慎用。
三、手动初始化关联集合
- 在Session关闭前手动触发加载
在DAO层或Service层,通过调用集合的size()
、iterator()
等方法强制初始化:
Hibernate.initialize(user.getRoles());
适用场景:明确知道需要访问哪些关联数据时,精准控制加载时机。
四、其他优化方案
- DTO投影与序列化处理
在返回数据到前端时,通过DTO或VO剥离实体关联,避免因序列化触发延迟加载:
public class UserDTO {
private Long id;
private String name;
private List<String> roleNames; // 手动映射角色名
}
优势:减少数据传输量,避免序列化异常。
- 检查事务配置与异常回滚
确保@Transactional
的rollbackFor
属性包含实际抛出的异常类型,避免事务提前回滚导致Session关闭。
总结选择方案
- 简单场景:直接使用
FetchType.EAGER
或@Transactional
注解。 - Web应用:启用OSIV模式(需权衡性能)。
- 复杂业务逻辑:通过JPQL预加载或手动初始化。
- 前后端分离架构:优先采用DTO投影,避免实体直接暴露。
若仍存在问题,可能需要检查Hibernate配置(如hibernate.enable_lazy_load_no_trans
)、连接池设置及事务传播行为。