Spring Security表单登录触发LazyInitializationException问题求助
解决Spring Security表单登录的LazyInitializationException问题
核心原因
你遇到的问题本质是:Spring Security认证流程中,UserDetailsService加载用户后,事务已结束,此时调用getAuthorities()访问懒加载的roles集合时,Hibernate Session已经关闭,导致无法初始化代理对象。你在User类上加的@Transactional无效,因为这个注解无法覆盖Spring Security的认证查询流程;而注册流程能正常访问是因为注册操作全程处于事务上下文内。
具体解决方案
方案1:在UserDetailsService的查询逻辑中确保事务覆盖并加载关联集合
- 在
UserDetailsService实现类的loadUserByUsername方法上添加@Transactional注解,让用户查询及后续权限集合的访问处于同一事务中:
@Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); // 主动触发roles集合加载,避免后续懒加载报错 user.getRoles().size(); return user; } }
- 更高效的方式是用
fetch join的JPQL查询,直接在查询时加载roles集合,从根源避免懒加载:
// 在UserRepository中定义查询方法 @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.username = :username") Optional<User> findByUsernameWithRoles(@Param("username") String username);
之后在loadUserByUsername中调用这个方法即可。
方案2:修改实体关联的FetchType为EAGER
直接将User类中roles字段的懒加载改为立即加载:
@ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles = new HashSet<>();
注意:这种方式会在每次查询User时都加载roles集合,若roles数据量大或用户查询频繁,可能存在性能损耗,需根据业务场景评估。
方案3:开启Open Session In View(不推荐生产环境)
可以通过配置spring.jpa.open-in-view=true开启OSIV模式,让Hibernate Session在请求全程保持打开。但这种方式容易引发内存泄漏和性能问题,仅适合小型项目或调试场景。
内容的提问来源于stack exchange,提问作者Batuhan Kertmen Student
相关产品推荐
相关产品推荐

