@Transactional

λ“€μ–΄κ°€λ©΄μ„œ

  • μŠ€ν”„λ§μ—μ„œ νŠΈλžœμž­μ…˜μ€ @Transactional 을 μ„ μ–Έν•˜λ©΄ 적용

  • ν•΄λ‹Ή κΈ°λŠ₯은 AOPλ₯Ό 톡해 κ΅¬ν˜„.

    • AOPκ°€ μ μš©λ˜μ—ˆλŠ” μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄μ„œλŠ” .getClass() λ₯Ό ν†΅ν•΄ν΄λž˜μŠ€ 이름을 확인해도 됨. ($$SpringCGLIB...)

    • ν˜Ήμ€ AopUtils λΌλŠ” ν΄λž˜μŠ€μ—μ„œ μ œκ³΅ν•˜λŠ” λ©”μ„œλ“œλ₯Ό 톡해 확인해볼 수 도 있음.

    • 그리고 public λ©”μ„œλ“œμ—λ§Œ 적용이 κ°€λŠ₯. (private, protected, package-visible μ—” 적용이 μ•ˆλ¨.)

      • protected, package-visible 의 경우 μ™ΈλΆ€μ—μ„œ ν˜ΈμΆœμ€ κ°€λŠ₯ν•˜λ‚˜, μŠ€ν”„λ§ μžμ²΄μ—μ„œ μ μš©λ˜μ§€ μ•Šλ„λ‘ 막고 있음.

  • μŠ€ν”„λ§μ—μ„œ νŠΈλžœμž­μ…˜μ— λŒ€ν•œ κ΄€λ¦¬λŠ” TransactionSynchronizationManager μ—μ„œ 관리.

    • ThreadLocal이라, 각 μŠ€λ ˆλ“œ λ³„λ‘œ κ΄€λ¦¬λœλ‹€κ³  보여짐.

@Transactional μ˜΅μ…˜μ—μ„œμ˜ μš°μ„ μˆœμœ„?

  • μŠ€ν”„λ§μ—μ„œ μš°μ„  μˆœμœ„?

    • 항상 더 ꡬ체적으둜 μžμ„Έν•œ 것이 더 높은 μš°μ„  μˆœμœ„λ₯Ό 가짐.

    • 클래슀 vs λ©”μ„œλ“œ -> λ©”μ„œλ“œ

    • μΈν„°νŽ˜μ΄μŠ€ vs 클래슀(μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ) -> 클래슀 (더 μžμ„Έν•œ κ²ƒμ΄λ―€λ‘œ)

@Transactional(readOnly = true)
    static class LevelService {

        @Transactional(readOnly = false)
        public void write() {
            log.info("call write");
        }
    }
  • 이와 같이 μ„ μ–Έλ˜μ–΄μžˆμ„ λ•Œ, ν”„λ‘μ‹œκ°€ 적용된 LevelSerivce#writeλ₯Ό 호좜 ν•˜λ©΄, readOnly μ˜΅μ…˜μ΄ false둜 μ μš©λ˜μ–΄μ„œ 싀행됨.

  • μ™œ? 더 ꡬ체적인 것(λ©”μ„œλ“œ) 에 적용된 μ˜΅μ…˜μ΄ λ¨Όμ € 적용되기 λ•Œλ¬Έμ—

@Transactional with Interface

  • μΈν„°νŽ˜μ΄μŠ€μ— νŠΈλžœμž­μ…˜μ„ μ μš©ν•˜λŠ” 방법은 μŠ€ν”„λ§μ—μ„œ ꢌμž₯ν•˜μ§€ μ•ŠλŠ” 방법.

  • μ™œ?

    • AOPλ₯Ό μ μš©ν•˜λŠ” 방식에 따라, μΈν„°νŽ˜μ΄μŠ€μ— μ‚¬μš©ν–ˆμ„ 경우 AOPκ°€ μ˜λ„ν•œ λŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•Šμ„ 수 있음.

    • 즉 μΈν„°νŽ˜μ΄μŠ€ 기반 ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°μ—λ§Œ, μ μš©ν•  수 μžˆλŠ” 방법

  • μΈν„°νŽ˜μ΄μŠ€ 기반 ν”„λ‘μ‹œ vs 클래슀 기반 ν”„λ‘μ‹œ

    • JDK Dynamic proxy vs CGLIB

    • μ•„λ§ˆλ„ Spring κΈ°λ³Έ AOP 적용 기술이 CGLIB 즉 클래슀 κΈ°λ°˜μ΄λ―€λ‘œ, λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμ–΄μ„œ μ΄λ ‡κ²Œ ꢌμž₯ν•˜λŠ” λ“―

@Transactional μ‚¬μš©ν•˜λ©΄μ„œ, λ°œμƒν•  수 μžˆλŠ” 문제점

λ‚΄λΆ€ 호좜


@Service
class CallService {

    public void external() {
        log.info("call external");
        internal();
}
    @Transactional
    public void internal() {
        log.info("call internal");
        printTxInfo();
    }

    private void printTxInfo() {
              boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
              log.info("tx active={}", txActive);
    }
}
  • Controllerμ—μ„œ CallService#external (ν”„λ‘μ‹œ 적용된 객체λ₯Ό κ±°μ³μ„œ) λ₯Ό 호좜 ν•œλ‹€κ³  κ°€μ •.

  • 그러면 external μ—μ„œ, internal() 이 호좜 될 것.

  • 이럴 λ•Œ @Transactional 이 적용 될까? (txActive = true?)

  • μ•ˆλœλ‹€.

  • μ™œ?

    • Proxy 객체λ₯Ό κ±°μ³μ„œ ν˜ΈμΆœλ˜λŠ” 것이 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ—, @Transactional 이 λΆ™μ–΄μžˆμ–΄λ„ μ•„λ¬΄λŸ° κΈ°λŠ₯을 ν•˜μ§€ λͺ»ν•¨.

    • 즉 AOPκ°€ μ μš©λ˜μ§€ μ•ŠμŒ.

  • μ •λ¦¬ν•˜μžλ©΄ Springμ—μ„œ AOPκ°€ λ™μž‘ν•˜λŠ” 방식 λ•Œλ¬Έμ— λ°œμƒν•  수 μžˆλŠ” λ¬Έμ œμž„.

  • 해결은 internal() 둜 ν˜ΈμΆœλ˜λŠ” 뢀뢄을 λ‹€λ₯Έ 클래슀둜 λΆ„λ¦¬ν•˜μ—¬ AOPκ°€ 적용된 μƒνƒœλ‘œ ν˜ΈμΆœλ˜λ„λ‘ μˆ˜μ •ν•˜λ©΄ 됨.

@PostContruct 와 @Transactional을 같이 μ‚¬μš©ν•  경우, νŠΈλžœμž­μ…˜μ΄ μ μš©λ˜μ§€ μ•ŠλŠ” 문제

  • μŠ€ν”„λ§ 빈 라이프 사이클

    1. μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆ 생성

    2. μŠ€ν”„λ§ 빈 생성

    3. μ˜μ‘΄κ΄€κ³„μ£Όμž…

    4. μ΄ˆκΈ°ν™” μ½œλ°±μ‚¬μš© @PostConstruct

    5. 빈 μ‚¬μš©

    6. μ†Œλ©Έμ „ 콜백 @PreDestro

  • 라이프 μ‚¬μ΄ν΄λ‘œ 미루어보아, 4λ²ˆμ—μ„œ @PostContruct 싀행함.

  • 4λ²ˆμ—μ„œ @Transactional 에 λŒ€ν•œ ν”„λ‘μ‹œ 빈이 μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— λͺ¨λ‘ μ μš©λ˜μ—ˆλ‹€κ³  보μž₯ν•  수 없을듯

    • @Transactional 을 λ¨Όμ € μˆœμ„œκ°€ 상단에 μžˆμ–΄λ„.. μ•ˆλ λ“―.

  • BeanPostProcessorλŠ” 3~5번 사이에 λ™μž‘ν•˜λ―€λ‘œ.

  • ν•΄κ²°?

  @EventListener(value = ApplicationReadyEvent.class)
  @Transactional
  public void init2() {
      log.info("Hello init ApplicationReadyEvent");
  }
  • @EventListener(value = ApplicationReadyEvent.class) μ΄μš©ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ λͺ¨λ‘ μ˜¬λΌμ™”μ„ λ•Œ, ν˜ΈμΆœλ˜λ„λ‘.

@Transaciotnal μ˜΅μ…˜

νŠΈλžœμž­μ…˜ μ˜ˆμ™Έ, λ‘€λ°±

  • unchecked exception (Runtime exception, Error) λ°œμƒ μ‹œ, 둀백함.

  • checked exeception (Exception) λ°œμƒ μ‹œ, νŠΈλžœμž­μ…˜ 컀밋.

  • 둜그 μ˜΅μ…˜μ„ 톡해, ν•΄λ‹Ή νŠΈλžœμž­μ…˜ 진행에 λŒ€ν•œ 둜그λ₯Ό λͺ¨λ‘ 확인할 수 있음.

logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG

#JPA log
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
logging.level.org.hibernate.resource.transaction=DEBUG

#JPA SQL
logging.level.org.hibernate.SQL=DEBUG
  • μ™œ 언체크 μ˜ˆμ™ΈλŠ” λ‘€λ°±, 체크 μ˜ˆμ™ΈλŠ” 컀밋?

    • 체크 μ˜ˆμ™ΈλŠ” λΉ„μ¦ˆλ‹ˆμŠ€ μ˜ˆμ™Έκ°€ 있음.

      • λΉ„μ¦ˆλ‹ˆμŠ€ μ˜ˆμ™Έ? μ‹œμŠ€ν…œμ  μ˜ˆμ™ΈλŠ” μ•„λ‹ˆμ§€λ§Œ, μ˜ˆμ™Έκ°€ λ°œμƒ (μž”κ³  λΆ€μ‘± 같은)

      • κ·Έλž˜μ„œ λ‘€λ°±κΉŒμ§„ ν•˜μ§€ μ•Šμ•„λ„ λ λ•Œ.

      • 예λ₯Ό λ“€μ–΄, λ§Œμ•½ 둀백해버리면 고객이 μ£Όλ¬Έν•œ μ£Όλ¬Έ μ •λ³΄κΉŒμ§€ μ—†μ–΄μ§€λŠ” 일이 λ°œμƒ. (λΆˆν•„μš”ν•œ..)

    • 언체크 μ˜ˆμ™ΈλŠ” 볡ꡬ λΆˆκ°€λŠ₯ν•˜λ―€λ‘œ, λ˜μ €μ…” μ‚¬μš©μžκ°€ μ•Œκ²Œν•΄μ•Όν•¨. μ‹œμŠ€ν…œ μ˜ˆμ™Έ 예λ₯Ό λ“€λ©΄ λ„€νŠΈμ›Œν¬ 이슈..

μ€‘μ²©λœ νŠΈλžœμž­μ…˜μ—μ„œμ˜ 컀밋과 λ‘€λ°±

νŠΈλžœμž­μ…˜ μ „νŒŒλŠ” κΈ°λ³Έ κ°’μž„ (required)

  • νŠΈλžœμž­μ…˜ μ§„ν–‰ 쀑에, κ·Έ νŠΈλžœμž­μ…˜μ΄ 아직 λλ‚˜μ§€ μ•Šμ•˜λŠ”(컀밋, λ‘€λ°±) 데 κ·Έ λ‚΄λΆ€μ—μ„œ λ˜λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ΄ μƒμ„±λœλ‹€λ©΄, μ™Έ, λ‚΄λΆ€ νŠΈλžœμž­μ…˜ λͺ¨λ‘ ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μœΌλ‘œ 여겨짐 (μŠ€ν”„λ§ κΈ°λ³Έ 방식)

    • νŠΈλžœμž­μ…˜ μ§„ν–‰ μ‹œμ—, 또 λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ„ μƒμ„±ν•œλ‹€λ©΄..

    • ν•˜λ‚˜μ˜ 물리적인 νŠΈλžœμž­μ…˜μ΄ μ‘΄μž¬ν•˜λ©°, κ·Έ μ•ˆμ—λŠ” 논리적 νŠΈλžœμž­μ…˜μ΄ 2개 μžˆλ‹€κ³  λ³Ό 수 있음.

    • 즉 물리 νŠΈλžœμž­μ…˜ = 논리 νŠΈλžœμž­μ…˜ + 논리 νŠΈλžœμž­μ…˜

  • 이 ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μœΌλ‘œ λ¬Άμ΄λŠ” 건 물리적으둜 즉 λ°μ΄ν„°λ² μ΄μŠ€ λ™μž‘μ„ 이야기 ν•˜λŠ” 것.

  • λ‘€λ°± μ „λž΅?

    • 논리 νŠΈλžœμž­μ…˜ 쀑 ν•˜λ‚˜λΌλ„ λ‘€λ°±λœλ‹€λ©΄, 물리적 νŠΈλžœμž­μ…˜μ€ 둀백됨.

    • 즉 논리 νŠΈλžœμž­μ…˜λ“€ λͺ¨λ‘κ°€ 문제 없이 컀밋 λ˜μ–΄μ•Ό, 물리적 νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λœλ‹€λŠ” 이야기 이고, 물리적 νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λœλ‹€λŠ” 건 κ·Έλ–„μ„œμ•Ό λ°μ΄ν„°λ² μ΄μŠ€μ— 컀밋이 λœλ‹€λŠ” 이야기

  • νŠΈλžœμž­μ…˜ μ‹€ν–‰ μ‹œμ—, ν˜„μž¬ μƒνƒœλ₯Ό 확인 (TransactionSynchronizationManagerλ₯Ό 톡해) ν˜„μž¬ μ§„ν–‰λ˜κ³  μžˆλŠ” νŠΈλžœμž­μ…˜μ΄ μžˆλŠ” μ§€ 확인 ν•œλ‹€μŒμ—, 있으면 ν•΄λ‹Ή νŠΈλžœμž­μ…˜μ„ μ΄μš©ν•˜λ„λ‘ 함.

    • ν•΄λ‹Ή νŠΈλžœμž­μ…˜μ— μ°Έμ—¬ν•œλ‹€? == 아무것도 μ•ˆν•œλ‹€λŠ” 뜻.

    • 물리컀λ„₯μ…˜μ„ νšλ“ν•˜κ±°λ‚˜.. 이런 일련의 μž‘μ—…μ„ ν•˜μ§€ μ•ˆν•œλ‹€λŠ” 뜻.

  • ν•˜λ‚˜μ˜ 물리적 νŠΈλžœμž­μ…˜μ΄ 있고, 2개의 논리적 νŠΈλžœμž­μ…˜μ΄ μžˆλ‹€κ³  κ°€μ •ν•˜μž.

    • μ™ΈλΆ€ νŠΈλžœμž­μ…˜(λ¨Όμ € μ‹œμž‘ν•œ νŠΈλžœμž­μ…˜) 은 μ •μƒμ μœΌλ‘œ μ»€λ°‹λ˜μ—ˆλŠ”λ°, λ‚΄λΆ€ νŠΈλžœμž­μ…˜(λ‚˜μ€‘μ— μ‹œμž‘ν•œ)이 λ‘€λ°±λœλ‹€λ©΄?

    • 결과적으둜 물리적 νŠΈλžœμž­μ…˜μ€ 둀백될 것이고.

    • λ‚΄λΆ€ νŠΈλžœμž­μ…˜μ—μ„œ λ‘€λ°±λ˜μ—ˆμ„ λ•Œ, νŠΈλžœμž­μ…˜μ˜ 속성값(rollback-only) λ³€κ²½ν•˜μ—¬, μ™ΈλΆ€ νŠΈλžœμž­μ…˜μ΄ 컀밋 전에 ν•΄λ‹Ή 속성값을 μ²΄ν¬ν•˜λŠ”λ° (컀밋해도 될지), 이 값이 true이기 λ•Œλ¬Έμ—, λ‘€λ°±λœλ‹€.

    • νŠΈλžœμž­μ…˜ λ§€λ‹ˆμ €λŠ” μ‹ κ·œ νŠΈλžœμž­μ…˜μΈμ§€ μ•„λ‹Œμ§€ 여뢀에 따라, μ§„μ§œ 물리적으둜 λ‘€λ°± ν• μ§€, μ•„λ‹ˆλ©΄ μ „μ—­ νŠΈλžœμž­μ…˜μ— 마크만 ν• μ§€ 결정함.

  • μ •λ¦¬ν•˜μžλ©΄, λ‚΄λΆ€ νŠΈλžœμž­μ…˜μ΄ 둀백되면, 물리적 νŠΈλžœμž­μ…˜μ„ λ‘€λ°±ν•˜μ§„ μ•ŠμŒ. κ·Έ λŒ€μ‹ μ— rollback-only = true 에 λ§ˆν¬ν•¨.

    • κ·Έλž˜μ„œ μ™ΈλΆ€ νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹ν•˜κΈ° 전에, ν•΄λ‹Ή 값을 보고, λ‘€λ°±ν•΄μ•Ό 말지 νŒλ‹¨. 마크 λ˜μ–΄μžˆλ‹€λ©΄ 물리적 νŠΈλžœμž­μ…˜μ„ λ‘€λ°±ν•˜κ²Œ 됨.

    • 그리고 UnexpectedRollbackException μ˜ˆμ™Έλ₯Ό 던짐.

νŠΈλžœμž­μ…˜ μ „νŒŒ

REQUIRED

  • κΈ°λ³Έ

    • κ°€μž₯ 많이 μ‚¬μš©ν•¨.

  • κΈ°μ‘΄ νŠΈλžœμž­μ…˜ 있으면, 참여함.

  • κΈ°μ‘΄ νŠΈλžœμž­μ…˜ μ—†μœΌλ©΄ 생성함.

REQUIRED_NEW

  • 항상 μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μƒμ„±ν•˜λŠ” μ˜΅μ…˜

  • κΈ°μ‘΄ νŠΈλžœμž­μ…˜ 있으면, μƒˆλ‘œ 생성.

  • κΈ°μ‘΄ νŠΈλžœμž­μ…˜ μ—†μœΌλ©΄, μƒˆλ‘œ 생성.

    • 즉 μ™ΈλΆ€, λ‚΄λΆ€λ₯Ό μ™„μ „νžˆ λΆ„λ¦¬ν•΄μ„œ μ‚¬μš©ν•˜λŠ” 방법. 즉 물리적으둜 μ™„μ „νžˆ λΆ„λ¦¬ν•˜λŠ” λ°©λ²•μž„.

  • ν•΄λ‹Ή μ˜΅μ…˜μ˜ νŠΈλžœμž­μ…˜μ΄ ν˜ΈμΆœλ˜λŠ” μ‹œμ μ—, 컀λ„₯μ…˜ 풀에 컀λ„₯μ…˜μ΄ λΆ€μ‘±ν•˜λ©΄.. λ°λ“œλ½μ΄ 걸릴 κ°€λŠ₯μ„± 있음.

    • timeout이 있기 λ•Œλ¬Έμ—, μ–΄λŠ μ‹œμ μ—μ„œλŠ” λ‘€λ°±λ˜κ² μ§€λ§Œ..

  • 응? 이게 μ™œ λ‘€λ°±λ˜λŠ”κ±°μ§€? μΆ”κ°€μ μœΌλ‘œ 읽어봐야할 것.

SUPPORT

NOT_SUPPORT

MANDATORY

NEVER

NESTED

  • κΈ°μ‘΄ νŠΈλžœμž­μ…˜μ΄ μ—†μœΌλ©΄, μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜ 생성.

  • κΈ°μ‘΄ νŠΈλžœμž­μ…˜μ΄ 있으면, 쀑첩 νŠΈλžœμž­μ…˜μ„ 생성

  • 쀑첩 νŠΈλžœμž­μ…˜ ?

    • 이 νŠΈλžœμž­μ…˜μ€ μ™ΈλΆ€ νŠΈλžœμž­μ…˜μ˜ 영ν–₯을 λ°›μ§€λ§Œ, 이 νŠΈλžœμž­μ…˜μ΄ μ™ΈλΆ€ νŠΈλžœμž­μ…˜μ— 영ν–₯을 μ£Όμ§„ μ•ŠμŒ.

    • e.g μ™ΈλΆ€ νŠΈλžœμž­μ…˜ λ‘€λ°± -> 쀑첩 νŠΈλžœμž­μ…˜ λ‘€λ°±, 쀑첩 νŠΈλžœμž­μ…˜ λ‘€λ°± -> μ™ΈλΆ€ νŠΈλžœμž­μ…˜ 컀밋 κ°€λŠ₯

νŠΈλžœμž­μ…˜ μ „νŒŒ, μ˜΅μ…˜μ˜ 적용

  • isolation, timeout, readOnly λ“±μ˜ μ˜΅μ…˜μ„ νŠΈλžœμž­μ…˜μ΄ μƒˆλ‘œ μ‹œμž‘ν•  λ•Œλ§Œ 적용됨.

  • e.g REQUIRED μ—μ„œ κΈ°μ‘΄ νŠΈλžœμž­μ…˜μ΄ μ—†κ³ , μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•  λ•Œ 적용, μ°Έμ—¬ν•  λ•ŒλŠ” ν•΄λ‹Ή νŠΈλžœμž­μ…˜μ— μ˜΅μ…˜μ΄ μžˆμ–΄λ„ 적용이 μ•ˆλœλ‹€. (μ™ΈλΆ€ νŠΈλžœμž­μ…˜ μ˜΅μ…˜μ΄ 적용됨.)

μ°Έκ³ 

Last updated

Was this helpful?