드디어... 며칠만에... 좀 편한 강의에 도달했다... 이번 강의 시리즈 자체는 좀 깁니다. 그런데 앞에 두 개만 봐서 양이 좀 적나보네요.
"트랜젝션 매니저" 강의 시리즈에서는 이전부터 설명은 않고 계속 써오던 @Transactional 과 트랜젝션, 그리고 그걸 관리하는 트랜젝션 매니저에 대해서 다룹니다. 강의 하나가 길어서 그냥 n조각된 거 같아보이네요. 그래서 두 개 봤습니다.
트랜젝션
- 트랜젝션은 DB의 개념입니다. 아시다시피요.
- @Transactional 이 있는 범위가 트랜젝션의 시작과 끝입니다. 중첩되면 어떻게 되는지도 이미 아시죠?
- 트랜젝션은 트랜젝션 매니저가 관리해주는 모양입니다. 아직 강의에서 실체가 나오지는 않았습니다.
트랜젝션의 특징
ACID
오랜만이네요.
https://ko.wikipedia.org/wiki/ACID
- 원자성 - atomicity: 모 아니면 도입니다. 전부 반영하던지 전부 반영하지 않던지.
- 일관성 - consistency: 일관성에 해당하는 룰이 지켜졌는지. f.key 같은거나 A+B = 100원 같은 커스텀 룰... 커스텀 룰이 되나 잠깐 검색해봤는데 DB에서 제공하는 기능이 있나보네요.
- 독립성 - isolation: 다른 트랜젝션이 끼어들어서 롤백이 불가능해진다던가 하는 일이 있으면 안 됩니다. A 트랜젝션을 돌리는 도중에는 잔액이 충분히 남아있었지만, A가 도는 사이에 B 트랜젝션이 통장 잔고를 음수로 만들었다: 이러면 롤백할 수가 없죠.
- 지속성 - durability: 성공적인 트랜젝션은 영원히 반영. 로그 같은 걸로 시스템이 터지더라도 복구가 가능해야한다네요.
JPA의 트랜젝션은 @Transactional 로
예상하셨겠지만 지금까지 써 온 @Transactional 으로 관리합니다.
두 가지 @Transactional
근데 이게 또 두 가지가 있습니다.
- Spring 네임스페이스의 @Transactional
- JPA / Jakarta 네임스페이스의 @Transactional
스프링 쪽이 좀 더 기능이 많은 모양입니다. 뭘 쓰던 상관은 없지만 스프링걸 쓰면 다른 컨테이너랑 호환이 안 될 거라고... 아무렴머어때요
강의에서는 Spring 쪽의 어노테이션을 사용합니다.
DB로 직접 확인하는 @Transactional 의 효과
: all or nothing (atomicity)
이번 강의 두 개에서는 트랜젝션이 실제 DB에 반영되는지 보기 위해서 mysql 클라이언트로 접속해서 SQL을 입력해보는 걸로 확인을 진행합니다. 저야 그냥 IDE에서 새로고침해서 확인했구요...
현재 DDL 관련 설정이 create-drop 으로 설정되어 있기에 디버그 브레이크포인트를 잡아서 확인해야합니다. 마치 디버그를 처음 다루는 어린양같이 설명해주실 필요는 없었는데... 아무튼,
예상하신 대로 동작합니다. @Transactional 을 벗어나지 않으면 DB에 반영되지 않습니다. 심지어 flush() 를 한 뒤어도 @Transactional 을 벗어나지 못했다면 외부 도구로 DB에 직접 쿼리해서 확인해보면 반영되지 않았음을 알 수 있습니다. 1차 캐시가 아니라 DB상의 트랜젝션에 막힌 거죠.
@Service
@RequiredArgsConstructor
public class BookService {
private final BookRepo bookRepo;
private final AuthorRepo authorRepo;
@Transactional
public void putBookAndAuthor() throws Exception {
Book book = new Book();
book.setName("JPA 시ㅏㅈㄱ하기");
bookRepo.save(book);
Author author = new Author();
author.setName("marting");
authorRepo.save(author);
System.out.println(bookRepo.findAll());
bookRepo.flush();
// flush 를 해도 반영되지 않습니다. DB상의 트랜젝션이 아직 안 끝났기 때문입니다.
} // 함수가 끝나야 반영되겠죠.
}
@SpringBootTest
class BookServiceTest {
생략
@Test
void transactionTest() {
try {
bookService.putBookAndAuthor();
} catch (Exception e) {
System.out.println(e);
}
System.out.println("books: " + bookRepo.findAll());
System.out.println("autho: " + authorRepo.findAll());
}
}
예외: checked exception 은 트랜젝션이 반영되니 주의!
현업에서 실수하는 케이스로 checked exception 을 발생시키는 경우를 보여주셨습니다. checked exception 이 발생되면, 개발자가 직접 예외를 처리하였을 것으로 가정하므로 자동으로 트랜젝션이 취소되고 롤백되지 않습니다.
반면, RuntimeException 을 상속받아서 메서드에 throws 를 표기할 필요가 없는 예외는 알아서 롤백이 됩니다.
@Transactional
public void putBookAndAuthorException() throws Exception {
Book book = new Book();
book.setName("JPA 시ㅏㅈㄱ하기");
bookRepo.save(book);
bookRepo.flush();
// checked exception - 롤백이 안 됩니다.
throw new Exception("asdf");
}
@Transactional
public void putBookAndAuthorRuntimeException() {
Book book = new Book();
book.setName("JPA 시ㅏㅈㄱ하기");
bookRepo.save(book);
bookRepo.flush();
// unchecked exception - 트랜젝션이 취소되고 이전 상태로 롤백됩니다.
throw new RuntimeException("asdf");
}
Checked Exception? Uncheced Exception?
어... 그래서 checked exception 이라고 하셨나요? 저는 처음 들어보는데요? 제가 학교에서 자바를 (검열됨)으로 배웠나보네요.
공식은 뭐라고 말할까요?
https://docs.oracle.com/javase/specs/jls/se7/html/jls-11.html#jls-11.1.1
아래는 말로 설명된 상속구조를 트리로 표현한 것입니다.
- Throwable : 모든 throw 가능한 것의 조상
- Error: 보통 복구가 불가능 (unchecked)
- Exception: 보통 프로그램들이 복구하길 원할 거 같은 오류들
- RuntimeException: 여러가지 이유로 발생할 수는 있지만 아직은 복구가 가능한 것들 (unchecked)
- 그 외 Exception 을 상속받는 것들
unchecked 가 아닌 예외 클래스는 모두 checked 이며, checked의 목적은 컴파일 타임 체킹, 즉 컴파일 시점에 오류가 발생할 여지가 있는지 확인하기 위해서입니다. 또한 API contract의 일부가 되기도 하죠.
컴파일 타임 체킹에 관해서:
https://docs.oracle.com/javase/specs/jls/se7/html/jls-11.html#jls-11.2
그럼 컴파일 타임에 확인을 해주는 checked exception 이 더 좋은 거 아닐까요? 왜 runtime exception 만 롤백을 해줄까요? checked 이면서 롤백을 해주는 방법은 없을까요? 설정은?
@Transactional은 "Declarative transaction management" 다
https://stackoverflow.com/a/44899098
https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch11s05.html
설정가능하지 않을까? 해서 찾다보게 되면 금방 스프링 문서에 도달하게 됩니다.
메서드에 @Transactional 을 정의하는 거 자체가 선언적 정의입니다. 그리고 문서를 보니... 이거 AOP 로 중간에 메소드 콜을 낚아채는 방식으로 되어있군요.
S/O를 보니 설정도 가능한 거 같은데... 뭐 필요하다면 다음 강의 어딘가에서 나오겠죠?
본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스: https://bit.ly/37BpXiC
#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는JavaSpring웹개발마스터초격차패키지Online
아 학교에서 자바 (검열됨)으로 배워서 30분짜리 학습이 1시간 반까지 늘었자나
어째 매번 꿀빨자는 목표가 허공으로 사라지고 저는 열심히 검색을 해보고 있는걸까요...
'FastCampus - 한번에 끝내는 Java|Spring 웹 개발 > 04 JPA' 카테고리의 다른 글
JPA Ch 7 영속성 - 트랜젝션 매니저 (5) - 패스트캠퍼스 챌린지 34일차 (0) | 2022.02.26 |
---|---|
JPA Ch 7 영속성 - 트랜젝션 매니저 (3~4) - 패스트캠퍼스 챌린지 33일차 (0) | 2022.02.25 |
JPA Ch 7 영속성 - 생명주기 - 패스트캠퍼스 챌린지 31일차 (0) | 2022.02.23 |
JPA Ch 7 영속성 - 엔티티 캐시 (1차 캐시) - 패스트캠퍼스 챌린지 30일차 (0) | 2022.02.22 |
JPA Ch 7 영속성 - 개요 & 리얼 DB - 패스트캠퍼스 챌린지 29일차 (0) | 2022.02.21 |