Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

Transaction Manager - 5 : Propagation 본문

JPA/영속성

Transaction Manager - 5 : Propagation

Seung__ 2021. 7. 6. 20:50

현재 Transaction과 다른 class의 transaction 간에 처리가 어떻게 되어야 할까? 교통정리 어떻게 할지 정의.

 

REQUIRED default 조건
기존에 사용하는 transaction이 있다면 그 transaction사용하고 없다면 새로운 transaction생성
ex) .save() : 상위에 @Transactional 없으면 각 save()가 하나의 transaction
즉, 상위 transaction과 완전히 동일한 transaction
REQUIRES_NEW 무조건 새로운 Transaction 생성,
호출하는 쪽의 rollback & commit에 관계 없이 자체적으로 commit & rollback 진행
SUPPORTS 호출하는 쪽에 Transaction이 있다면 사용한다.
하지만 Transaction이 없으면 Transaction없이 진행한다.
NOT_SUPPORTED Transaction 없이 진행한다.
만약 상위에 Transaction이 존재한다면 해당 method가 종료될 때 까지 정지시킨다.
NESTED 호출에서 생성하는 transaction(#1)을  그대로 활용하나 @Transactional(#2)에서 예외가 발생하면 
Transaction#1 전체가 rollback하는 것이 아닌 해당 method만 rollback.,
savePoint까지의 성공은 보장한다!
MANDATORY 필수적으로 transaction이 존재해야 한다. 
무조건 상위에 Transaction이 존재해야 한다. 없으면 오류 발생
NEVER Transaction이 없어야 한다.
상위에 Transaction이 존재한다면 오류 발생

 

 

1. REQUIRED


 

기존에 사용하는 transaction이 있다면 그 transaction사용하고 없다면 새로운 transaction생성
ex) .save() : 상위에 @Transactional 없으면 각 save()가 하나의 transaction

 

<AuthorService> putAuthor에 propagation = Required 설정

@Service
@RequiredArgsConstructor
public class AuthorService {

    private final AuthorRepository authorRepository;

    @Transactional(propagation = Propagation.REQUIRED)
    public void putAuthor(){

        Author author = new Author();
        author.setName("Hyun");

        authorRepository.save(author);
    }


}

<BookService> putBookAndAuthor에 propagation = Required 설정

@Service
@RequiredArgsConstructor
public class BookService {

    private final BookRepository bookRepository;

    private final AuthorRepository authorRepository;

    private final EntityManager entityManager;

    private final AuthorService authorService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void putBookAndAuthor(){

        Book book = new Book();
        book.setName("JPA 시작작");
        bookRepository.save(book); //DB insert

        authorService.putAuthor();
        
//        Author author = new Author();
//        author.setName("hyun");
//        authorRepository.save(author); // DB insert
//
//        throw new RuntimeException("오류 발생해서 DB commit 발생하지 않음.");

    }

  


}

 

실행결과 book과 author가 commit이 되었다.

 

books : [Book(super=BaseEntity(createdAt=2021-07-05T23:35:51.142530, updatedAt=2021-07-05T23:35:51.142530), 
id=1, name=JPA 시작작, authorId=null, category=null)]

Authors : [Author(super=BaseEntity(createdAt=2021-07-05T23:35:51.208594, updatedAt=2021-07-05T23:35:51.208594), 
Id=1, name=Hyun, country=null)]

 

중간에 오류가 발생하면 어떻게 될까?

@Transactional(propagation = Propagation.REQUIRED)
    public void putBookAndAuthor(){

        Book book = new Book();
        book.setName("JPA 시작작");
        bookRepository.save(book); //DB insert

        authorService.putAuthor();
        
//        Author author = new Author();
//        author.setName("hyun");
//        authorRepository.save(author); // DB insert
//
        throw new RuntimeException("오류가 발생했드아. transaction은 어찌될까");

    }

 

<Test>기존에 작성한 transactionTest를 이용

 @Test
    void transactionTest(){

        try{
            bookService.putBookAndAuthor();

        } catch (RuntimeException e){
            System.out.println(">>>>" + e.getMessage());
        }

        System.out.println("books : " + bookRepository.findAll());
        System.out.println("Authors : " + authorRepository.findAll());
    }

 

실행결과  putBookAndAuthor에서 error가 발생하여 둘 다 rollback이 되었다.

 

>>>>오류가 발생했드아. transaction은 어찌될까
Hibernate: 
    select
        book0_.id as id1_1_,
        book0_.created_at as created_2_1_,
        book0_.updated_at as updated_3_1_,
        book0_.author_id as author_i4_1_,
        book0_.category as category5_1_,
        book0_.name as name6_1_,
        book0_.publisher_id as publishe7_1_ 
    from
        book book0_
books : []
Hibernate: 
    select
        author0_.id as id1_0_,
        author0_.created_at as created_2_0_,
        author0_.updated_at as updated_3_0_,
        author0_.country as country4_0_,
        author0_.name as name5_0_ 
    from
        author author0_
Authors : []

 

만약 authorService class의 method에서 error가 발생한다면???

@Service
@RequiredArgsConstructor
public class AuthorService {

    private final AuthorRepository authorRepository;

    @Transactional(propagation = Propagation.REQUIRED)
    public void putAuthor(){

        Author author = new Author();
        author.setName("Hyun");

        authorRepository.save(author);

        throw new RuntimeException("오류가 발생했드아. transaction은 어찌될까");
    }


}

<BookService>

@Transactional(propagation = Propagation.REQUIRED)
    public void putBookAndAuthor(){

        Book book = new Book();
        book.setName("JPA 시작작");
        bookRepository.save(book); //DB insert

        try{ //try catch로 묶지 않으면 exception이 putBookAndAuthor로 전파됨..오류가 전파되지 않도록
            authorService.putAuthor();
        }catch (RuntimeException e){

        }


    }

putBookAndAuthor()는 정상적으로 실행은 되었으나, 예외 발생으로 rollback되었다.

 

Transaction이 Propagation.REQUIRED이다. 

 

따라서 #1 Transaction이 먼저 존재하기 때문에 putBookAndAuthor()가 하나의 transaction으로 묶인다.

 

그러므로 putBookAndAuthor()의 내부 method인 putAuthor에 @Transactional이 적용되어있어도, 

해당 method만 rollback이되는 것이 아니라 transaction(#1) 전체적으로 rollback이 일어난다.

 

>>>>Transaction silently rolled back because it has been marked as rollback-only
Hibernate: 
    select
        book0_.id as id1_1_,
        book0_.created_at as created_2_1_,
        book0_.updated_at as updated_3_1_,
        book0_.author_id as author_i4_1_,
        book0_.category as category5_1_,
        book0_.name as name6_1_,
        book0_.publisher_id as publishe7_1_ 
    from
        book book0_
books : []
Hibernate: 
    select
        author0_.id as id1_0_,
        author0_.created_at as created_2_0_,
        author0_.updated_at as updated_3_0_,
        author0_.country as country4_0_,
        author0_.name as name5_0_ 
    from
        author author0_
Authors : []

 

즉 정리하면 REQUIRED의 경우 먼저 만들어진 Transaction을 같이 사용한다.

 

따라서 하위에 별도로 @Transactional이 적용되도 먼저 만들어진 transaction기준으로 commit, rollback이 적용된다.

 

 

 

2.  REQUIRES_NEW


<BookService >  : Propagation.REQUIRES_NEW

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void putBookAndAuthor(){

        Book book = new Book();
        book.setName("JPA 시작작");
        bookRepository.save(book); //DB insert

        try{ //try catch로 묶지 않으면 exception이 putBookAndAuthor로 전파됨..오류가 전파되지 않도록
            authorService.putAuthor();
        }catch (RuntimeException e){

        }


    }

 

<AuthorService> : Propagation.REQUIRES_NEW

@Service
@RequiredArgsConstructor
public class AuthorService {

    private final AuthorRepository authorRepository;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void putAuthor(){

        Author author = new Author();
        author.setName("Hyun");

        authorRepository.save(author);

        throw new RuntimeException("오류가 발생했드아. transaction은 어찌될까");
    }


}

 

실행 결과를 보면 book은 조회가 되었지만, author는 rollback 되었다.

 

Transaction #1과 Transaction #2가 별개 transaction으로 #2에서 예외가 발생했기 때문에 #2만 rollback이 되었다.

(#2에서 발생한 예외는 #1에서 catch를 통해 처리)

books : [Book(super=BaseEntity(createdAt=2021-07-06T19:56:12.597686, updatedAt=2021-07-06T19:56:12.597686), id=1, name=JPA 시작작, authorId=null, category=null)]

Authors : []

 

반대로 BookService에서 예외를 발생시켜보자.

 

Book은 rollback, author는 정상적으로 진행되었다.

 

즉, REQUIRES_NEW를 적용하면 무조건 transaction을 새로 생성하고 commit/rollback은 별도로 진행된다.

 

books : []

Authors : [Author(super=BaseEntity(createdAt=2021-07-06T20:03:08.643824, updatedAt=2021-07-06T20:03:08.643824), Id=1, name=Hyun, country=null)]

 

 

3. NESTED


@Transactional 적용된 method를 호출한 쪽에 Transaction이 존재하면 같이 사용한다. 

 

REQUIRED와 비슷하지만, NESTED적용된 method에서 예외 발생 시 Transaction전체가 rollback되는 것이 아니라

 

해당 method만 rollback된다. 

 

이 경우에는 author는 rollback되지만 book은 commit이 진행된다. 

 

하지만! book이 rollback이 일어난다면 같은 Transaction을 사용하는 author(NESTED)도 같이 rollback 된다.

 

일종의 집합 개념으로 보면 이해가 될 것 같다. 

 

Method2가 rollback이 일어나도 전체 transaction(method1)은 rollback이 일어나지 않지만,

transaction(method1)에서 rollback이 일어나는 경우는 method2도 같이 rollback이 발생한다.

 

 

Requires_New처럼 Transaction을 완전히 분리해서 사용하는 것이 아니기 때문에,

Save Point라는 개념이 사용된다. 즉 savePoint까지 성공은 보장한다.

 

JpaTransactionManager의 getSavePointManager()를 보자.

private SavepointManager getSavepointManager() {
  if (!isSavepointAllowed()) {
  throw new NestedTransactionNotSupportedException(
  "Transaction manager does not allow nested transactions");
  }
  SavepointManager savepointManager = getEntityManagerHolder().getSavepointManager();
  if (savepointManager == null) {
  throw new NestedTransactionNotSupportedException(
  "JpaDialect does not support savepoints - check your JPA provider's capabilities");
  }
  return savepointManager;
  }

JpaTransactionManager는 기본적으로 지원하지 않는다. 물론 설정하면 사용은 가능하다.

의도한 대로 동작하지 않는 경우가 많다..

 

 

 

4. SUPPORT


호출하는 쪽에 Transaction이 있다면 사용한다.

 

하지만 Transaction이 없으면 Transaction없이 진행한다.

 

 

5.NOT_SUPPORTED


Transaction 없이 진행한다.

 

만약 상위에 Transaction이 존재한다면 해당 method가 종료될 때 까지 정지시킨다.

 

 

6. Mandatory


필수적으로 transaction이 존재해야 한다. 

Required의 경우 상위에 Transaction이 존재하면 해당 transaction사용하고  없으면 생성했다.

 

하지만 Mandatory는 무조건 상위에 Transaction이 존재해야 한다. 없으면 오류 발생

 

 

 

7.Never


Transaction이 없어야 한다.

 

상위에 Transaction이 존재한다면 오류 발생

 

 

8. method가 아닌 class에 @Transactiona 적용하면?


method : method의 시작과 끝 관리

class : 각 methods에 대한 설정을 @Transactional로 하겠다

우선순위 :  method > class

 

9. GitHub : 210706 Propagation 


 

 

bsh6463/BookManager

Contribute to bsh6463/BookManager development by creating an account on GitHub.

github.com

 

Comments