Spring의 트랜잭션 관리TransactionManager(프로그래밍적 트랜잭션 관리)TransactionTemplate(프로그래밍적 트랜잭션 관리)@Transactional (선언적 트랜잭션 관리)Transaction PropagationREQUIREDREQUIRED_NEW가 CustomerService에 붙어있을 때SUPPORTSNOT_SUPPORTEDTransaction Isolation Level
Spring의 트랜잭션 관리
- 스프링 트랜잭션 기능은 전역 트랜잭션으로 동작하는 JTA(Java Transaction API)뿐만 아니라 JDBC API, 하이버네이트 트랜잭션 API, JPA 트랜잭션 API를 포함한 다양한 트랜잭션 API에 대한 추상화를 제공함
- 후자의 세 개 API는 로컬 트랜잭션과 함께 동작
전역 트랜잭션
은 일반적으로 관계형 데이터베이스와 메시지 큐(JMS)와 같은 다양한 트랜잭션 자원과 함께 동작할 수 있음
- JTA로 전역 트랜잭션을 관리하는 것은 애플리케이션 서버
- 반면에 예를 들어 JDBC 커넥션과 관련된 트랜잭션인 로컬 트랜잭션은
자원에 한정적임
. 로컬 트랜잭션은 다양한 자원들과 함께 동작할 수 없다
TransactionManager(프로그래밍적 트랜잭션 관리)

- 위와 같은 구조를 가지고 애플리케이션 계층에서는
PlatformTransactionManager
를 보고 활용함. 그 안에 구현체가 무엇이든지 상관없이(JDBC를 쓰던, Hibernate를 쓰던..)
private final PlatformTransactionManager transactionManager; public void testTransaction(Customer customer){ var transaction = transactionManager.getTransaction( new DefaultTransactionDefinition()); try{ jdbcTemplate.update("UPDATE customers SET name = :name WHERE customer_id = UUID_TO_BIN(:customerId);", Map.of("name", customer.getName(), "customerId", customer.getCustomerId().toString())); jdbcTemplate.update("UPDATE customers SET email = :email WHERE customer_id = UUID_TO_BIN(:customerId);", Map.of("email", customer.getEmail(), "customerId", customer.getCustomerId().toString())); transactionManager.commit(transaction); }catch (DataAccessException e){ logger.error(e.getMessage()); transactionManager.rollback(transaction); } }
- 이와 같이 transaction을 받아와서 commit, rollback을 일일히 진행해주어야 함 → Template화 진행
TransactionTemplate(프로그래밍적 트랜잭션 관리)
transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { jdbcTemplate.update("UPDATE customers SET email = :email WHERE customer_id = UUID_TO_BIN(:customerId);", Map.of("email", customer.getEmail(), "customerId", customer.getCustomerId().toString())); jdbcTemplate.update("UPDATE customers SET name = :name WHERE customer_id = UUID_TO_BIN(:customerId);", Map.of("name", customer.getName(), "customerId", customer.getCustomerId().toString())); } });
@Transactional (선언적 트랜잭션 관리)
[ 우아한형제들 기술블로그 ] CheckedException과 UncheckedException 에 대한 default rollback 전략
- 만약에 Repository의 메서드에 @Transactional 을 붙이면 이 Repository가 bean으로 만들어 질 때 spring에서 proxy객체로 만들어줌(Spring AOP 프레임워크 기반). 그리고 이것을 쓰는 곳에서 Repository의 proxy객체를 이용하게 됨
- 주로 service단에 @Transactional 을 많이 붙임. 왜냐하면 repository의 insert, update, delete등이 한 군데 모이는 곳이고 걔네들을 한번에 처리하려고 하기 때문
- 해당 어노테이션을 사용하려면
@EnableTransactionManagement
을 달아주어야 함(스프링 부트에서는 필요 없다). @Configuration같은 곳에
- [ default 설정 ] checkedException에 대해서는 rollback 안하고, uncheckedException은 rollback 한다.
- checked는 알고 있는 에러가 터지는 거라 rollback 안하고, unchecked는 예상 못한 에러라 rollback 하는 것.. (default 전략은 그렇다)
Transaction Propagation
- @Transactional 붙은 메서드 안에 또 다른 @Transactional이 붙은 메서드가 있을 때 적용됨



REQUIRED

REQUIRED_NEW가 CustomerService에 붙어있을 때

SUPPORTS
- 트랜잭션이 있는 곳에서 트랜잭션(support)를 부르면 해당 트랜잭션 사용하고, 바깥에 부른 곳에서 트랜잭션이 없으면 트랜잭션 사용 안하고.
NOT_SUPPORTED

Transaction Isolation Level

- READ_UNCOMMITED : 트랜잭션에서 처리 중인 커밋되지 않은 데이터를 다른 트랜젝션에서 읽는 것을 허용하는 것
- READ_COMMITED : 트랜잭션이 커밋이 되어서 확정된 데이터만 읽도록
- REPEATABLE_READ : 먼저 발생한 트랜잭션이 읽은 데이터는 다른 트랜잭션에 업데이트하거나 삭제하는 것을 방지 해줌. 같은 데이터를 한 트랜잭션 내에서 여러번 쿼리 했을 때 일관성이 있는 것
- SERIALIZABLE : 추가되는 것도 막아줌. 이전 것은 update, delete를 방지하고, 이 단계에서는 추가되는 것 까지 막아주는 것
- @Transactional의 Isolation.DEFAULT 는 사용하는 DB의 설정을 따르겠다는 의미임
- dirty read : 커밋되지 않은 수정 중인 데이터를 다른 트랜잭션에서 읽을 수 있을 때 발생함
- non-repeatable reads : 한 트랜 잭션 내에서 같은 쿼리를 실행할 때, 일관되지 않은 결과가 발생하는 것
- phantom reads : 한 트랜잭션 안에서 일정범위의 쿼리를 읽어올 때, 첫 번째 쿼리에는 없던 데이터가 두 번째 쿼리에는 생기는 현상(유령 레코드가 생기는 것)