Transactional μ „νŒŒ 속성 REQUIREDS_NEW

μ„±λŠ₯적으둜 μ’‹μ§€ μ•Šλ‹€λŠ” μ–˜κΈ°λ₯Ό 많이 λ“€μ—ˆλ˜ νŠΈλžœμž­μ…˜ μ „νŒŒ 속성인 REQUIREDS_NEWλ₯Ό μ‚¬μš©ν•˜λ©΄ 항상 두 νŠΈλžœμž­μ…˜μ€ "독립적"으둜 λ™μž‘ν•˜λŠ” 쀄 μ•Œμ•˜μ§€λ§Œ 그게 μ•„λ‹ˆμ˜€λ‹€.

κ²°λ‘ λΆ€ν„° μ–˜κΈ°ν•˜λ©΄ "독립적"μ΄λΌλŠ” ν‘œν˜„μ€ λͺ¨λ“  상황에 μ‚¬μš©ν•  수 μ—†λŠ” 것 κ°™λ‹€.

νŠΈλžœμž­μ…˜μ˜ μ „νŒŒ 속성인 REQUIRES_NEWλŠ” λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•˜κ³  μ •μ˜ν•  수 μžˆλ‹€.

@Transactional(propagation = Propagation.REQUIRES_NEW)
  • "μ™ΈλΆ€ νŠΈλžœμž­μ…˜κ³Ό λ‚΄λΆ€ νŠΈλžœμž­μ…˜μ„ μ™„μ „νžˆ λΆ„λ¦¬ν•˜λŠ” μ „νŒŒ 속성이닀."

  • "이미 μ§„ν–‰ 쀑인 νŠΈλžœμž­μ…˜μ΄ μ‘΄μž¬ν•  λ•Œ μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•˜λŠ” μ „νŒŒ 속성이닀."

상황

κ²Œμ‹œκΈ€μ„ μƒμ„±ν•˜κΈ° μœ„ν•΄μ„œλŠ” μΉ΄ν…Œκ³ λ¦¬λ₯Ό 선택해야 ν•œλ‹€λŠ” 쑰건이 μžˆλ‹€κ³  ν•˜μž.

@Service
public class BoardService() {

    private final CateogryService categoryService;
    
    @Transactional
    public Board create(long categoryId) {
        categoryService.findById(categoryId);
        
        Board boardEntity = new Board();
        boardRepository.save(board);
    }
}

@Service
public class CategoryService() {
    ...
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Category findById(long categoryId) {
        return categoryRepository.findById(categoryId)
                .orElseThrow(() -> new RunTimeException("..."));
    }
}

μœ„μ—μ„œ μ •μ˜ν•œ REQUIRES_NEW 속성 λŒ€λ‘œλΌλ©΄, findById()μ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•΄μ„œ RuntimeException이 λ°œμƒν•΄λ„ 두 νŠΈλžœμž­μ…˜μ€ "독립적"이기 λ•Œλ¬Έμ— save()λŠ” λ™μž‘ν•˜κΈ° λ•Œλ¬Έμ— boardEntityκ°€ DB에 μ •μƒμ μœΌλ‘œ μ €μž₯될 것이라고 μ˜ˆμΈ‘ν•  수 μžˆλ‹€.

에상과 λ‹€λ₯΄κ²Œ findById()μ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ save()도 commit λ˜μ§€ μ•Šκ³  rollback λœλ‹€.

λ…λ¦½λœ νŠΈλžœμž­μ…˜μ΄ μ•„λ‹Œ 이유

@Transactional(propagation = Propagation.REQUIRES_NEW)λ₯Ό μ„ μ–Έν•΄μ„œ λΆ€λͺ¨ νŠΈλžœμž­μ…˜κ³Ό λ…λ¦½μ μœΌλ‘œ λ™μž‘ν•˜λŠ” 것을 κΈ°λŒ€ν–ˆμ§€λ§Œ, μ—¬κΈ°μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ λΆ€λͺ¨ νŠΈλžœμž­μ…˜μ—μ„œ λ™μž‘ν•˜λŠ” save()κΉŒμ§€ λ‘€λ°± μ‹œμΌ°λ‹€. κ·Έ μ΄μœ λŠ” 뭘까? πŸ€”

Javaμ—μ„œλŠ” μ˜ˆμ™Έκ°€ λ°œμƒν•œ κ³³μ—μ„œ μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŠμœΌλ©΄ 콜 μŠ€νƒ(call stack)을 λ”°λΌμ„œ 계속 μƒμœ„ λ©”μ„œλ“œλ‘œ μ „νŒŒλœλ‹€.

이 μ „νŒŒ κ³Όμ •μ—μ„œ μƒμœ„ λ©”μ„œλ“œ 쀑 ν•˜λ‚˜λΌλ„ μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•˜λ©΄ ν”„λ‘œκ·Έλž¨μ€ μ •μƒμ μœΌλ‘œ 계속 싀행될 수 μžˆμ§€λ§Œ, μƒμœ„ λ©”μ„œλ“œλ“€

쀑 μ–΄λŠ κ³³μ—μ„œλ„ μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŸμœΌλ©΄ JVMκΉŒμ§€ μ˜ˆμ™Έ κ°€ μ „νŒŒλ˜μ–΄ ν”„λ‘œκ·Έλž¨μ΄ μ’…λ£Œλœλ‹€.

이것과 같은 이유둜 CategoryService의 findById()μ—μ„œ λ°œμƒν•œ μ˜ˆμ™Έλ₯Ό ν•Έλ“€λ§ν•˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— μ˜ˆμ™Έκ°€ μƒμœ„ λ©”μ†Œλ“œμΈ create()둜 μ „νŒŒλœλ‹€.

create() λ©”μ„œλ“œ λ‚΄μ—μ„œ μ˜ˆμ™Έκ°€ μ²˜λ¦¬λ˜μ§€ μ•ŠμœΌλ©΄, μŠ€ν”„λ§μ˜ νŠΈλžœμž­μ…˜ λ§€λ‹ˆμ €λŠ” 전체 νŠΈλžœμž­μ…˜μ„ λ‘€λ°±ν•œλ‹€.

λ…λ¦½λœ νŠΈλžœμž­μ…˜μœΌλ‘œ λ™μž‘ν•˜λŠ” 방법

방법은 μ–΄λ ΅μ§€ μ•Šλ‹€. create() λ©”μ†Œλ“œμ—μ„œ findById()의 μ˜ˆμ™Έλ₯Ό μΊμΉ˜ν•˜κ³  μ μ ˆν•˜κΈ° μ²˜λ¦¬ν•˜λ©΄ λœλ‹€.

μ΄λ ‡κ²Œ μ„€μ •ν•˜λ©΄ findById()μ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•΄λ„ save() λ©”μ†“λŠ” λ³„λ„μ˜ νŠΈλžœμž­μ…˜μœΌλ‘œ 싀행될 수 μžˆλ‹€.

λ¬Όλ‘  μ΄λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ— 따라 적절히 μ‘°μ •ν•΄μ•Ό ν•œλ‹€.

Summary

docsμ—μ„œλ„ createλΌλŠ” 생성에 κ΄€ν•œ λ‚΄μš©μ€ λ‹΄κ²¨μžˆμ§€λ§Œ "독립"에 λŒ€ν•œ 언급은 μ „ν˜€ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€.

μ²˜μŒμ—λ„ λ§ν–ˆμ§€λ§Œ κ·Έλ ‡κΈ° λ•Œλ¬Έμ— "독립적"μ΄λΌλŠ” ν‘œν˜„μ„ λͺ¨λ“  상황에 μ μš©ν•  수 없을 것 κ°™λ‹€.

또, PROPAGATION_REQUIRES_NEWλ₯Ό μ‚¬μš©ν•˜λ”λΌλ„ μ˜ˆμ™Έ μ²˜λ¦¬μ™€ νŠΈλžœμž­μ…˜ 관리에 주의λ₯Ό κΈ°μšΈμ—¬μ•Ό ν•œλ‹€.

μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμ„ λ•Œ μ›ν•˜λŠ” λ™μž‘μ„ κΈ°λŒ€ν•˜λ €λ©΄, μœ„μ™€ 같은 μ˜ˆμ™Έλ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ²˜λ¦¬ν•˜κ³  νŠΈλžœμž­μ…˜ 경계λ₯Ό 적절히 μ„€μ •ν•΄μ•Ό ν•œλ‹€.

Last updated