Transactional

μŠ€ν”„λ§μ€ PlatformTransactionManager μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 νŠΈλžœμž­μ…˜μ„ μΆ”μƒν™”ν•˜κ³  선언적 νŠΈλžœμž­μ…˜μ„ μ§€μ›ν•˜μ—¬ νŠΈλžœμž­μ…˜μ„ νŽΈλ¦¬ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλ„λ‘ μ§€μ›ν•œλ‹€.

  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” @Transactional μ• λ…Έν…Œμ΄μ…˜μ΄ 적용된 λΉˆμ„ 찾으면, ν•΄λ‹Ή 빈의 μ‹€μ œ 객체 λŒ€μ‹  νŠΈλžœμž­μ…˜ λ‘œμ§μ„ 담은 ν”„λ‘μ‹œ 객체λ₯Ό μƒμ„±ν•˜μ—¬ μ»¨ν…Œμ΄λ„ˆμ— 등둝

  • λ‹€λ₯Έ λΉˆμ—μ„œ μ˜μ‘΄μ„±μ„ μ£Όμž…λ°›μ„ λ•Œ, μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μ‹€μ œ 객체 λŒ€μ‹  ν”„λ‘μ‹œ 객체λ₯Ό μ£Όμž…

  • ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ€ μ£Όμž…λ°›μ€ ν”„λ‘μ‹œ 객체λ₯Ό 톡해 μ „λ‹¬λ˜λ©°, ν”„λ‘μ‹œλŠ” νŠΈλžœμž­μ…˜ 처리 ν›„ μ‹€μ œ 객체의 λ©”μ„œλ“œλ₯Ό 호좜

νŠΈλžœμž­μ…˜ λ™μž‘ 방식과 동기화

@Transactional이 적용된 λ©”μ„œλ“œκ°€ 호좜될 λ•Œμ˜ 흐름은 λ‹€μŒκ³Ό κ°™λ‹€.

  1. ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ„ ν”„λ‘μ‹œ 객체가 λ¨Όμ € λ°›μŒ

  2. ν”„λ‘μ‹œλŠ” νŠΈλžœμž­μ…˜μ΄ ν•„μš”ν•œμ§€ ν™•μΈν•˜κ³ , ν•„μš”ν•˜λ‹€λ©΄ PlatformTransactionManagerλ₯Ό 톡해 νŠΈλžœμž­μ…˜μ„ μ‹œμž‘

  3. ν”„λ‘μ‹œλŠ” νŠΈλžœμž­μ…˜ 동기화 λ§€λ‹ˆμ €(TransactionSynchronizationManager)에 ν˜„μž¬ νŠΈλžœμž­μ…˜ 정보λ₯Ό 보관

  4. ν”„λ‘μ‹œκ°€ μ‹€μ œ 객체의 λ©”μ„œλ“œλ₯Ό 호좜

  5. μ‹€μ œ 객체의 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€ν–‰

  6. 둜직 μˆ˜ν–‰μ΄ λλ‚˜λ©΄ ν”„λ‘μ‹œλ‘œ μ œμ–΄κ°€ λŒμ•„μ˜΄

  7. μ˜ˆμ™Έ λ°œμƒ 여뢀에 따라 ν”„λ‘μ‹œλŠ” PlatformTransactionManagerμ—κ²Œ νŠΈλžœμž­μ…˜ 컀밋 λ˜λŠ” 둀백을 μš”μ²­

  8. νŠΈλžœμž­μ…˜μ΄ μ’…λ£Œλ˜κ³ , νŠΈλžœμž­μ…˜ 동기화 λ§€λ‹ˆμ €μ— λ³΄κ΄€λœ νŠΈλžœμž­μ…˜ 정보가 정리

νŠΈλžœμž­μ…˜ 컀밋 κ³Όμ •

νŠΈλžœμž­μ…˜ 컀밋 과정은 λ‹€μŒκ³Ό 같은 λ‹¨κ³„λ‘œ 이루어진닀.

  1. @Transactional λ©”μ„œλ“œκ°€ 호좜되면, μŠ€ν”„λ§μ€ AOPλ₯Ό 톡해 νŠΈλžœμž­μ…˜ 처리 μ‹œμž‘

  2. ν•΄λ‹Ή ν”„λ‘μ‹œλŠ” μ‹€μ œ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€ν–‰ μ „ν›„λ‘œ νŠΈλžœμž­μ…˜ κ΄€λ ¨ λΆ€κ°€ κΈ°λŠ₯을 μˆ˜ν–‰ν•˜λŠ” TransactionInterceptor에 μ œμ–΄λ₯Ό μœ„μž„

  3. TransactionInterceptorλŠ” PlatformTransactionManager의 κ΅¬ν˜„μ²΄λ₯Ό μ‚¬μš©ν•˜μ—¬ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•˜κ³ , λΉ„μ¦ˆλ‹ˆμŠ€ 둜직이 μ •μƒμ μœΌλ‘œ μ™„λ£Œλ˜λ©΄ 컀밋 μš”μ²­

  4. μ‹€μ œ 컀밋 과정은 PlatformTransactionManager의 좔상 클래슀인 AbstractPlatformTransactionManager의 processCommit λ©”μ„œλ“œμ—μ„œ λ‹¨κ³„μ μœΌλ‘œ 처리

processCommit λ©”μ„œλ“œμ—μ„œ μ»€λ°‹μ˜ 핡심 둜직이 μˆ˜ν–‰λ˜λ©°, μ£Όμš” λ‹¨κ³„λŠ” λ‹€μŒκ³Ό κ°™λ‹€.


private void processCommit(DefaultTransactionStatus status) {
    try {
        boolean beforeCompletionInvoked = false;

        try {
            prepareForCommit(status);          // 1. 컀밋 μ€€λΉ„
            triggerBeforeCommit(status);       // 2. 컀밋 μ „ 콜백
            triggerBeforeCompletion(status);   // 3. μ™„λ£Œ 직전 콜백
            beforeCompletionInvoked = true;

            // ... μ„Έμ΄λΈŒν¬μΈνŠΈ/νŠΈλžœμž­μ…˜ λΆ„κΈ° 처리 λ“±
            else if (status.isNewTransaction()) {
                doCommit(status);              // 4. DB νŠΈλžœμž­μ…˜ 컀밋 (이 μ‹œμ μ— DB νŠΈλžœμž­μ…˜ μ’…λ£Œ)
            }

        } catch (Exception ex) {
            // μ˜ˆμ™Έ λ°œμƒ μ‹œ λ‘€λ°± 및 μ˜ˆμ™Έ μ „νŒŒ
            throw ex;
        }

        try {
            triggerAfterCommit(status);        // 5. 컀밋 이후 콜백
        } finally {
            triggerAfterCompletion(            // 6. μ™„λ£Œ ν›„ 콜백
                    status, TransactionSynchronization.STATUS_COMMITTED
            );
        }

    } finally {
        cleanupAfterCompletion(status);        // 7. νŠΈλžœμž­μ…˜ μ»¨ν…μŠ€νŠΈ 정리 (λ¦¬μ†ŒμŠ€ 언바인딩)
    }
}
  1. prepareForCommit: ν”ŒλŸ¬μ‹œ λ“± 컀밋 직전 μ€€λΉ„ μˆ˜ν–‰

  2. triggerBeforeCommit: 컀밋 μ „ 콜백 μ‹€ν–‰

    • 아직 DB νŠΈλžœμž­μ…˜μ΄ μ‚΄μ•„μžˆλŠ” μƒνƒœ

  3. triggerBeforeCompletion: μ™„λ£Œ 직전 콜백 μ‹€ν–‰

  4. doCommit: μ‹€μ œ DB 컀밋 μˆ˜ν–‰

    • 이 μ‹œμ μ— DB νŠΈλžœμž­μ…˜μ΄ μ’…λ£Œ

  5. triggerAfterCommit: 컀밋 이후 콜백 μ‹€ν–‰

    • DB νŠΈλžœμž­μ…˜μ€ 이미 μ’…λ£Œλ˜μ—ˆμœΌλ‚˜, μŠ€ν”„λ§ νŠΈλžœμž­μ…˜ μ»¨ν…μŠ€νŠΈλŠ” 아직 μ‚΄μ•„μžˆλŠ” μƒνƒœ

  6. triggerAfterCompletion: μ™„λ£Œ ν›„ 콜백 μ‹€ν–‰

  7. cleanupAfterCompletion: μŠ€λ ˆλ“œ μ»¨ν…μŠ€νŠΈ 정리

νŠΈλžœμž­μ…˜ μš°μ„  μˆœμœ„

@Transactional μ• λ…Έν…Œμ΄μ…˜μ€ 클래슀, μΈν„°νŽ˜μ΄μŠ€, λ©”μ„œλ“œμ— μ μš©ν•  수 있으며, μš°μ„ μˆœμœ„λŠ” 더 ꡬ체적이고 μžμ„Έν•œ 것이 높은 μš°μ„ μˆœμœ„λ₯Ό κ°€μ§€λŠ” 것을 μ›μΉ™μœΌλ‘œ ν•œλ‹€.

  1. 클래슀의 λ©”μ„œλ“œ

  2. 클래슀

  3. μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ„œλ“œ

  4. μΈν„°νŽ˜μ΄μŠ€

자기 호좜(Self Invocation)

@Transactional이 적용 됐을 λ•Œ νŠΈλžœμž­μ…˜μ΄ μ μš©μ€ ν”„λ‘μ‹œ 객체λ₯Ό 톡해 μˆ˜ν–‰λ˜λŠ”λ°, λ§Œμ•½ ν”„λ‘μ‹œ 객체λ₯Ό κ±°μΉ˜μ§€ μ•Šκ³  λŒ€μƒ 객체λ₯Ό 직접 ν˜ΈμΆœν•˜κ²Œ 되면 νŠΈλžœμž­μ…˜μ΄ μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€. λ³΄ν†΅μ˜ κ²½μš°λŠ” ν”„λ‘μ‹œ 객체λ₯Ό 거치기 λ•Œλ¬Έμ— λ¬Έμ œκ°€ λ˜μ§€ μ•Šμ§€λ§Œ, λŒ€μƒ 객체 λ‚΄λΆ€μ—μ„œ λ©”μ„œλ“œ ν˜ΈμΆœμ„ ν•˜κ²Œ 되면 ν”„λ‘μ‹œλ₯Ό κ±°μΉ˜μ§€ μ•Šκ²Œ λ˜μ–΄ μœ„μ˜ λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆλ‹€.

class Example {

    public void external() {
        // do something
        internal(); // ν”„λ‘μ‹œλ₯Ό κ±°μΉ˜μ§€ μ•Šκ³  λŒ€μƒ 객체 λ‚΄λΆ€μ—μ„œ λ©”μ„œλ“œ ν˜ΈμΆœν•˜κΈ° λ•Œλ¬Έμ— νŠΈλžœμž­μ…˜μ΄ μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€.
    }

    @Transactional
    public void internal() {
        // do something
    }
}

ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ external λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν–ˆμ„ λ•Œ λ‹€μŒκ³Ό 같은 ν”„λ‘œμ„ΈμŠ€κ°€ μ§„ν–‰λœλ‹€.

  1. ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ ν”„λ‘μ‹œ 호좜

  2. ν”„λ‘μ‹œμ—μ„œ external λ©”μ„œλ“œμ— νŠΈλžœμž­μ…˜μ΄ μ μš©λ˜μ–΄ μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— νŠΈλžœμž­μ…˜ 없이 λ©”μ„œλ“œ 호좜

  3. μ‹€μ œ external λ©”μ„œλ“œ μ‹€ν–‰

  4. external λ©”μ„œλ“œ λ‚΄λΆ€μ—μ„œ internal λ©”μ„œλ“œ 호좜

  5. μ‹€ν–‰ 된 internal λ©”μ„œλ“œλŠ” μ‹€μ œ κ°μ²΄μ—μ„œ μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ— νŠΈλžœμž­μ…˜μ΄ μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€.

이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μ—¬λŸ¬ ν•΄κ²°λ°©μ•ˆλ“€μ΄ μ‘΄μž¬ν•˜μ§€λ§Œ 보톡 μ‹€λ¬΄μ—μ„œλŠ” 별도 클래슀둜 λΆ„λ¦¬ν•˜μ—¬ ν˜ΈμΆœν•˜λŠ” 것이 κ°€μž₯ μ μ ˆν•œ 방법이닀.

μ΄ˆκΈ°ν™” μ‹œμ 

μŠ€ν”„λ§ μ΄ˆκΈ°ν™” μ‹œμ μ—λŠ” νŠΈλžœμž­μ…˜ AOPκ°€ μ μš©λ˜μ§€ μ•Šμ„ 수 있기 λ•Œλ¬Έμ— νŠΈλžœμž­μ…˜μ΄ ν•„μš”ν•œ 둜직이 ν•„μš”ν•œ λ©”μ„œλ“œ μ‹€ν–‰ μ‹œμ μ„ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ™„μ „νžˆ μƒμ„±λ˜κ³  λ‚œ 뒀에 ν˜ΈμΆœν•  수 μžˆλ„λ‘ μ„€μ •ν•˜λŠ” 것이 μ’‹λ‹€.

class Hello {

    @PostConstruct
    @Transactional
    public void init1() {
        boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
        log.info("tx active={} ", isActive); // false
    }

    @EventListener(ApplicationReadyEvent.class)
    @Transactional
    public void init2() {
        boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
        log.info("tx active={} ", isActive); // true
    }
}

νŠΈλžœμž­μ…˜ μ˜΅μ…˜

@Transactional μ• λ…Έν…Œμ΄μ…˜μ„ 톡해 νŠΈλžœμž­μ…˜μ„ μ μš©ν•  λ•Œ μ•„λž˜μ™€ 같이 μ˜΅μ…˜μ„ μ„€μ •ν•  수 있으며, μ§€μ •ν•˜μ§€ μ•Šμ€ κ²½μš°μ—” 기본값이 μ μš©λœλ‹€.


@Transactional(isolation = Isolation.DEFAULT, readOnly = false)
class Example {

}

μ• λ…Έν…Œμ΄μ…˜μ— μ μš©ν•  수 μžˆλŠ” μ˜΅μ…˜λ“€μ€ μ•„λž˜μ™€ κ°™λ‹€.

1. rollbackFor / noRollbackFor

μ˜ˆμ™Έ λ°œμƒμ‹œ μŠ€ν”„λž‘ νŠΈλžœμž­μ…˜μ˜ λ‘€λ°± μ •μ±…μœΌλ‘œ κΈ°λ³Έ 정책은 μ•„λž˜μ™€ κ°™λ‹€.

  • 언체크 μ˜ˆμ™Έ: λ‘€λ°±

  • 체크 μ˜ˆμ™Έ: λ‘€λ°±ν•˜μ§€ μ•Šκ³  컀밋

이 μ˜΅μ…˜μ— μΆ”κ°€λ‘œ λ‘€λ°±ν•  μ˜ˆμ™Έλ₯Ό μ§€μ •ν•˜κ²Œ 되면, ν•΄λ‹Ή μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμ„ λ•Œ λ‘€λ°±ν•˜κ²Œ λœλ‹€.


@Transactional(rollbackFor = Exception.class)
class Example {

}

λ°˜λŒ€λ‘œ noRollbackFor μ˜΅μ…˜μ€ λ‘€λ°±ν•˜μ§€ μ•Šμ„ μ˜ˆμ™Έλ₯Ό μ§€μ •ν•  수 μžˆλ‹€.

2. isolation

νŠΈλžœμž­μ…˜ 격리 μˆ˜μ€€ μ§€μ •μœΌλ‘œ 보톡 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ„€μ •ν•œ νŠΈλžœμž­μ…˜ μˆ˜μ€€μ„ μ‚¬μš©ν•˜λŠ” DEFAULTλ₯Ό μ‚¬μš©ν•œλ‹€.

3. timeout

νŠΈλžœμž­μ…˜ νƒ€μž„μ•„μ›ƒμ„ μ§€μ •ν•˜λŠ” μ˜΅μ…˜μœΌλ‘œ 기본값은 -1둜 λ¬΄μ œν•œμ΄λ‹€.

4. readOnly

  • false: 읽기 μ“°κΈ°κ°€ λͺ¨λ‘ κ°€λŠ₯ν•œ νŠΈλžœμž­μ…˜

  • true: 읽기 μ „μš© νŠΈλžœμž­μ…˜(λ“œλΌμ΄λ²„λ‚˜ DB에 따라 읽기 μ „μš© νŠΈλžœμž­μ…˜μ„ μ§€μ›ν•˜μ§€ μ•Šμ„ 수 있음)

5. propagation

νŠΈλžœμž­μ…˜ μ „νŒŒ μ˜΅μ…˜μœΌλ‘œ 기본값은 REQUIRED둜, λŒ€λΆ€λΆ„ 이 μ˜΅μ…˜μ„ μ‚¬μš©ν•œλ‹€.

μ˜΅μ…˜
μ„€λͺ…
κΈ°μ‘΄ νŠΈλžœμž­μ…˜ X
κΈ°μ‘΄ νŠΈλžœμž­μ…˜ O

REQUIRED

ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜ μ‚¬μš©

μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜ 생성

κΈ°μ‘΄ νŠΈλžœμž­μ…˜ μ‚¬μš©

REQUIRES_NEW

항상 μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜ μ‚¬μš©

μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜ 생성

μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜ 생성

SUPPORT

νŠΈλžœμž­μ…˜ 지원

νŠΈλžœμž­μ…˜ 없이 μ§„ν–‰

κΈ°μ‘΄ νŠΈλžœμž­μ…˜ μ‚¬μš©

NOT_SUPPORTED

νŠΈλžœμž­μ…˜ 미지원

νŠΈλžœμž­μ…˜ 없이 μ§„ν–‰

νŠΈλžœμž­μ…˜ 없이 μ§„ν–‰(κΈ°μ‘΄ νŠΈλžœμž­μ…˜ 보λ₯˜)

MANDATORY

νŠΈλžœμž­μ…˜μ΄ λ°˜λ“œμ‹œ μ‘΄μž¬ν•΄μ•Ό 함

μ˜ˆμ™Έ λ°œμƒ

κΈ°μ‘΄ νŠΈλžœμž­μ…˜ μ‚¬μš©

NEVER

νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•˜μ§€ μ•ŠμŒ

νŠΈλžœμž­μ…˜ 없이 μ§„ν–‰

μ˜ˆμ™Έ λ°œμƒ

isolation , timeout , readOnly λŠ” νŠΈλžœμž­μ…˜μ΄ 처음 μ‹œμž‘λ  λ•Œλ§Œ 적용되며, νŠΈλžœμž­μ…˜μ— μ°Έμ—¬ν•˜λŠ” κ²½μš°μ—λŠ” μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€.

νŠΈλžœμž­μ…˜ μ „νŒŒ 흐름 - REQUIRED μ˜΅μ…˜

νŠΈλžœμž­μ…˜ μ „νŒŒ μ˜΅μ…˜μ΄ REQUIRED인 경우 이미 νŠΈλžœμž­μ…˜μ΄ μ‘΄μž¬ν•˜λ©΄ ν•΄λ‹Ή νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•˜κ³  μ—†μœΌλ©΄ μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μƒμ„±ν•˜κ²Œ λœλ‹€. ν•˜λ‚˜μ˜ 컀밋이라도 λ°œμƒν•˜λ©΄ 전체 νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λ˜κ³ , ν•˜λ‚˜μ˜ 둀백이라도 λ°œμƒν•˜λ©΄ 전체 νŠΈλžœμž­μ…˜μ΄ 둀백되며, κ·Έ 원리와 μˆœμ„œλŠ” μ•„λž˜μ™€ κ°™λ‹€.

νŠΈλžœμž­μ…˜ μš”μ²­/응닡 흐름
  • λ‚΄λΆ€ νŠΈλžœμž­μ…˜ μ‹€νŒ¨λ‘œ μ™ΈλΆ€ νŠΈλžœμž­μ…˜μ΄ λ‘€λ°±λ˜λŠ” μ˜ˆμ‹œ

// μ™ΈλΆ€ νŠΈλžœμž­μ…˜
@Service
public class BatchRegistrationService {

    private final MemberService memberService;

    public BatchRegistrationService(MemberService memberService) {
        this.memberService = memberService;
    }

    @Transactional
    public void registerMultipleMembers() {
        for (long point = 0; point < 5; point++) {
            try {
                memberService.registerMember(point);
            } catch (Exception e) {
                System.out.println("μ˜ˆμ™Έ λ°œμƒ: " + e.getMessage());
            }
        }
    }
}

// λ‚΄λΆ€ νŠΈλžœμž­μ…˜
@Service
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Transactional
    public void registerMember(long point) {
        if (point == 2L) {
            throw new RuntimeException("ν¬μΈνŠΈκ°€ 2인 νšŒμ›μ€ 등둝할 수 μ—†μŠ΅λ‹ˆλ‹€.");
        }
        Member member = new Member(point);
        memberRepository.save(member);
    }
}

μ™ΈλΆ€ νŠΈλžœμž­μ…˜μ—μ„œ μ˜ˆμ™Έλ₯Ό κ°μ‹Έμ„œ μ™ΈλΆ€ νŠΈλžœμž­μ…˜μ—μ„œμ˜ μ˜ˆμ™Έ λ°œμƒμ„ λ°©μ§€ν–ˆμ§€λ§Œ, λ‚΄λΆ€ νŠΈλžœμž­μ…˜μ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ—¬ λ‘€λ°± λ§ˆν‚ΉλκΈ° λ•Œλ¬Έμ— 전체 νŠΈλžœμž­μ…˜μ΄ λ‘€λ°±λœλ‹€.

참고자료

Last updated

Was this helpful?