You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 11:36:13