Spring 数据无法延迟初始化角色集合,无法初始化代理 - 没有会话



我正在使用Spring Boot制作一个API,但我似乎从未设法初始化惰性集合。唯一对我有用的解决方案是将其更改为渴望,但这不是解决方案。

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "user")
public class UserEntity {
@Id
@GeneratedValue
private Long id;
@Column(unique = true)
String email;
@ElementCollection(fetch = LAZY)
List<String> roles = new ArrayList<>();
public UserEntity(String email) {
this.email = email;
}
}
@Order(Ordered.LOWEST_PRECEDENCE)
@Component
public class AuthFilter extends OncePerRequestFilter {
Logger LOG = LoggerFactory.getLogger(AuthFilter.class);
@Autowired
UserRepository userRepository;
@Override
@Transactional
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null && authentication.isAuthenticated()){
String email = authentication.getName();
UserEntity user = userRepository.findByEmail(email).orElse(new UserEntity(email));
user.getRoles().clear();
user.getRoles().addAll(authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()));
userRepository.save(user);
}
filterChain.doFilter(request, response);
}
}

这是我的两节课。将从身份验证对象创建用户的用户实体和筛选器。当调用线路user.getRoles().clear();时,我得到著名的错误org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ch.ciip.ressources.api.user.UserEntity.roles, could not initialize proxy - no Session

据我了解,Spring Data 将为每个惰性关系创建一个代理,并且只有在访问数据后才会获取数据。但是要访问它需要一个会话,显然,Spring 在访问代理时无法创建会话,但是,当我在存储库上调用保存时,这样做没有问题。

我尝试的是:

  • @Transactional来自org.springframework.transaction.annotation.Transactionaljavax.transaction.Transactional。但它绝对不会改变任何东西。
  • 将提取类型更改为EAGER。这种方法有效,但这不是我想做的。
  • 在应用程序属性文件中将enable_lazy_load_no_trans=true更改为 true。不推荐使用此方法,因此我不会尝试。
  • 在尝试修改惰性集合之前调用方法。例如,调用user.getRoles().size();以便对其进行初始化。但它不起作用。
  • 调用Hibernate.initialize(user.getRoles());以初始化集合。但它不起作用。

我想知道为什么春天说No Session,因为它确实为userRepository.findByEmail(email)userRepository.save(user)创造了一个会话。

我可以手动创建一个实体管理器并使用它来获取/保留我的实体。但这不是目标,因为我使用的是Spring Data JPA。

使方法public,即:

@Override
@Transactional
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null && authentication.isAuthenticated()){
String email = authentication.getName();
UserEntity user = userRepository.findByEmail(email).orElse(new UserEntity(email));
user.getRoles().clear();
user.getRoles().addAll(authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()));
userRepository.save(user);
}
filterChain.doFilter(request, response);
}

创建一个单独的服务类并将其注入到此过滤器中是我可以提出的解决方法。

@Service
class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void findUserByEmailAndAddRoles(String email, Set<String> authorities) {
UserEntity user = userRepository.findByEmail(email)
.orElse(new UserEntity(email));
user.getRoles().clear();
user.getRoles().addAll(authorities);
userRepository.save(user);
}
}

然后在过滤器中使用它

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null && authentication.isAuthenticated()){
String email = authentication.getName();
Set<String> authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
userService.findUserByEmailAndAddRoles(email, authorities);
}
filterChain.doFilter(request, response);
}

最新更新