@startuml
skinparam defaultFontName "NanumGothic"
package org.springframwork.transaction {
  interface TransactionManager
  interface PlatformTransactionManager
  interface ReactiveTransactionManager
  package org.springframwork.transaction.support {
    interface ResourceTransactionManager
    abstract class AbstractPlatformTransactionManager
    abstract class AbstractReactiveTransactionManager
  }
  package org.springframework.transaction.annotation {
    annotation Transactional
  }
  TransactionManager <|-- PlatformTransactionManager
  TransactionManager <|-- ReactiveTransactionManager
  PlatformTransactionManager <|-- ResourceTransactionManager
  PlatformTransactionManager <|-- AbstractPlatformTransactionManager
}
package org.springframework.jdbc {
  package org.springframework.jdbc.datasource {
    class DataSourceTransactionManager
  }
  package org.springfremework.jdbc.support {
    class JdbcTransactionManager
  }
}
package org.springframework.orm.jpa {
  class JpaTransactionManager
}
AbstractPlatformTransactionManager <|-- DataSourceTransactionManager
ResourceTransactionManager <|-- DataSourceTransactionManager
DataSourceTransactionManager <|-- JdbcTransactionManager
AbstractPlatformTransactionManager <|-- JpaTransactionManager
ResourceTransactionManager <|-- JpaTransactionManager
@enduml
- 
PlatformTransactionManager: 트랜잭션의 경계를 지정하는데 사용.commit,rollback인터페이스
- 
https://godekdls.github.io/Spring%20Data%20Access/transactionmanagement/ 
격리 수준(Isolation)
- 
동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 얼마나 서로 고립되어 있는지는 나타내는 것 
- 
특정 트랜잭션이 다른 트랜잭션에 변경한 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것 
ANSI/ISO SQL Standard (SQL192)
| Note | ANSI SQL DBMS(Oracle, My-SQL, DB2 등등)들에서 각기 다른 SQL를 사용하므로, 미국 표준 협회American National Standards Institute에서 이를 표준화하여 표준 SQL문을 정립 시켜 놓은 것이다. | 
Level 0. Read Uncommitted
1. A 트랜잭션에서 ID가 1인 사용자 나이를 20에서 21로 변경함
2. 아직 커밋하지 않음
3. B 트랜잭션에서 ID가 1인 사용자의 나이를 조회함
4. 21이 조회됨 (1)
5. A 트랜잭션에서 문제가 발생하여 롤백함
6. B 트랜잭션은 ID가 1인 사용자의 나이가 여전히 21이라고 생각하고 로직 수행함- 
이를 Dirty Read라고 함 
- 
이 경우 커밋되지 않은 데이터를 다른 트랜잭션이 조회하여 사용함 
- 
트랜잭션에서 처리중인/아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용 
- 
Dirty Read, Non-Repeatable Read, Phantom Read 현상 발생 
| Note | Dirty Read 
 | 
| Note | Non-Repeatable Read 
 | 
| Note | Phantom Read 한 트랜잭션 안에서 일정범위의 레코드를 두번 이상 읽을 때, 첫번째 쿼리에서 없던 유령 레코드가 두번째 쿼리에서 나타나는 현상 | 
Level 1. Read Committed
1. B 트랜잭션에서 ID가 1인 사용자 나이 조회
2. 20이 조회됨
3. A 트랜잭션에서 ID가 1인 사용자 나이를 20에서 21로 변경하고 커밋함
4. B 트랜잭션에서 ID가 1dl 사용자 나이 재조회
5. 21이 조회됨 (1)- 
하나의 트랜잭션에서 똑같은 쿼리를 수행했을 때 같은 결과를 반환해야한다는 Repeatable Read 정합성이 어긋남 
- 
커밋되어야만 다른 트랜잭션에서 조회 가능함 
Level 2. Repeatable Read
- 
트랜잭션이 시작되지 전에 커밋된 내용에 대해서만 조회할 수 있는 격리 수준 
- 
Non-Repeatable Read 부정합이 발생하지 않음 
- 
자신의 트랜잭션보다 낮은 트랜잭션 번호에 변경된(커밋된) 것만 보게 되는 것 
- 
트랜잭션이 길어질수록 버전을 유지해야하는 단점 존재 
Level 3. Serializable Read
- 
가장 단순하고 엄격한 격리수준 
- 
읽기 작업에도 락 설정 
- 
동시 처리 능력이 다른 격리 수준보다 떨어짐 
전파(Propagation)
- REQUIRED
- 
기본 속성. 부모 트랜잭션이 있으면 참여하고 아니면 신규로 진행한다. 예외가 발생하면 롤백되고 호출한 곳에도 롤백이 전파된다. 
- REQUIRED_NEW
- 
진행중인 트랜잭션이 있으면 잠시 보류하고, 새로운 트랜잭션 시작한다. 부모 트랜잭션을 무시하고 무조건 새로운 트랜잭션이 생성된다. 각 트랜잭션이 독립적으로 동작한다. 트랜잭션간 예외로 인한 롤백이 전파되지 않는다. 
- NESTED
- 
이미 진행중인 트랜잭션이 있으면 중첩 트랜잭션을 시작한다. 둘러쌓인 트랜잭션이 없을 경우 REQUIRED와 동일하다. DB가 SAVEPOINT기능을 지원한다면 지정한 시점까지 부분 롤백이 가능하다.
- MANDATORY
- 
부모 트랜잭션 내에서 실행하며, 부모 트랜잭션이 없을 경우 예외가 발생한다. 
- SUPPORT
- 
부모 트랜잭션 내에서 실행되며, 부모 트랜잭션이 없을 경우 non-transactionally로 실행된다. 
- NOT_SUPPORT
- 
non-transactionally로 실행되며, 부모 트랜잭션이 존재하면 일시 정지한다. 
- NEVER
- 
non-transactional로 실행되며, 부모 트랜잭션이 있을 경우 예외가 발생한다. 
kotlin
Example
@Service
fun UserService(
  transactionManager: PlatformTransactionManager,
) {
  fun execute() {
      TransactionTemplate(transactionManager)
        .apply {
          isolationLevel = TransactionDefinition.ISOLATION_DEFAULT
          propagationBehavior = TransactionDefinition.PROPAGATION_MANDATORY
          timeout = 10_000
          isReadOnly = true
        }
        .execute {
          // 트랜잭션 범위
        }
  }
  // TODO: open class가 되어야 함
  @Transactional
  open fun executeWithAnnotation() {
    // 트랜잭션 범위
  }
}