You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
이 경우 external()은 트랜잭션이 없지만, internal()에서는 트랜잭션이 적용되는 것 처럼 보임
실행 로그 - externalCall()
실행 로그를 보면 트랜잭션 관련 코드가 보이지 ㅇ낳음
프록시가 아닌 실제 callService에서 남긴 로그만 확인 가능, 추가로 internal() 내부에서 호출한 tx activce=false 로그를 통해 확실히 트랜잭션이 수행되지 않은 것을 확인 가능
우리의 기대와 다르게 internal()에서 트랜잭션이 전혀 적용되지 않음, 왜 이런 문제가 발생할까??
프록시의 내부 호출
클라이언트인 테스트 코드는 callService.external() 호출, 여기서 callService는 트랜잭션 프록시
callService의 트랜잭션 프록시가 호출
external() 메서드에는 @transactional이 없음, 따라서 트랜잭션 프록시는 트랜잭션을 저용하지 않음
트랜잭션 적용하지 않고, 실제 callService 객체 인스턴스의 external()을 호출
external()은 내부에서 internal() 메서드 호출 여기서 문제 발생
문제 원인
자바 언어에서 메서드 앞에 별도의 참조가 없으면 this라는 뜻으로 자기 자신의 인스턴스를 가리킴, 결과적으로 자기 자신의 내부 메서드를 호출하는 this.internal()이 되는데, 여기서 this는 자기 자신을 가리키므로, 실제 대상 객체(target)의 인스턴스 뜻함
결과적으로 이러한 내부 호출은 프록시를 거치지 않음, 따라서 트랜잭션을 적용할 수 없음, 즉 target에 있는 internal()을 직접 호출 함
프록시 방식의 AOP 한계
@transactional를 사용하는 트랜잭션 AOP는 프록시를 사용, 프록시를 사용하면 메서드 내부 호출에 프록시를 적용할 수 없음
이 문제를 해결할 가장 간단한 방법 internal() 메서드를 별도의 클래스로 분리
트랜잭션 AOP 주의 사항 - 프록시 내부 호출2
메서드 내부 호출 떄문에 트랜잭션 프록시가 적용되지 않는 문제를 해결하기 위해 internal() 메서드를 변도의 클래스로 분리
참고로 애노테이션에서 속성이 하나인 경우 위 예처럼 value는 생량하고 값을 넣을 수 있음
rollbackFor
예외 발생시 스프링 트랜잭션의 기본 정책은 다음과 같음
언체크 에외인 RuntimeException, Error 외 그 하위 예외가 발생하면 롤백
체크 예외인 Exception과 그 하위 예외들은 커밋함
아래 옵션을 지정하면 기본 정책에 추가로 어떤 예외가 발생할 때 롤백할 지 지정할 수 있음
3-1. @Transactional(rollbackFor = Exception.class) 이렇게 지정하면 체크 예외인 Exception이 발생해도 롤백함(하위 예외들도 대상에 포함)
3-2. rollbackForClassName 도 있는데, rollbackFor 는 예외 클래스를 직접 지정, rollbackForClassName 는 예외 이름을 문자로 넣으면 됨
noRollbackFor
위에 설명한 rollbackFor와 반대, 기본 정책에 추가로 어떤 예외가 발생했을 때 롤백하면 안 되는지 지정할 수 있음
예외 이름을 문자로 넣을 수 있는 noRollbackForClassName 있음
isolation
트랜잭션 격리 수준을 지정할 수 있음, 기본 값은 디비에 설정한 트랜잭션 격리 수준을 사용하는 DEFAULT 임
대부분 디비 설정할 기준을 따름
애플리케이션 개발자가 트랜잭션 격리 수준을 직접 지정하는 경우는 드뭄
DEFAULT : 디비에서 설정한 격리 수준을 따름
READ_UNCOMMITTED : 커밋되지 않은 읽기
READ_COMMITED : 커밋된 읽기
REPEATABLE_READ : 반복 가능한 읽기
SERIALIZABLE : 직렬화 가능
timeout
트랜잭션 수행 시간에 대한 타임아웃을 초 단위로 지정, 기본 값은 트랜잭션 시스템의 타임아웃을 사용
운영 환경에 따라 동작하는 경우도 있고, 그렇지 않은 경우도 있기 때문에 꼭 확인하고 사용해야 함
timeoutString도 있는데, 숫자 대신 문자 값으로 지정할 수 있음
label
트랜잭션 애노테이션에 있는 값을 직접 읽어서 어떤 동작을 하고 싶을 때 사용할 수 있음, 일반적으로 사용하지 않음
readOnly
트랜잭션은 기본적으로 읽기 쓰기 모두 가능한 트랜잭션이 생성
readOnly = true 옵션을 사용하면 읽기 전용 트랜잭션 생성, . 이 경우 등록, 수정, 삭제가 안되고 읽기 기능만
작동 (드라이버나 데이터베이스에 따라 정상 동작하지 않는 경우도 있음) 그리고 readOnlyy 옵션을 사용하면 읽기에서 다양한 성능 최적화가 발생할 수 있음
readOnly 옵션은 크게 3곳에서 적용
1. 프레임워크
JdbcTemplate은 읽기 전용 트랜잭션 안에서 변경 기능을 실행하면 예외를 던짐
JPA(하이버네이트)는 읽기 전용 트랜잭션의 경우 커밋 시점에 플러시를 호출하지 않음
읽기 전용이니 변경에 사용되는 플러시 호출할 필요 없음
추가로 변경이 필요 없으니 변경 감지를 위한 스냅샷 객체도 생성하지 않음
2. JDBC 드라이버
읽기 전용 트랜잭션에서 변경 쿼리가 발생하면 예외를 던짐
읽기, 쓰기(마스터, 슬레이브) 이비를 구분해서 요청함, 읽기 전용 트랜잭션의 경우 읽기(슬레이브)디비의 커넥션을 획득해서 사용
3. 데이터베이스
데이터베이스에 따라 읽기 전용 트랜잭션의 경우 읽기만 하면 됨, 내부에서 성능 최적화가 발생함
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
인터페이스에 @transactional 적용
인터페이스에도 @transactional을 적용 가능, 아래와 같은 순서대로 적용
구체적인 것이 더 높은 우선순위를 가짐
1, 클래스의 메서드(우선순위 가장 높음)
클래스의 메서드를 찾고, 만약 없으면 클래스의 타입을 찾고 만약 없으면 인터페이스의 메서드를 찾고 그래도 없으면 인터페이스의 타입을 찾음
인터페이스에 @transactional 사용하는 것은 스프링 공식 메뉴얼에서 권장하지 않음
AOP를 사용하는 방식에 따라서 인터페이스에 에노테이션을 두면 AOP가 적용이 되지 않는 경우도 있기 때문
구체 클래스에 @transactional을 사용 필요
트랜잭션 AOP 주의 사항 - 프록시 내부 호출1
CallService
@Transactional이 하나라도 있으면 트랜잭션 프록시 객체가 만들어짐, callService 빈을 주입 받으면 트랜잭션 프록시 객체가 대신 주입 됨internalCall() 실행
internal()
실행 로그 - internalCall()
externalCall() 실행
external()
실행 로그 - externalCall()
프록시의 내부 호출
문제 원인
프록시 방식의 AOP 한계
트랜잭션 AOP 주의 사항 - 프록시 내부 호출2
실제 호출되는 흐름 분석
실행 로그 - externalCallV2()
public 메서드만 트랜잭션 적용
스프링이 public에만 트랜잭션을 적용하는 이유
트랜잭션 AOP 주의 사항 - 초기화 시점
initV1() 관련 로그
Hello init @PostConstruct tx active=falseinit2() ApplicationReadyEvent 이벤트가 호출하는 코드
트랜잭션 옵션 소개 - 추가 기능
@transactional - 코드
rollbackFor
@Transactional(rollbackFor = Exception.class)이렇게 지정하면 체크 예외인 Exception이 발생해도 롤백함(하위 예외들도 대상에 포함)noRollbackFor
isolation
트랜잭션 격리 수준을 지정할 수 있음, 기본 값은 디비에 설정한 트랜잭션 격리 수준을 사용하는 DEFAULT 임
대부분 디비 설정할 기준을 따름
애플리케이션 개발자가 트랜잭션 격리 수준을 직접 지정하는 경우는 드뭄
DEFAULT : 디비에서 설정한 격리 수준을 따름
READ_UNCOMMITTED : 커밋되지 않은 읽기
READ_COMMITED : 커밋된 읽기
REPEATABLE_READ : 반복 가능한 읽기
SERIALIZABLE : 직렬화 가능
timeout
label
readOnly
작동 (드라이버나 데이터베이스에 따라 정상 동작하지 않는 경우도 있음) 그리고 readOnlyy 옵션을 사용하면 읽기에서 다양한 성능 최적화가 발생할 수 있음
readOnly 옵션은 크게 3곳에서 적용
1. 프레임워크
2. JDBC 드라이버
3. 데이터베이스
All reactions