1. κ°μ
λ§€μΌ κ°λ°νλ©° μ¬μ©ν΄μ€λ @Transactional μ΄λ Έν μ΄μ μ λν΄ μλκ² λ무 μλ€κ³ λκΌλ€. μ¬λ¬ μ ν μ λ΅, μ΅μ μ΄ μλ€λκ±° μ λλ§ μκ³ μμμ§λ§, κΉκ² κ³ λ €ν΄λ³΄μ§ μμλ κ² κ°λ€.
μ΄μ°Έμ 곡λΆνκ³ μ λλ‘ μκ³ μ¬μ©νκ³ μ μ΄ κΈμ μμ±νλ€.
2. νΈλμμ μ΄λ?
@Transactional μ μμ보기 μμ νΈλμμ μ λν΄ νλ² λ μ§κ³ λμ΄κ°μ.
νΈλμμ (Transaction)μ μ¬μ μ μλ―Έλ κ±°λμ΄κ³ ,
μ»΄ν¨ν° κ³Όν λΆμΌμμμ νΈλμμ (Transaction)μ "λμ΄μ λΆν μ΄ λΆκ°λ₯ν μ 무μ²λ¦¬μ λ¨μ"λ₯Ό μλ―Ένλ€.
μ΄κ²μ νλμ μμ μ μν΄ λμ΄μ λΆν λ μ μλ λͺ λ Ήλ€μ λͺ¨μ, μ¦, νκΊΌλ²μ μνλμ΄μΌ ν μΌλ ¨μ μ°μ°λͺ¨μμ μλ―Ένλ€.
λ°μ΄ν°λ² μ΄μ€μ μ΄ν리μΌμ΄μ μ λ°μ΄ν° κ±°λ(Transaction)μ μμ΄μ μμ μ±μ ν보νκΈ° μν λ°©λ²μ΄ νΈλμμ μΈ κ²μ΄λ€.
νΈλμμ μ νΉμ§μ ν¬κ² 4κ°μ§λ‘ λλλ€.
- μμμ± (Atomicity)
- μΌκ΄μ± (Consistency)
- λ λ¦½μ± (Isolation)
- μ§μμ± (Durability)
μμμ±
1. νΈλμμ μ μ°μ°μ λ°μ΄ν°λ² μ΄μ€μ λͺ¨λ λ°μλλμ§ μλλ©΄ λͺ¨λ λ°μλμ§ μμμΌνλ€.
2. νΈλμμ λ΄μ λͺ¨λ λͺ λ Ήμ λ°λμ μλ²½ν μνλμ΄μΌ νλ©°, νλλΌλ μ€λ₯κ° λ°μνλ©΄ νΈλμμ μ λΆκ° μ·¨μλμ΄μΌ νλ€.
μΌκ΄μ±
1. νΈλμμ μ μ±κ³΅μ μΌλ‘ μλ£νλ©΄ μΌκ΄μ± μλ λ°μ΄ν°λ² μ΄μ€ μνλ‘ λ³ννλ€.
2. μμ€ν μ΄ κ°μ§κ³ μλ κ³ μ μμλ νΈλμμ μν μ κ³Ό νΈλμμ μν μλ£ νμ μνκ° κ°μμΌ νλ€.
λ 립μ±
1. λ μ΄μμ νΈλμμ μ΄ λμμ μ€νλλ κ²½μ° λ€λ₯Έ νΈλμμ μ μ°μ°μ΄ λΌμ΄λ€ μ μλ€.
2. μνμ€μΈ νΈλμμ μ μμ ν μλ£λ λκΉμ§ λ€λ₯Έ νΈλμμ μμ μν κ²°κ³Όλ₯Ό μ°Έμ‘°ν μ μλ€.
μ§μμ±
1. μ±κ³΅μ μΌλ‘ μλ£λ νΈλμμ μ κ²°κ³Όλ μꡬμ μΌλ‘ λ°μλμ΄μΌ νλ€.
3. μ€νλ§μμμ Transaction
μ€νλ§μμλ νΈλμμ (Transaction)μ κ΄λ¦¬νκΈ° μν΄ @Transactional μ΄λ Έν μ΄μ μ μ 곡νλ€.
μ€νλ§μ νΈλμμ μ νλ‘κ·Έλλ° λ°©μκ³Ό μ μΈμ λ°©μ λ κ°μ§λ‘ μ¬μ©ν μ μμ§λ§, λλΆλΆμ κ²½μ° μ μΈμ λ°©μμ μ νΈνκ³ @Transactional μ΄ μ΄μ ν΄λΉλλ€.
ν΄λμ€ λλ λ©μλ μμ @Transactionalμ λΆμ΄λ©΄, νΈλμμ κΈ°λ₯μ΄ μ μ©λ νλ‘μ κ°μ²΄κ° μμ±λλ©°, νΈλμμ μ±κ³΅ μ¬λΆμ λ°λΌ Commit λλ Rollback μμ μ΄ μ΄λ£¨μ΄μ§λ€.
μ€νλ§μμ νΈλμμ μ κ΄λ¦¬νλ λ°©λ²
Springμ΄ μ 곡νλ @Transactional μ μ₯μ μ€ νλλ μ¬λ¬ νΈλμμ μ λ¬Άμ΄μ 컀λ€λ νλμ νΈλμμ κ²½κ³λ₯Ό λ§λ€ μ μλ€λ μ μ΄λ€.
κ°λ°μ νλ€ λ³΄λ©΄ νΈλμμ μ€μ μλ‘μ΄ νΈλμμ μ λ§λ€μ΄μΌ ν λλ μλλ°, μ΄λ μ΄λ»κ² νΈλμμ μ μ²λ¦¬ν κ²μΈμ§μ λν΄ μ€νλ§μ μ¬λ¬ μμ±μ μ 곡ν΄μ€λ€. μ΄κ²μ΄ μ ν μμ±(Propagation)μ΄λ€.
μ ν μμ±μ λ°λΌ κΈ°μ‘΄μ νΈλμμ μ μ°Έμ¬ν μλ μκ³ , λ³λμ νΈλμμ μΌλ‘ μ§νν μλ μκ³ , μλ¬λ₯Ό λ°μμν€λ λ± μ¬λ¬ μ νμ ν μ μλ€. μ΄λ κ² νλμ νΈλμμ μ΄ λ€λ₯Έ νΈλμμ μ λ§λλ μν©μ κ·Έλ¦ΌμΌλ‘ λνλ΄λ©΄ λ€μκ³Ό κ°λ€.
μ΄λ° μν©μ μν΄ μ΄λ₯Ό 물리 νΈλμμ κ³Ό λ Όλ¦¬ νΈλμμ μΌλ‘ λλμ΄ μ²λ¦¬νλ€.
물리 νΈλμμ μ μ€μ λ°μ΄ν°λ² μ΄μ€μ μ μ©λλ νΈλμμ μΌλ‘, 컀λ₯μ μ ν΅ν΄ 컀λ°/λ‘€λ°±νλ λ¨μμ΄κ³ ,
λ Όλ¦¬ νΈλμμ μ μ€νλ§μ΄ μ²λ¦¬νλ νΈλμμ μμμ ꡬλΆνκΈ° μν΄ λ§λ€μ΄μ§ κ°λ μ΄λ€.
μλ₯Ό λ€μ΄ λ€μμ κ·Έλ¦Όμ μΈλΆ νΈλμμ κ³Ό λ΄λΆ νΈλμμ μ΄ 1κ°μ 물리 νΈλμμ (컀λ₯μ )μ μ¬μ©νλ κ²½μ°μ΄λ€.
κΈ°μ‘΄μ νΈλμμ μ΄ μ§νμ€μΌ λ λ λ€λ₯Έ νΈλμμ μ΄ μ¬μ©λλ©΄ 볡μ‘ν μν©μ΄ λ°μνλ€. μ€νλ§μ λ Όλ¦¬ νΈλμμ μ΄λΌλ κ°λ μ λμ ν¨μΌλ‘μ¨ μν©μ λν μ€λͺ μ μ½κ² λ§λ€κ³ , λ€μκ³Ό κ°μ λ¨μν μμΉμ μΈμΈμ μμλ€.
1. λͺ¨λ λ Όλ¦¬ νΈλμμ μ΄ μ»€λ°λμ΄μΌ 물리 νΈλμμ μ΄ μ»€λ°λ¨
2. νλμ λ Όλ¦¬ νΈλμμ μ΄λΌλ λ‘€λ°±λλ©΄ 물리 νΈλμμ μ λ‘€λ°±λ¨
νΈλμμ μ ν μμ±
μ€νλ§μ μ ν μμ±μλ μ΄ 7κ°μ§κ° μλ€.
1. REQUIRED
Defualt μμ±μ΄λ©°, λͺ¨λ νΈλμμ 맀λμ κ° μ§μνλ€. λ³΄ν΅ μ΄ μμ±μΌλ‘ λ§μ΄ μ¬μ©νλ€. 미리 μμλ νΈλμμ μ΄ μμΌλ©΄ μ°Έμ¬νκ³ , μμΌλ©΄ νΈλμμ μ μμ±νλ€.
λΆλͺ¨ λ©μλμ μμ λ©μλκ° νλμ νΈλμμ μΌλ‘ λ¬Άμ¬ λμνλ€. νλμ νΈλμμ μΌλ‘ λ¬Άμ΄κΈ° λλ¬Έμ λΆλͺ¨λ μμμμ μμΈκ° λ°μνλ©΄ μ 체 νΈλμμ μ΄ λ‘€λ°±λλ€.
2. SUPPORTS
μ΄λ―Έ μμλ νΈλμμ μ΄ μμΌλ©΄ μ°Έμ¬νκ³ μμΌλ©΄ νΈλμμ μμ΄ μ§ννλ€. νΈλμμ μ΄ μκΈ΄ νμ§λ§ ν΄λΉ κ²½κ³ μμμ Connectionμ΄λ Hibernate Session λ±μ 곡μ ν μ μλ€.
3. MANDATORY
REQUIREDμ λΉμ·νλ©°, μ΄λ―Έ μμλ νΈλμμ μ΄ μμΌλ©΄ μ°Έμ¬νλ€. νμ§λ§ νΈλμμ μ΄ μλ€λ©΄ μμ±νλ κ²μ΄ μλλΌ μμΈλ₯Ό λ°μμν¨λ€. νΌμμ λ 립μ μΌλ‘ νΈλμμ μ μ€ννλ©΄ μλλ κ²½μ°μ μ¬μ©νλ€.
4. REQUIRED_NEW
νμ μλ‘μ΄ νΈλμμ μ μμνλ€. μ΄λ―Έ μ§ν μ€μΈ νΈλμμ μ΄ μλ€λ©΄, νΈλμμ μ 보λ₯μν¨λ€.
λΆλͺ¨ νΈλμμ κ³Ό μμ λ©μλμ νΈλμμ μ΄ μμ ν λΆλ¦¬λμ΄ λμνλ€. μμ λ©μλμ νΈλμμ μ΄ μλ£λλ©΄ λΆλͺ¨ νΈλμμ μ΄ λ€μ νμ±νλλ€. λΆλͺ¨μ μμ νΈλμμ μ΄ μλ‘ λ 립μ μ΄κΈ° λλ¬Έμ νλκ° μ€ν¨νλλΌλ λ€λ₯Έ νλλ μν₯μ λ°μ§ μμ΅λλ€.
5. NOT_SUPPORTED
νΈλμμ μ μ¬μ©νμ§ μκ² νλ€. μ΄λ―Έ μ§ν μ€μΈ νΈλμμ μ΄ μμΌλ©΄ 보λ₯μν¨λ€.
6. NEVER
νΈλμμ μ μ¬μ©νμ§ μλλ‘ κ°μ νλ€. μ΄λ―Έ μ§ν μ€μΈ νΈλμμ λ μ‘΄μ¬νλ©΄ μλλ©°, νΈλμμ μ΄ μλ€λ©΄ μμΈλ₯Ό λ°μμν¨λ€.
7. NESTED
μ΄λ―Έ μ§ν μ€μΈ νΈλμμ μ΄ μμΌλ©΄ μ€μ²© νΈλμμ μ μμνλ€. μ€μ²© νΈλμμ μ λ§κ·Έλλ‘ νΈλμμ μμ νΈλμμ μ λ§λλ κ²μ΄λ€.λ 립μ μΈ νΈλμμ μ λ§λλ REQUIRED_NEWμλ λ€λ₯΄λ€.
μ€μ²©λ νΈλμμ μ λ¨Όμ μμλ λΆλͺ¨ νΈλμμ μ 컀λ°κ³Ό λ‘€λ°±μλ μν₯μ λ°μ§λ§, μμ μ 컀λ°κ³Ό λ‘€λ°±μ λΆλͺ¨ νΈλμμ μκ² μν₯μ μ£Όμ§ μλλ€. μ΄λ° μ μμ REQUIREDμλ λ€λ₯΄λ€
4. ν μ€νΈ μ½λλ‘ κ²μ¦
λͺ¨λ μ ν μμ±μ κ²μ¦νμ§ μμκ³ , λͺ κ°μ§ κΆκΈν μμ±λ€μ λν΄μλ§ ν μ€νΈλ₯Ό μ§ννμλ€.
1. REQUIERD - μμμμ μμΈκ° ν°μ§ λ
// AService
@Transactional
public void saveWithRequiredFail(Member aMember, Member bMember) {
memberRepository.save(aMember);
// μμΈ λ°μ
try {
bService.saveMemberFail(bMember);
} catch (RuntimeException e) {
System.out.println("Required νΈλμμ
λ‘€λ°± μ²λ¦¬: " + e.getMessage());
}
}
// BService
@Transactional
public void saveMemberFail(Member bMember) {
memberRepository.save(bMember);
throw new RuntimeException();
}
// Test Code
@Test
@DisplayName("[REQUIRED] μμμμ μμΈκ° ν°μ‘μ λ")
public void saveWithRequiredFailTest() {
Member aMember = new Member(1L);
Member bMember = new Member(2L);
assertThatThrownBy(() -> aService.saveWithRequiredFail(aMember, bMember))
.isInstanceOf(RuntimeException.class);
assertThat(memberRepository.findAll()).isEmpty();
}
μμ λ©μλμμ λ°μνλ μμΈλ₯Ό try - catchλ‘ μ‘μμ§λ§, μμ νΈλμμ μμ μμΈκ° ν°μ§λ©΄μ λΆλͺ¨κΉμ§ ν¨κ» λ‘€λ°±λλ€.
2.1 MANDATORY - νΈλμμ μμ΄ μμ νΈλμμ μ νΈμΆνμ λ
// AService
public void saveWithMandatoryFail(Member aMember, Member bMember) {
memberRepository.save(aMember);
bService.saveMemberWithMandatory(bMember);
}
// BService
@Transactional(propagation = Propagation.MANDATORY)
public void saveMemberWithMandatory(Member bMember) {
memberRepository.save(bMember);
}
// Test Code
@Test
@DisplayName("[MANDATORY] λΆλͺ¨κ° νΈλμμ
μ΄ μμ λ")
public void saveWithMandatoryFailTest() {
Member aMember = new Member(1L);
Member bMember = new Member(2L);
// λΆλͺ¨μμ νΈλμμ
μμ΄ μ€ν
assertThatThrownBy(() -> aService.saveWithMandatoryFail(aMember, bMember))
.isInstanceOf(IllegalTransactionStateException.class);
}
νΈλμμ μμ΄ MANDATORY μμ±μ κ°μ§ νμ νΈλμμ μ νΈμΆνλ©΄ IllegalTransactionStateException μμΈκ° λ°μνλ€.
2.2 MANDATORY - νΈλμμ μμ΄ μμ νΈλμμ μ νΈμΆνμ λ
// AService
@Transactional
public void saveWithMandatorySuccess(Member aMember, Member bMember) {
memberRepository.save(aMember);
bService.saveMemberWithMandatory(bMember);
}
// BService
@Transactional(propagation = Propagation.MANDATORY)
public void saveMemberWithMandatory(Member bMember) {
memberRepository.save(bMember);
}
// Test Code
@Test
@DisplayName("[MANDATORY] μ μ₯ μ±κ³΅ ν
μ€νΈ")
public void saveWithMandatorySuccessTest() {
Member aMember = new Member(1L);
Member bMember = new Member(2L);
aService.saveWithMandatorySuccess(aMember, bMember);
assertThat(memberRepository.findAll()).size().isEqualTo(2);
}
νΈλμμ μ λ¬κ³ νΈμΆνλ©΄ μμΈ μμ΄ μ μ μ₯λλ€.
3. NOT SUPPORTED - λΆλͺ¨μμ μμΈκ° ν°μ§ λ NOT SUPPORTED μμ±μ κ°μ§ μμμ΄ μμ λ
// AService
@Transactional
public void saveWithNotSupported(Member aMember, Member bMember) {
memberRepository.save(aMember);
bService.saveWithNotSupported(bMember);
throw new RuntimeException();
}
// BService
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveWithNotSupported(Member bMember) {
memberRepository.save(bMember);
}
// Test Code
@Test
@DisplayName("[NOT SUPPORTED] μμμ Not Supported μ΄κ³ , λΆλͺ¨μμ μμΈκ° λ°μν λ")
public void saveWithNotSupportedTest() {
Member aMember = new Member(1L);
Member bMember = new Member(2L);
assertThatThrownBy(() -> aService.saveWithNotSupported(aMember, bMember))
.isInstanceOf(RuntimeException.class);
assertThat(memberRepository.findAll()).size().isEqualTo(1);
}
NOT SUPPORTED μμ±μ λΆλͺ¨μ νΈλμμ μ 보λ₯μν¨λ€κ³ λμ΄μλ€. νΈλμμ μ΄ Suspendλ μνμ΄λ―λ‘ μμμ νΈλμμ μ μν₯μ λ°μ§ μκ² λκ³ , BServiceμμ μ μ₯ν BMemberλ μ¦μ Databaseμ μ μ₯λλ€.
λ§μ½ λΆλͺ¨ νΈλμμ μμ μμΈκ° ν°μ§μ§ μμλλΌλ©΄ bMember κ° Databaseμ μ μ₯λ ν aMemberκ° μ μ₯λμ κ²μ΄λ€.
4. NEVER - λΆλͺ¨κ° νΈλμμ μ΄ μλλ°, μμμ΄ NEVER μμ±μ κ°μ§ λ
// AService
@Transactional
public void saveWithNeverFail(Member aMember, Member bMember) {
memberRepository.save(aMember);
bService.saveWithNeverFail(bMember);
}
// BService
@Transactional(propagation = Propagation.NEVER)
public void saveWithNeverFail(Member bMember) {
memberRepository.save(bMember);
}
// Test Code
@Test
@DisplayName("[NEVER] μμμ Never μ΄κ³ , λΆλͺ¨κ° νΈλμμ
μ΄ μμ λ")
public void saveWithNeverFailTest() {
Member aMember = new Member(1L);
Member bMember = new Member(2L);
assertThatThrownBy(() -> aService.saveWithNeverFail(aMember, bMember))
.isInstanceOf(IllegalTransactionStateException.class);
}
5.1 NESTED - λΆλͺ¨ νΈλμμ μ λ‘€λ°±μ΄ NESTED μμ±μ κ°μ§ μμμκ² μ νλ λ
// AService
@Transactional
public void saveWithNestedParentException(Member aMember, Member bMember) {
memberRepository.save(aMember);
bService.saveWithNestedParentException(bMember);
throw new RuntimeException();
}
// BService
@Transactional(propagation = Propagation.NESTED)
public void saveWithNestedParentException(Member bMember) {
memberRepository.save(bMember);
}
// Test Code
@Test
@DisplayName("[NESTED] λΆλͺ¨ νΈλμμ
μ λ‘€λ°±μ΄ μμμκ² μ ν")
public void saveWithNestedParentExceptionTest() {
Member aMember = new Member(1L);
Member bMember = new Member(2L);
assertThatThrownBy(() -> aService.saveWithNestedParentException(aMember, bMember))
.isInstanceOf(RuntimeException.class);
assertThat(memberRepository.findAll()).size().isEqualTo(0);
}
μμ νΈλμμ μ΄ NESTED μμ±μ κ°μ§ λ λΆλͺ¨ νΈλμμ μμ μμΈκ° ν°μ§λ©΄ μμ νΈλμμ λ ν¨κ» λ‘€λ°±λλ€.
λ°λμ μν©μ μ΄λ¨κΉ??
5.2 NESTED - NESTED μμ±μ κ°μ§ μμ νΈλμμ μ΄ λ‘€λ°±λ λ
// AService
@Transactional
public void saveWithNestedChildException(Member aMember, Member bMember) {
memberRepository.save(aMember);
try {
bService.saveWithNestedChildException(bMember); // μ€μ²© νΈλμμ
} catch (RuntimeException e) {
System.out.println("μ€μ²© νΈλμμ
λ‘€λ°± μ²λ¦¬: " + e.getMessage());
}
}
// BService
@Transactional(propagation = Propagation.NESTED)
public void saveWithNestedChildException(Member bMember) {
memberRepository.save(bMember);
throw new RuntimeException();
}
// Test Code
@Test
@DisplayName("[NESTED] μμ νΈλμμ
μ λ‘€λ°±μ΄ λΆλͺ¨μκ² μ νλμ§ μμ")
public void saveWithNestedChildExceptionTest() {
Member aMember = new Member(1L);
Member bMember = new Member(2L);
aService.saveWithNestedChildException(aMember, bMember);
assertThat(memberRepository.findAll()).size().isEqualTo(1);
}
μμμ νΈλμμ κ³Όλ 무κ΄νκ² λΆλͺ¨ νΈλμμ μμ μ μ₯ν aMemberλ 컀λ°λλ€.