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

认证成功后调用Jpa Repository更新遇TransactionRequiredException异常

解决方案:Filter方法中事务注解不生效的问题

问题根源

你遇到的TransactionRequiredException是因为successfulAuthentication属于Spring Security的Filter类,而Filter由Servlet容器初始化,不在Spring容器的代理管理范围内。直接在Filter方法上添加@Transactional注解,Spring的AOP代理无法拦截该方法,导致事务不生效,执行更新/删除查询时就会抛出异常。

修复步骤

1. 抽取数据库操作到Spring管理的Service类

把端口相关的数据库逻辑抽成独立的Service,在Service方法上添加@Transactional注解(Spring会为Service创建代理,事务注解能正常生效):

@Service
public class PortService {

    private final PortRepo portRepo;

    // 构造注入(推荐方式)
    public PortService(PortRepo portRepo) {
        this.portRepo = portRepo;
    }

    @Transactional
    public Port assignPortToUser(Long userId) {
        // 启用过期端口
        portRepo.enableExpiredPorts(LocalDateTime.now());
        
        // 检查用户是否已有端口
        Port port = portRepo.checkIfUserHasPort(userId);
        
        if (port == null) {
            port = portRepo.getOpenPort();
        } else {
            portRepo.extendDuration(port.getPort());
        }
        
        portRepo.setInUse(userId, port.getPort());
        return port;
    }
}

2. 修正Repository中的SQL参数问题

你的enableExpiredPorts方法定义了@Param("localDateTime"),但SQL中直接用了datetime(),参数未被实际使用,需要修正SQL语句:

@Repository
public interface PortRepo extends JpaRepository<Port, Integer> {

    // 修正SQL,使用传入的参数
    @Modifying
    @Query(value = "UPDATE Port SET active=1 WHERE expiration < :localDateTime", nativeQuery = true)
    void enableExpiredPorts(@Param("localDateTime") LocalDateTime localDateTime);

    // 其他方法保持不变
    @Query(value = "SELECT * FROM Port WHERE user_id=:userId", nativeQuery = true)
    Port checkIfUserHasPort(@Param("userId") Long userId);

    @Query(value = "SELECT * FROM Port WHERE active=1 LIMIT 1", nativeQuery = true)
    Port getOpenPort();

    @Modifying
    @Query(value = "UPDATE Port SET active=0, user_id=:userId, expiration=datetime('now')  WHERE port=:port", nativeQuery = true)
    void setInUse(@Param("userId") Long userId, @Param("port") int port);

    @Modifying
    @Query(value = "UPDATE Port SET expiration=datetime('now')  WHERE port=:port", nativeQuery = true)
    void extendDuration(@Param("port") int port);
}

3. 修改Filter中的方法,调用Service

在你的AuthenticationFilter中注入PortService,调用Service方法处理端口逻辑:

// 注入PortService和UserRepo
private final PortService portService;
private final UserRepo userRepo;

// 构造注入
public YourAuthenticationFilter(PortService portService, UserRepo userRepo) {
    this.portService = portService;
    this.userRepo = userRepo;
}

@Override
public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
    User user = (User) authentication.getPrincipal();
    AppUser appUser = userRepo.findByUsername(user.getUsername());

    // 调用Service处理端口分配
    Port port = portService.assignPortToUser(appUser.getUserId());
    
    // 后续无关逻辑
}

原理说明

Spring的事务管理依赖AOP代理,只有Spring容器管理的Bean(比如Service、Repository)的方法会被代理拦截,从而应用事务逻辑。Filter不属于Spring Bean的代理范围,直接在Filter方法上加@Transactional无效。将逻辑移到Service后,Service的方法被Spring代理,事务注解就能正常生效,执行更新/删除查询时会有事务上下文,不会再抛出TransactionRequiredException。

内容的提问来源于stack exchange,提问作者Grimalkin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 03:24:24