spring-data-jpa 使用Jpa资料档案库更新时出现TransactionRequiredException

slmsl1lt  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(216)

在成功验证后,我想调用db以检索一些附加信息,但我得到:

org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query

我的successfulAuthentication方法:

@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());

        Port port;
        portRepo.enableExpiredPorts(LocalDateTime.now());
        port = portRepo.checkIfUserHasPort(appUser.getUserId());
        if (port == null) {
            port = portRepo.getOpenPort();
        } else {
            portRepo.extendDuration(port.getPort());
        }
        portRepo.setInUse(appUser.getUserId(), port.getPort());
        //Some unrelated logic
    }

我正在调用的回购协议:

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

    @Modifying
    @Query(value = "UPDATE Port SET active=1 WHERE expiration<datetime()", 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);
}

添加@Transactional注解没有帮助(它是来自org.springframework.transaction.annotation.Transactional的正确注解)

1l5u6lss

1l5u6lss1#

successulAuthentication是一个内部方法调用,因此添加@Transactional没有帮助(因为没有通过代理)。
最简单的方法是创建另一个类并将逻辑移到那里,然后用@Transactional注解该类(或方法)。

@Component
@Transactional
public class AfterAuthenticationService {

  private final UserRepo userRepo;
  private final PortRepo portRepo;

  public AfterAuthenticationService(UserRepo userRepo, PortRepo portRepo) {
    this.userRepo=userRepo;
    this.portRepo=portRepo;
}

  public void onSuccessfulAuthentication(Authentication authentication) {
    User user = (User) authentication.getPrincipal();
    AppUser appUser = userRepo.findByUsername(user.getUsername());

    portRepo.enableExpiredPorts(LocalDateTime.now());
    Port port = portRepo.checkIfUserHasPort(appUser.getUserId());
    if (port == null) {
      port = portRepo.getOpenPort();
    } else {
      portRepo.extendDuration(port.getPort());
    }
    portRepo.setInUse(appUser.getUserId(), port.getPort());
   //Some unrelated logic
  }
}

然后在筛选器中调用此服务,接下来调用super方法。

@Override
public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
  this.afterAuthenticationService.onSuccessfulAuthentication(authentication);
  super.successfulAuthentication(request, response, chain, authentication);    
}

或者把这一切都抛开,为InteractiveAuthenticationSuccessEvent编写一个事件侦听器,然后在那里运行你的逻辑。这样你就不必扩展过滤器等,仍然可以得到想要的行为(并使它松散耦合)。

@Component
@Transactional
public class AfterAuthenticationListener {

  private final UserRepo userRepo;
  private final PortRepo portRepo;

  public AfterAuthenticationService(UserRepo userRepo, PortRepo portRepo) {
    this.userRepo=userRepo;
    this.portRepo=portRepo;
}

  @EventListener(InteractiveAuthenticationSuccessEvent.class)
  public void onSuccesfulAuthentication(InteractiveAuthenticationSuccessEvent event) {
    Authentication authentication = event.getAuthentication();
    User user = (User) authentication.getPrincipal();
    AppUser appUser = userRepo.findByUsername(user.getUsername());

    portRepo.enableExpiredPorts(LocalDateTime.now());
    Port port = portRepo.checkIfUserHasPort(appUser.getUserId());
    if (port == null) {
      port = portRepo.getOpenPort();
    } else {
      portRepo.extendDuration(port.getPort());
    }
    portRepo.setInUse(appUser.getUserId(), port.getPort());
   //Some unrelated logic
  }
}

现在,您可以使用侦听器省略修改后的筛选器。

相关问题