일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- springdatajpa
- JPQL
- jpa
- 그리디
- SpringBoot
- Thymeleaf
- spring
- java
- kotlin
- Spring Boot
- 스프링 핵심 기능
- Android
- QueryDSL
- Servlet
- 스프링 핵심 원리
- AOP
- 백준
- 자바
- pointcut
- Greedy
- 인프런
- db
- Exception
- http
- 스프링
- 김영한
- Proxy
- 알고리즘
- transaction
- JDBC
- Today
- Total
개발자되기 프로젝트
Transaction Manager - 5 : Propagation 본문
현재 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
'JPA > 영속성' 카테고리의 다른 글
@Transactional을 Interface에 적용한 경우 (0) | 2021.07.10 |
---|---|
Cascade Remove, Orphan Removal, Soft Delete, @Where (0) | 2021.07.08 |
Transaction Manager - 4 : Isolation (0) | 2021.07.05 |
Transaction Manager-3 : Isolation (0) | 2021.07.04 |
Transaction Manager - 2 (0) | 2021.07.04 |