ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JPA] @Scheduled 어노테이션과 트랜잭션 처리
    이슈 해결 2019. 7. 23. 11:53

    회사에서 특정 주기에 따라 사용자들의 상태를 변경하는 서비스가 필요해서 배치 프로그램을 만들려고 했다가 우연히 Okky 사이트를 통해 @Scheduled 어노테이션이 스케쥴링 기능을 제공한다는 것을 알게되서 처음으로 이 기능을 사용해 DB 조작을 해보기로 했다.

     

     

    대략 코드는 다음과 같았다.

     

    UserStateService

    @Service
    public class UserStateService {
        @Autowired
        private UserRepository userRepo;
        
        @Scheduled(cron = "10 * * * * *")
        @transactional
        public void clearState() {
            LocalDateTime endDate = LocalDateTime.now();
            
            List<User> findUserList = userRepo.findAllByEndDate(endDate);
            
            findUserList.forEach(row -> {
                row.updateState(false);
            });
            
            userRepo.saveAll(findUserList);
        }
    }

     

    1. find 메소드를 통해 상태를 변경해야할 대상들을 불러온다.

    2. 불러온 대상들의 상태를 forEach()를 통해 수정해준다.

    3. 수정된 정보를 saveAll()을 통해 저장한다.

     

     

     

    문제

    스케쥴링 시간이 되어 해당 스케쥴 서비스가 수행될 때 아래와 같은 트랜잭션 요구 에러가 발생했다.

     


    Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query

     

    update/delete 쿼리를 사용 중에 트랜잭션이 필요하다는 요구 에러이다.

     

     

     

    원인

    이미 트랜잭션에 대한 처리는 잘 되고 있는데 왜 이런 문제가 발생하는지 찾아봐도 원하는 내용을 찾기가 쉽지 않았다.

     

    그러던 중 다음과 같은 글을 하나 찾았다.

    https://stackoverflow.com/questions/33248846/spring4-scheduled-transaction-throws-no-transaction-is-in-progress-at-flush-fo

     

    내용은 트랜잭션 매니저가 @EnableTransactionManagement를 통해 DataSourceTransactionManager로 구성된 경우, 하이버네이트의 begin()메소드가 AbstractTransactionImpl을 부르지 않는다는 것이다.

    이로 인해서 @Scheduled를 사용하는 메소드에서 트랜잭션 처리가 불가능하다는 것이다.

    트랜잭션 처리를 하기 위해서는 트랜잭션 매니저를 JpaTransactionManager로 구현하라고 되어있다.

     

     

     

    해결


    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages="com.xxxx.xxx", 
        entityManagerFactoryRef="entityManagerFactory", 
        transactionManagerRef="jpaTransactionManager")
    public class WebDBConfig {
        @Bean
        @ConfigurationProperties(prefix="spring.datasource.hikari")
        public DataSource myDataSource() {
            return DataSourceBuilder.create().build();
        }
        
        @Bean
        public LocalContainerentityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
            return builder.dataSource(this.myDataSource())
                .packages("com.xxxx.xxx")
                .build();
        }
        
        @Bean
        public PlatformTransactionManager jpaTransactionManager(@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
            return new JpatransactionManager(entityManagerFactory);
        }
    }

     

    그래서 트랜잭션 매니저를 바꿨습니다...

Designed by Tistory.