2011-03-08 3 views
2

아마도 뭔가 잘못하고 있지만 다음과 같은 상황에서 좋은 방법을 찾을 수 없습니다.Spring 트랜잭션을 다른 쓰레드로 전파하는 방법은 무엇입니까?

작업을 실행하기 위해 Spring Batch 아래에있는 서비스를 단위 테스트하고 싶습니다. 작업은 별도의 스레드에 미리 구성된 AsyncTaskExecutor을 통해 실행됩니다. 내 단위 테스트에서 나는 싶습니다 : 작업이

  • 사용 DAO에 완료 될 때까지

    1. 이 작업을
    2. 대기를 시작합니다 서비스 메소드를 호출 몇 도메인 객체를 생성하고 DAO
    3. 그들을 통해 지속 도메인 객체를 검색하고 분명히 자신의 상태

    을 확인, 무엇보다도 하나의 트랜잭션 내에서 실행되어야하지만 불행하게도, transactions are not propagated to new threads는 (나는이 뒤에 근거를 이해). 내 마음에 온

    아이디어 :

    • 단계 (1) 후 트랜잭션 # 1을 커밋합니다. 단위 테스트 후에 DB 상태를 롤백해야하므로 좋지 않습니다.
    • 작업 구성에 Isolation.READ_UNCOMMITTED을 사용하십시오. 그러나이를 위해서는 테스트와 생산을위한 두 가지 구성이 필요합니다.
  • +0

    나는 어떤 사람들이 동의하지 않는 것을 알고 있지만 실행 한 후 롤백 테스트를 실행하는 아주 좋은 방법이 아닙니다. 특히 ORM을 사용하는 경우 세션을 선택하거나 플러시 할 때까지 삽입/업데이트 명령을 실행하지 않으므로 ORM을 사용하는 경우 특히 유용합니다. – Augusto

    +1

    @Augusto : 테스트가 서로 충돌하지 않기 때문에 롤백 기능이 좋습니다. 그렇지 않으면 유닛 테스트 클래스 당 하나의'setUp()'과 정확히 하나의'test()'가 필요합니다.보상 트랜잭션 (예 :'delete form mytable')을 사용할 수는 있지만 스프링 철학에 어떻게 맞는지는 알 수 없습니다. –

    +0

    다시, 나는 모두가 내가 제안한 것에 동의한다고 생각하지 않는다. 그리고 실제로 무언가를 테스트하고 있는지 확인해야합니다. 그렇지 않으면 어설 션없이 테스트를 수행하는 것과 같습니다. – Augusto

    답변

    2

    가장 간단한 해결책은 테스트 실행 중 SyncTaskExecutor를 사용하여 JobLauncher를 구성하는 것입니다.이 방법은 테스트와 동일한 스레드에서 작업이 실행되고 트랜잭션을 공유합니다.

    작업 실행자 구성을 별도의 스프링 구성 XML 파일로 이동할 수 있습니다. 두 가지 버전이 있습니다. 하나는 테스트 중에 사용되는 SyncTaskExecutor이고 다른 하나는 프로덕션 실행에 사용되는 AsyncTaskExecutor입니다.

    +0

    나는 당신의 솔루션을 완전히 이해하지 못했다고 생각한다. 나는 어떤 파일도 처리하지 않는다 : 처리 단계는 DB에서 무언가를 읽고 DB에 무언가를 다시 쓴다. 초기화 단계는 일부 값으로 DB를 초기화해야합니다. 이것은 DB 객체가 복잡하고 DAO를 사용하여 지속되어야하므로 별도의 SQL 스크립트 ("파일"이라고도 함)로 수행 할 수 없습니다. DAO 메소드가 메인 쓰레드에서 호출되기 때문에 변경 사항은 SUT에 표시되지 않습니다. –

    +0

    스프링 구성 파일에 대해 이야기하고있었습니다. 타스크 실행 프로그램 구성을 별도의 spring xml 파일로 이동할 수 있습니다. 이 xml 파일에는 두 개의 버전이 있습니다. 테스트에 사용되는 SyncTaskExecutor는 동일한 스레드에서 작업을 시작하므로 테스트에서 시작된 트랜잭션은 작업에도 전파됩니다. 다른 버전에는 실제 응용 프로그램 실행에 사용될 AsyncTaskExecutor가 포함됩니다. – gkamal

    +0

    네, 그게 가능합니다, 이것을 지적 해 주셔서 감사합니다. 프로덕션 컨텍스트/AsyncTaskExecutor를 사용하여 엔드 - 투 - 엔드 테스트를 수행하려는 통합 단계는 어떻게됩니까? –

    1

    별도의 구성을 원한다면 구성에 템플릿 정책을 적용하고 속성 파일에서 해당 값을 가져와 테스팅을위한 다양한 Spring 구성을 사용하지 않는 것이 좋습니다. 찌르다.

    하지만 동일한 정책 제작 사용을 사용하는 것이 가장 좋습니다. 고정 장치 데이터가 얼마나 방대하고 롤백에 의존 할 필요가 없도록 setUp() 단계가 데이터를 날려 버리고 다시 작성했는지 (데이터가 많은 경우 스냅 샷에서 가져온 것일 수 있음) 얼마나 나쁠까요?

    +0

    의견을 보내 주셔서 감사합니다. 테스트 데이터 (DB 상태)는 Unit Test 클래스의 각 테스트마다 달라야하므로 각 테스트의 시작 부분에 DB를 설정하고 커밋하고 싶습니다. 그러나 TransactionStatus를 전달할 방법을 찾지 못했습니다. 'PlatformTransactionManager # commit()'. 나는 하나의'setUp()'과 하나의'test()'메소드로 구성된 Unit Test 클래스를 가지고'setUp()'에서 어떻게 든 Hibernate 트랜잭션을 프로그래밍 방식으로 시작할 것이라고 생각한다. 나는 이미 그 추한 코드를 상상한다. –

    3

    귀하의 질문에 대한 진정한 해결책은 아니지만, 나는 새로운 트랜잭션을 작업자 스레드에서 수동으로 시작할 수 있음을 발견했습니다. 어떤 경우에는 이것이 충분할 수도 있습니다.

    출처 : Spring programmatic transactions.

    예 :

    @PersistenceContext 
    private EntityManager entityManager; 
    @Autowired 
    private PlatformTransactionManager txManager; 
    
    /* in a worker thread... */ 
    public void run() { 
        TransactionStatus tx = txManager.getTransaction(new DefaultTransactionDefinition()); 
        try { 
         entityManager.find(...) 
         ... 
         entityManager.flush(...) 
         etc... 
         txManager.commit(tx); 
        } catch (RuntimeException e) { 
         txManager.rollback(tx); 
        } 
    } 
    
    +0

    그렇게하면 'TransactionTemplate'을 사용하는 것이 좋습니다. –

    +1

    @rustyx이지만 다른 스레드에 대한 트랜잭션의 _propagation_이 아닙니다. 대신 새 스레드를 만듭니다. 작업자 스레드가 주 스레드에 의해 생성 된 트랜잭션에 참여하게하는 방법이 있습니까? – surlac

    관련 문제