自定义getCurrentAuditor调用JPA仓库时触发JPA事务提交错误
问题原因及解决方案
核心原因
Spring JPA审计的AuditorAware.getCurrentAuditor()方法执行时,处于实体生命周期的回调阶段(如@PreUpdate/@PrePersist),此时调用Spring Data JPA仓库方法(默认带有@Transactional注解)会触发新事务创建,与当前正在进行的审计事务产生冲突,最终导致事务提交失败。
具体解决方案
1. 直接使用EntityManager查询,规避仓库事务
替换UserRepository调用为EntityManager手动查询,跳过Spring Data JPA默认的事务包装:
public class AuditorAwareImpl implements AuditorAware<Integer> { @PersistenceContext private EntityManager em; @Override public Optional<Integer> getCurrentAuditor() { DefaultOidcUser oidcUser = (DefaultOidcUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String emailId = oidcUser.getEmail(); try { User user = em.createQuery("SELECT u FROM User u WHERE u.emailId = :email", User.class) .setParameter("email", emailId) .getSingleResult(); return Optional.of(user.getUserId()); } catch (NoResultException e) { // 处理用户不存在的场景,比如返回空或抛出业务异常 return Optional.empty(); } } }
2. 修改仓库方法的事务传播行为
在UserRepository.findByEmailId方法上添加@Transactional(propagation = Propagation.SUPPORTS),让方法复用当前已存在的事务,而非创建新事务:
public interface UserRepository extends JpaRepository<User, Integer> { @Transactional(propagation = Propagation.SUPPORTS) User findByEmailId(String emailId); }
3. 缓存用户ID到SecurityContext,避免重复查询
在OAuth2认证流程中自定义OidcUserService,加载用户时查询数据库获取ID并存入用户属性,后续直接从SecurityContext读取:
@Service public class CustomOidcUserService extends OidcUserService { @Autowired private UserRepository userRepo; @Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { OidcUser oidcUser = super.loadUser(userRequest); String email = oidcUser.getEmail(); User user = userRepo.findByEmailId(email); // 将用户ID存入属性集合 Map<String, Object> attributes = new HashMap<>(oidcUser.getAttributes()); attributes.put("userId", user.getUserId()); return new DefaultOidcUser(oidcUser.getAuthorities(), oidcUser.getIdToken(), attributes); } }
然后在AuditorAwareImpl中直接读取ID:
@Override public Optional<Integer> getCurrentAuditor() { DefaultOidcUser oidcUser = (DefaultOidcUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); Integer userId = (Integer) oidcUser.getAttributes().get("userId"); return Optional.ofNullable(userId); }
内容的提问来源于stack exchange,提问作者shirdisai
相关产品推荐
相关产品推荐

