[JPA] 트랜잭션
*트랜잭션의 특징 : acid
원자성 : 전체 성공 or 전체 실패
일관성 : 데이터간의 정확성
독립셩 : 다른 트랜잭션이 중간 연산결과에 접근불가
영속성 : 상태가 영구적으로 db에 저장
* ddl-auto를 create-drop하면 테스트 실행동안 테이블이 생기다가 끝나면 삭제됨
(>>디버그 브레이크 포인트를 통해 테이블 확인할 수 있음)
* 테이블에 데이터 추가하는 메서드를 @Transactional로 묶으면 메서드 중간에는 데이터가 추가되지 않고 메서드가 끝나고 한번에 처리됨
ㄴ @Transactional하면 java에서 제공하는 것과 스프링에서 제공하는 것 두가지가 있다. (여기선 스프링걸 써봄)
* 메서드가 끝나기 직전에 Exception을 발생하면 어떻게 될까?
1) RuntimeException : rollback되어 연산들이 모두 취소, db에 반영되지 않는다.
2) (Checked) Exception : 그래도 커밋이 된다. (대신 에러 처리 catch문이 필수이다)
(*참고: new Exception(“msg”) : msg가 메시지가 되어 e.getMessage()하면 msg가 출력
ㄴ @Transactional의 속성으로 rollbackFor이라는 것이 있는데 이 속성의 값으로 Exception.class를 취해주면 2)Checked Exception이라 해도 rollback이 되어 커밋되지 않는다.
* A라는 메서드에서 @Transactional인 메서드를 호출하는데, 이 A라는 메서드를 호출하는 방식으로 메서드를 실행하면 어떻게 될까?
=> @Transactional은 무시됨.
ㄴ @Transactional 메서드가 public이 아닐경우 이런 실수를 많이 하므로 조심하길 바람
참고) 호출되려는 메서드에서 에러가 던져지고 이걸 try-catch로 처리하지 않았다면(throw방식 포함) 이 메서드를 호출할 수 없다.
*isolation
0) DEFAULT : 사용하는 db설정에 따름
1) READ_UNCOMMITTED : 커밋되지 않아도 읽어온다.
2) READ_COMMITTED : 커밋된 것만 읽어온다.
3) REPEATABLE_READ : 커밋 여부와 상관없이 각 트랜잭션의 흐름은 방해받지 않는다.
4) SERIALIZABLE : 커밋이 되지 않은 트랜잭션이 있으면 다른 트랜잭션이 기다림
=> 밑으로 갈수록 강한 격리성, 데이터의 정합성 보낭 .but 수행성능 떨어짐
1) READ_UNCOMMITTED
ㄴ 커밋되지 않은 걸 읽어오는 것을 “더티리드”라고 한다
ㄴ 만약에 조작하고 -> 다른거 update 후 저장 -> 조작 rollback 해도, update된 결과에는 조작이 rollback되지 않는다. => jpa에서 update방식의 default가 변경필드 포함 모든 필드를 업데이트 하기 때문
ㄴ 조작을 rollback해도 이미 저장된건 rollback이 안됨.
ㄴ update 된 것만 변경하려면 @DynamicUpdate를 엔티티에 달아주면 된다.
2) READ_COMMITTED
ㄴ 한 트랜잭션 중간에 다른 트랜잭션이 변경하고 커밋해도 그 후에 findId를 하면 변경된 값을 가져오진 않는다. 1차캐쉬 때문이다. => manager.clear로 해결할 수 있다.
ㄴ 하지만 다른 트랜잭션에서 변경하고 커밋을 하면 한 트랜잭션 내의 흐름이 깨지는건 마찬가지이다. => unrepeatable read
3) REPEATABLE_READ (mysql)
ㄴ 초기의 db상태를 스냅샷으로 찍어둬서 한 트랜잭션에서는 이것만 계속 사용
ㄴ 하지만 phantom read : 아직 안들어온 데이터도 미리 변경을 함.
ㄴ (ex. ‘b 트랜잭션에서 레코드 insert -> a 트랜잭션에서 한 필드 전체 값을 update 후 커밋 -> b 트랜잭션도 커밋’ 했을 시 분명 b트랜잭션 커밋 전에 update해서 insert된 레코드에는 update가 반영되지 않아야 하는데, insert된 레코드의 필드값 까지 update됨)
propagation
1) REQUIRED (default) : 기존에 트랜잭션이 있다면 그것을 쓰고, 없으면 새로 생성 (ex. save 메서드)
=> 즉 두 트랜잭션은 서로 연결되어 있어서 둘다 성공 or 실패한다.
2) REQUIRES_NEW : 항상 별도의 transaction 생성 => 두 트랜잭션은 독립적, 성공/실패도 따로
3) NESTED : 무조건 호출하는 쪽의 트랜잭션만 사용. -웬만해선 사용하지 않는다.
=> 호출되는 트랜잭션에 오류나도 호출하는 쪽은 ㄱㅊ
4) SUPPORTS : 호출하는 쪽의 트랜잭션이 있다면 그것을 쓰고, 없어도 생성하지 않고 처리.
5) NOT_SUPPORTED : 트랜잭션들은 독립적, 근데 호출되는 쪽은 트랜잭션을 생성하지 않음
6) MANDATORY : 이미 생성된 트랜잭션이 필수로 있어야 한다. 없으면 생성하지 않고 오류 발생
7) NEVER : 이미 생성된 트랜잭션이 무조건 없어야 한다. 있으면 오류발생
* 참고) 트랜잭션의 단위는 클래스나 메서드이다.(@Transactional의 Target 속성)
ㄴ 클래스 단위면 해당 메서드들이 모두 트랜잭션 단위가 된다.