[Spring] πŸ“š @Transactional μ΄ν•΄ν•˜κΈ°

ν—ŒμΉ˜Β·2022λ…„ 8μ›” 7일
14

Spring

λͺ©λ‘ 보기
3/13
post-thumbnail

ν•΄λ‹Ή 글은 CS μŠ€ν„°λ”” 자료 와 λ™μΌν•©λ‹ˆλ‹€.

//κ²Œμ‹œνŒμ˜ κ²Œμ‹œκΈ€μ„ μ‚­μ œν•˜λŠ” λ©”μ„œλ“œ 
@Transactional
public void removeBoard(Long id)throws Exception{
    replyDAO.removeAll(id); //μ‚­μ œν•  κ²Œμ‹œκΈ€μ˜ λ‹΅κΈ€ μ‚­μ œ
    boardDAO.deleteBoard(id); //κ²Œμ‹œκΈ€ μ‚­μ œ 
}

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

1. μ •μ˜

λ°μ΄ν„°λ² μ΄μŠ€ 관리 μ‹œμŠ€ν…œ λ˜λŠ” μœ μ‚¬ν•œ μ‹œμŠ€ν…œμ—μ„œ μƒν˜Έμž‘μš©μ˜ λ‹¨μœ„μ΄λ‹€. μ—¬κΈ°μ„œ μœ μ‚¬ν•œ μ‹œμŠ€ν…œμ΄λž€ νŠΈλžœμž­μ…˜μ΄ 성곡과 μ‹€νŒ¨κ°€ λΆ„λͺ…ν•˜κ³  μƒν˜Έ 독립적이며, μΌκ΄€λ˜κ³  믿을 수 μžˆλŠ” μ‹œμŠ€ν…œμ„ μ˜λ―Έν•œλ‹€. λ°μ΄ν„°μ˜ 정합성을 보μž₯ν•˜κΈ° μœ„ν•΄ κ³ μ•ˆλœ 방법이닀.

λͺ©μ 

  • 였λ₯˜λ‘œλΆ€ν„° 볡ꡬλ₯Ό ν—ˆμš©ν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μΌκ΄€μ„±μžˆκ²Œ μœ μ§€ν•˜λŠ” μ•ˆμ •μ μΈ μž‘μ—… λ‹¨μœ„λ₯Ό μ œκ³΅ν•œλ‹€.
  • λ™μ‹œ μ ‘κ·Όν•˜λŠ” μ—¬λŸ¬ ν”„λ‘œκ·Έλž¨ κ°„ 격리λ₯Ό μ œκ³΅ν•œλ‹€

2. ACID

이둠적으둜 λ°μ΄ν„°λ² μ΄μŠ€ μ‹œμŠ€ν…œμ€ 각각의 νŠΈλžœμž­μ…˜μ— λŒ€ν•΄ μ›μžμ„±(Atomicity), 일관성(Consistency), 독립성(Isolation), μ˜κ΅¬μ„±(Durability)을 보μž₯ν•œλ‹€. 이 μ„±μ§ˆμ„ μ²«κΈ€μžλ₯Ό λ”° ACID라 λΆ€λ₯Έλ‹€.

  • μ›μžμ„±(Atomicity) :
    νŠΈλžœμž­μ…˜μ΄ μ™„μ „νžˆ μ„±κ³΅ν•˜κ±°λ‚˜ μ™„μ „νžˆ μ‹€νŒ¨ν•˜λŠ” 단일 λ‹¨μœ„λ‘œ μ²˜λ¦¬λ˜λ„λ‘ 보μž₯ν•˜λŠ” λŠ₯λ ₯이닀. 쀑간 λ‹¨κ³„κΉŒμ§€ μ‹€ν–‰λ˜κ³  μ‹€νŒ¨ν•˜λŠ” 일이 없도둝 ν•˜λŠ” 것이닀.
  • 일관성(Consistency): 각 데이터 νŠΈλžœμž­μ…˜μ΄ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό 일관성 μžˆλŠ” μƒνƒœμ—μ„œ 일관성 μžˆλŠ” μƒνƒœλ‘œ 이동해야 함을 μ˜λ―Έν•œλ‹€. 즉 νŠΈλžœμž­μ…˜μ΄ μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œν•˜λ©΄ μ–Έμ œλ‚˜ λ™μΌν•œ λ°μ΄ν„°λ² μ΄μŠ€ μƒνƒœλ‘œ μœ μ§€ν•˜λŠ” 것을 μ˜λ―Έν•œλ‹€.
  • 독립성(Isolation): νŠΈλžœμž­μ…˜μ„ μˆ˜ν–‰ μ‹œ λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ˜ μ—°μ‚° μž‘μ—…μ΄ 끼어듀지 λͺ»ν•˜λ„둝 보μž₯ν•˜λŠ” 것을 μ˜λ―Έν•œλ‹€. 이것은 νŠΈλžœμž­μ…˜ 밖에 μžˆλŠ” μ–΄λ–€ 연산도 쀑간 λ‹¨κ³„μ˜ 데이터λ₯Ό λ³Ό 수 μ—†μŒμ„ μ˜λ―Έν•œλ‹€. μ—¬λŸ¬ νŠΈλžœμž­μ…˜μ΄ λ™μ‹œμ— λ°œμƒν•˜λŠ” 경우 μ΅œμ’… μƒνƒœλŠ” νŠΈλžœμž­μ…˜μ΄ κ°œλ³„μ μœΌλ‘œ λ°œμƒν•œ 것과 κ°™μ•„μ•Ό ν•œλ‹€. 즉, λ°μ΄ν„°λ² μ΄μŠ€λŠ” 슀트레슀 ν…ŒμŠ€νŠΈλ₯Ό 톡과해야 ν•œλ‹€. 즉, κ³ΌλΆ€ν•˜λ‘œ 인해 잘λͺ»λœ λ°μ΄ν„°λ² μ΄μŠ€ νŠΈλžœμž­μ…˜μ΄ λ°œμƒν•˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€.
  • 지속성(Durability) : μ„±κ³΅μ μœΌλ‘œ μˆ˜ν–‰λœ νŠΈλžœμž­μ…˜μ€ μ˜μ›νžˆ 반영(기둝)λ˜μ–΄μ•Ό 함을 μ˜λ―Έν•œλ‹€. νŠΈλžœμž­μ…˜μ€ λ‘œκ·Έμ— λͺ¨λ“  것이 μ €μž₯된 ν›„μ—λ§Œ commit μƒνƒœλ‘œ 간주될 수 μžˆλ‹€. λ°μ΄ν„°λ² μ΄μŠ€ λ‚΄μ˜ λ°μ΄ν„°λŠ” νŠΈλžœμž­μ…˜μ˜ 결과둜만 λ³€κ²½λ˜μ–΄μ•Ό ν•˜λ©° μ™ΈλΆ€ 영ν–₯에 μ˜ν•΄ 변경될 수 μ—†μ–΄μ•Ό ν•œλ‹€. 예λ₯Ό λ“€μ–΄ μ†Œν”„νŠΈμ›¨μ–΄ μ—…λ°μ΄νŠΈλ‘œ 인해 데이터가 μ‹€μˆ˜λ‘œ λ³€κ²½λ˜κ±°λ‚˜ μ‚­μ œλ˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€.

ACID - μœ„ν‚€λ°±κ³Ό, 우리 λͺ¨λ‘μ˜ 백과사전

3. νŠΈλžœμž­μ…˜ κ³Όμ •

  1. νŠΈλžœμž­μ…˜ μ‹œμž‘
  2. λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€ν–‰(μ—¬λŸ¬ 쿼리듀이 μ‹€ν–‰) (DBλ‚΄ 갱신이 아직 μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€)
  3. νŠΈλžœμž­μ…˜ 컀밋 (νŠΈλžœμž­μ…˜μ΄ 성곡적이며, 갱신이 μ‹€μ œ 적용됨)

λ§Œμ•½ 쿼리 ν•˜λ‚˜κ°€ μ‹€νŒ¨ν•˜λ©΄, λ°μ΄ν„°λ² μ΄μŠ€ μ‹œμŠ€ν…œμ€ 전체 νŠΈλžœμž­μ…˜ λ˜λŠ” μ‹€νŒ¨ν•œ 쿼리λ₯Ό λ‘€λ°±ν•œλ‹€.

4. νŠΈλžœμž­μ…”λ„ λ°μ΄ν„°λ² μ΄μŠ€

νŠΈλžœμž­μ…˜μ„ μ§€μ›ν•˜λŠ” λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό νŠΈλžœμž­μ…”λ„ λ°μ΄ν„°λ² μ΄μŠ€(transactional database)라고 λΆ€λ₯Έλ‹€.

ν˜„μž¬ λŒ€λΆ€λΆ„μ˜ κ΄€κ³„ν˜• λ°μ΄ν„°λ² μ΄μŠ€ κ΄€λ¦¬μ‹œμŠ€ν…œμ€ νŠΈλžœμž­μ…˜ λ°μ΄ν„°λ² μ΄μŠ€μ΄λ‹€.

λ°μ΄ν„°λ² μ΄μŠ€ νŠΈλžœμž­μ…˜ - μœ„ν‚€λ°±κ³Ό, 우리 λͺ¨λ‘μ˜ 백과사전

5. μš”μ•½

νŠΉμ • μ‹€ν–‰λ‹¨μœ„μ—μ„œ 였λ₯˜ λ°œμƒμ‹œ 전체 μ‹€ν–‰ λ‚΄μš©μ„ λ‘€λ°±ν•΄μ£ΌλŠ” κΈ°λŠ₯

순수 JDBCTemplete

Connection connection=dataSource.getConnection()
    try(connection){
        connection.setAutoCommit(false);
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μˆ˜ν–‰
        connection.commit();
    }catch(SQLException e){
        connection.rollback();
    }finally{
        connection.close()
}

1. κ°œλ…

  1. setAutoCommit(false) λ₯Ό 톡해 νŠΈλžœμž­μ…˜μ„ 직접 관리할 수 있게 λ˜μ—ˆλ‹€.
  2. 이에 따라 κΈ°λŠ₯듀이 μˆ˜ν–‰λœ λ’€ 컀밋을 ν•˜κ³ , μ˜ˆμ™Έ λ°œμƒμ‹œ 둀백을 ν•˜κ³  μžˆλ‹€.

2. 문제점

  1. Connection 였브젝트λ₯Ό ν•œλ²ˆ 생성 ν›„ 계속 λ©”μ†Œλ“œμ˜ νŒŒλΌλ―Έν„°λ‘œ μ „λ‹¬ν•˜λ‹€κ°€ DAOλ₯Ό ν˜ΈμΆœν•  λ•Œ μ‚¬μš©ν•œλ‹€
    1. β†’ Spring νŠΈλžœμž­μ…˜ λ™κΈ°ν™”λ‘œ ν•΄κ²°!
  2. 데이터 μ ‘κ·Ό κΈ°μˆ μ— 의쑴적인 μ½”λ“œ
    1. β†’ Spring νŠΈλžœμž­μ…˜ μΆ”μƒν™”λ‘œ ν•΄κ²°!
  3. 순수 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§κ³ΌλŠ” λ‹€λ₯Έ κ΄€μ‹¬μ‚¬μ˜ 일(DB μ ‘κ·Ό κ΄€λ ¨)을 μˆ˜ν–‰ν•œλ‹€. μ½”λ“œκ°€ κΉ”λ”ν•˜μ§€ μ•Šλ‹€.
    1. β†’ Spring 선언적 νŠΈλžœμž­μ…˜λ‘œ ν•΄κ²°!

Spring Transaction

1. νŠΈλžœμž­μ…˜ 동기화

1-1. κ°œλ…

  1. Connection 였브젝트λ₯Ό νŠΉλ³„ν•œ μ €μž₯μ†Œμ— 보관해두고,
  2. 이후에 DAO의 λ©”μ†Œλ“œμ—μ„œ νŠΈλžœμž­μ…˜μ΄ ν•„μš”ν•  λ•Œ
  3. μ €μž₯된 Connection을 κ°€μ Έλ‹€κ°€ μ‚¬μš©ν•œλ‹€.

1-2. TransactionSynchronizationManager

νŠΈλžœμž­μ…˜ 동기화 μ €μž₯μ†Œ

  • μž‘μ—… μŠ€λ ˆλ“œλ§ˆλ‹€ λ…λ¦½μ μœΌλ‘œ Connection 였브젝트λ₯Ό μ €μž₯ν•˜κ³  관리
  • β†’ 닀쀑 μ‚¬μš©μžλ₯Ό μ²˜λ¦¬ν•˜λŠ” μ„œλ²„μ˜ λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλ„ 좩돌 x

이제 μ—¬λŸ¬ 쿼리λ₯Ό ν•œ 둜컬 νŠΈλžœμž­μ…˜μ— 관리 κ°€λŠ₯ν•˜λ‹€.

νŒŒλΌλ―Έν„°λ„ μ œκ±°ν•  수 μžˆλ‹€.

private DeataSource dataSource;

// Connection을 생성할 λ•Œ μ‚¬μš©ν•  DataSourceλ₯Ό DI λ°›λŠ”λ‹€
public void setDataSource(DataSource dataSource){
    this.dataSource=dataSource;
}

public void method()throws Exception{
    // 1. 동기화 μž‘μ—… μ΄ˆκΈ°ν™”
    TransactionSynchronizationManager.initSynchronization();
    // 2. DB 컀λ„₯μ…˜μ„ μƒμ„±ν•˜κ³  νŠΈλžœμž­μ…˜μ„ μ‹œμž‘
    Connection c=DataSourceUtils.getConnection(dataSource);
    c.setAuthCommit(false);

    try{
        // 3. λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μˆ˜ν–‰
        c.commit();
    }catch(Exception e){
        c.rollback();
        throw e;
    }finally{
        DataSourceUtils.releaseConnection(c,dataSource);
    // 4. 동기화 μž‘μ—… μ’…λ£Œ 및 정리
        TransactionSynchronizationManager.unbindResource(this.dataSource);
        TransactionSynchronizationManager.clearSynchronization();
    }
}

1-3. 문제점

DataSourceUtils 을 μ‚¬μš©ν•΄ Connection 였브젝트λ₯Ό κ°€μ Έμ˜¨λ‹€. 즉, 아직 Data μ ‘κ·Ό κΈ°μˆ μ— μ˜μ‘΄μ μ΄λ‹€.

2. νŠΈλžœμž­μ…˜ 좔상화

image

2-1. κ°œλ…

μŠ€ν”„λ§μ€ Data μ ‘κ·Ό κΈ°μˆ μ— 독립적인 νŠΈλžœμž­μ…˜μ„ μ‚¬μš© ν•  수 μžˆλ‹€.

기술 λ³„λ‘œ κ°€μ Έμ˜€λŠ” νŠΈλžœμž­μ…˜ ν˜•νƒœλ§Œ λ‹€λ₯Ό 뿐, νŠΈλžœμž­μ…˜ κΈ°λŠ₯은 κ°™κΈ° λ•Œλ¬Έμ— 좔상화가 κ°€λŠ₯ν•˜λ‹€.

2-2. PlatformTransactionManager

JPA, JDBC λ“± λ‹€μ–‘ν•œ 기술의 νŠΈλžœμž­μ…˜μ„ 좔상화

  • κΈ°μˆ μ— 독립적이닀
  • νŠΈλžœμž­μ…˜ 경계섀정이 κ°€λŠ₯ν•΄μ‘Œλ‹€
public void method()throws Exception{
    // 1.μ‚¬μš©ν•  DB(JDBC)의 DataSourceλ₯Ό μƒμ„±μžμ— λ„£μ–΄ νŠΈλžœμž­μ…˜ 좔상 였브젝트 생성
    PlatformTransactionManager transactionManager=new DataSourceTransactionManager(dataSource);
    TransactionStatus status=transactionManager.getTransaction(new DefaultTransactionDefinition());  //νŠΈλžœμž­μ…˜ μ‹œμž‘ 

    try{
        // 2. λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μˆ˜ν–‰
        transactionManager.commit(status);
    }catch(Exception e){
        transactionManager.rollback(status);
        throw e;
    }
}

2-3. JDBC λŒ€μ‹  JPAλ₯Ό μ΄μš©ν•˜λŠ” 법

PlatformTransactionManager transactionManager=new DataSourceTransactionManager(dataSource);

μ—μ„œ

PlatformTransactionManager transactionManager=new JpaTransactionManager();

μ΄λ ‡κ²Œ λ°”κΏ”μ£ΌκΈ°λ§Œ ν•˜λ©΄ λœλ‹€.

2-4. 문제점

μ—¬μ „νžˆ 순수 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§κ³ΌλŠ” λ‹€λ₯Έ κ΄€μ‹¬μ‚¬μ˜ 일(DB μ ‘κ·Ό κ΄€λ ¨)을 μˆ˜ν–‰ν•œλ‹€. μ½”λ“œκ°€ κΉ”λ”ν•˜μ§€ μ•Šλ‹€.

3. 선언적 νŠΈλžœμž­μ…˜ (@Transactional)

3-1. κ°œλ…

μ†ŒμŠ€μ½”λ“œμ— 직접 νŠΈλžœμž­μ…˜ κ΄€λ ¨ λ‘œμ§μ„ 넣어두지 μ•Šκ³  λΉ„μ§€λ‹ˆμŠ€ λ‘œμ§μ—μ„œ μ™„μ „νžˆ λΆ„λ¦¬ν•˜λŠ” 방식이닀.

이 방식을 μ‚¬μš©ν•˜λ©΄ ν”„λ‘œκ·Έλž˜λ°μ— μ˜ν•œ νŠΈλžœμž­μ…˜μ—μ„œ λ‚˜μ˜¨ 단점인 νŠΈλžœμž­μ…˜ μ½”λ“œ 쀑볡 문제, μ†ŒμŠ€μ½”λ“œ μœ μ§€λ³΄μˆ˜μ˜ 문제λ₯Ό λͺ¨λ‘ ν•΄κ²°ν•  수 μžˆλ‹€.

νŠΈλžœμž­μ…˜μ΄λΌλŠ” νš‘λ‹¨ 관심사λ₯Ό λΉ„μ§€λ‹ˆμŠ€ λ‘œμ§μ—μ„œ μ™„μ „νžˆ λΆ„λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— SRP κ΄€μ μ—μ„œ λ΄€μ„λ•Œλ„ μ ν•©ν•˜κ³  λ§Žμ€ μ–‘μ˜ νŠΈλžœμž­μ…˜ λ‘œμ§μ„ μ μš©ν•˜κΈ°μ—λ„ 합리적이닀.

3-2. κ΅¬ν˜„λ°©μ‹

Springμ—μ„œλŠ” AOPλ₯Ό μ‚¬μš©ν•΄μ„œ 선언적 νŠΈλžœμž­μ…˜μ„ κ΅¬ν˜„ν•˜κ³  μžˆλ‹€.

  1. AbstractPlatformTransactionManagerμ—μ„œ νŒ©ν† λ¦¬ λ©”μ„œλ“œ νŒ¨ν„΄μ„ μ‚¬μš©ν•΄μ„œ μ—¬λŸ¬ PlatformTransactionManager을 ν™•μž₯ν•œλ‹€.
  2. TransactionInterceptor λ₯Ό 톡해 νŠΈλžœμž­μ…˜ 경계λ₯Ό μ„€μ •ν•˜κ³  μ—¬κΈ°μ—μ„œ μ£Όμž…λœ PlatformTransactionManager λ₯Ό μ‚¬μš©ν•œλ‹€.
  3. SpringTransactionAnnotationParser λ₯Ό 톡해 @Transactional κ΄€λ ¨ 속성을 νŒŒμ‹±ν•œλ‹€.
  4. AbstractAutoProxyCreator 에 μ˜ν•΄ Proxy둜 μƒμ„±λ˜κ³  μ‹€μ œ ν΄λΌμ΄μ–ΈνŠΈκ°€ 타깃에 μ ‘κ·Όν• λ•ŒλŠ” Proxyλ₯Ό 거쳐 TransactionInterceptor λ₯Ό μ‚¬μš©ν•΄ νŠΈλžœμž­μ…˜μ„ μ—΄κ³ 
    νƒ€κΉƒμ˜ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜κ³  컀밋, 둀백을 μˆ˜ν–‰ν•œλ‹€β€¦.

μ€‘μš”ν•œκ±΄, 이제 μ–΄λ…Έν…Œμ΄μ…˜μœΌλ‘œ νŠΈλžœμž­μ…˜ 관리가 κ°€λŠ₯ν•΄μ‘Œλ‹€λŠ” 것이닀!!

4. @Transactional

  • 클래슀 주석 λ¬Έμ„œ λ²ˆμ—­

    /*
    κ°œλ³„ λ©”μ„œλ“œ λ˜λŠ” 클래슀의 νŠΈλžœμž­μ…˜ νŠΉμ„±μ„ μ„€λͺ…ν•©λ‹ˆλ‹€.
    
    이 주석이 클래슀 μˆ˜μ€€μ—μ„œ μ„ μ–Έλ˜λ©΄ μ„ μ–Έν•˜λŠ” 클래슀 및 ν•΄λ‹Ή ν•˜μœ„ 클래슀의 λͺ¨λ“  λ©”μ„œλ“œμ— κΈ°λ³Έκ°’μœΌλ‘œ μ μš©λ©λ‹ˆλ‹€. 클래슀 κ³„μΈ΅μ˜ μƒμœ„ ν΄λž˜μŠ€μ—λŠ” μ μš©λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μƒμ†λœ λ©”μ„œλ“œλŠ” ν•˜μœ„ 클래슀 μˆ˜μ€€ 주석에 μ°Έμ—¬ν•˜κΈ° μœ„ν•΄ λ‘œμ»¬μ—μ„œ λ‹€μ‹œ μ„ μ–Έν•΄μ•Ό ν•©λ‹ˆλ‹€. λ©”μ†Œλ“œ κ°€μ‹œμ„± μ œμ•½μ— λŒ€ν•œ μžμ„Έν•œ λ‚΄μš©μ€ μ°Έμ‘° λ§€λ‰΄μ–Όμ˜ νŠΈλžœμž­μ…˜ 관리 μ„Ήμ…˜μ„ μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.
    
    이 주석은 일반적으둜 Spring의 org.springframework.transaction.interceptor.RuleBasedTransactionAttribute ν΄λž˜μŠ€μ™€ 직접 비ꡐ할 수 있으며 μ‹€μ œλ‘œ AnnotationTransactionAttributeSourceλŠ” 이 μ£Όμ„μ˜ 속성을 RuleBasedTransactionAttribute의 μ†μ„±μœΌλ‘œ 직접 λ³€ν™˜ν•˜λ―€λ‘œ Spring의 νŠΈλžœμž­μ…˜ 지원 μ½”λ“œλŠ” 주석에 λŒ€ν•΄ μ•Œ ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.
    
    Attribute Semantics
    
    이 주석에 μ‚¬μš©μž 지정 λ‘€λ°± κ·œμΉ™μ΄ κ΅¬μ„±λ˜μ§€ μ•Šμ€ 경우 νŠΈλžœμž­μ…˜μ€ RuntimeException 및 Error μ‹œ λ‘€λ°±λ˜μ§€λ§Œ ν™•μΈλœ μ˜ˆμ™Έμ—μ„œλŠ” λ‘€λ°±λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
    
    λ‘€λ°± κ·œμΉ™μ€ μ§€μ •λœ μ˜ˆμ™Έκ°€ throw될 λ•Œ νŠΈλžœμž­μ…˜μ„ λ‘€λ°±ν•΄μ•Ό ν•˜λŠ”μ§€ μ—¬λΆ€λ₯Ό κ²°μ •ν•˜λ©° κ·œμΉ™μ€ νŒ¨ν„΄μ„ 기반으둜 ν•©λ‹ˆλ‹€. νŒ¨ν„΄μ€ ν˜„μž¬ μ™€μΌλ“œμΉ΄λ“œ 지원이 μ—†λŠ” μ •κ·œν™”λœ 클래슀 이름 λ˜λŠ” μ˜ˆμ™Έ μœ ν˜•(Throwable의 ν•˜μœ„ ν΄λž˜μŠ€μ—¬μ•Ό 함)에 λŒ€ν•œ μ •κ·œν™”λœ 클래슀 μ΄λ¦„μ˜ ν•˜μœ„ λ¬Έμžμ—΄μΌ 수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ "javax.servlet.ServletException" λ˜λŠ” "ServletException" 값은 javax.servlet.ServletException 및 ν•΄λ‹Ή ν•˜μœ„ ν΄λž˜μŠ€μ™€ μΌμΉ˜ν•©λ‹ˆλ‹€.
    
    λ‘€λ°± κ·œμΉ™μ€ 각각 νŒ¨ν„΄μ„ 클래슀 μ°Έμ‘° λ˜λŠ” λ¬Έμžμ—΄λ‘œ 지정할 수 μžˆλ„λ‘ ν•˜λŠ” rollbackFor/noRollbackFor 및 rollbackForClassName/noRollbackForClassName을 톡해 ꡬ성할 수 μžˆμŠ΅λ‹ˆλ‹€. μ˜ˆμ™Έ μœ ν˜•μ΄ 클래슀 참쑰둜 μ§€μ •λ˜λ©΄ μ •κ·œν™”λœ 이름이 νŒ¨ν„΄μœΌλ‘œ μ‚¬μš©λ©λ‹ˆλ‹€. 결과적으둜 @Transactional(rollbackFor = example.CustomException.class)은 @Transactional(rollbackForClassName = "example.CustomException")κ³Ό λ™μΌν•©λ‹ˆλ‹€.
    
    κ²½κ³ : νŒ¨ν„΄μ΄ μ–Όλ§ˆλ‚˜ ꡬ체적인지, νŒ¨ν‚€μ§€ 정보λ₯Ό 포함할지 μ—¬λΆ€λ₯Ό μ‹ μ€‘ν•˜κ²Œ κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€
    (ν•„μˆ˜ 사항은 μ•„λ‹˜). 
    예λ₯Ό λ“€μ–΄ "μ˜ˆμ™Έ"λŠ” 거의 λͺ¨λ“  ν•­λͺ©κ³Ό μΌμΉ˜ν•˜λ©° λ‹€λ₯Έ κ·œμΉ™μ„ 숨길 수 μžˆμŠ΅λ‹ˆλ‹€. "Exception"이 κ²€μ‚¬λœ λͺ¨λ“  μ˜ˆμ™Έμ— λŒ€ν•œ κ·œμΉ™μ„ μ •μ˜ν•˜λ €λŠ” 경우 "java.lang.Exception"이 μ •ν™•ν•©λ‹ˆλ‹€. "BaseBusinessException"κ³Ό 같은 보닀 κ³ μœ ν•œ μ˜ˆμ™Έ 이름을 μ‚¬μš©ν•˜λ©΄ μ˜ˆμ™Έ νŒ¨ν„΄μ— λŒ€ν•΄ μ™„μ „ν•œ 클래슀 이름을 μ‚¬μš©ν•  ν•„μš”κ°€ 없을 κ²ƒμž…λ‹ˆλ‹€. 
    λ˜ν•œ λ‘€λ°± κ·œμΉ™μœΌλ‘œ 인해 μœ μ‚¬ν•œ μ΄λ¦„μ˜ μ˜ˆμ™Έ 및 쀑첩 ν΄λž˜μŠ€μ— λŒ€ν•΄ μ˜λ„ν•˜μ§€ μ•Šμ€ μΌμΉ˜κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” throw된 μ˜ˆμ™Έμ˜ 이름에 λ‘€λ°± κ·œμΉ™μ— λŒ€ν•΄ κ΅¬μ„±λœ μ˜ˆμ™Έ νŒ¨ν„΄μ΄ ν¬ν•¨λœ 경우 throw된 μ˜ˆμ™Έκ°€ μ§€μ •λœ λ‘€λ°± κ·œμΉ™κ³Ό μΌμΉ˜ν•˜λŠ” κ²ƒμœΌλ‘œ κ°„μ£Όλ˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 
    예λ₯Ό λ“€μ–΄, com.example.CustomExceptionμ—μ„œ μΌμΉ˜ν•˜λ„λ‘ κ΅¬μ„±λœ κ·œμΉ™μ΄ 주어지면 ν•΄λ‹Ή κ·œμΉ™μ€ com.example.CustomExceptionV2λΌλŠ” μ˜ˆμ™Έ(CustomExceptionκ³Ό λ™μΌν•œ νŒ¨ν‚€μ§€μ— μžˆμ§€λ§Œ μΆ”κ°€ 접미사가 μžˆλŠ” μ˜ˆμ™Έ) λ˜λŠ” comμ΄λΌλŠ” μ˜ˆμ™Έμ™€ μΌμΉ˜ν•©λ‹ˆλ‹€. example.CustomException$AnotherException(CustomExceptionμ—μ„œ 쀑첩 클래슀둜 μ„ μ–Έλœ μ˜ˆμ™Έ).
    
    이 주석에 μžˆλŠ” λ‹€λ₯Έ μ†μ„±μ˜ μ˜λ―Έμ— λŒ€ν•œ νŠΉμ • μ •λ³΄λŠ” TransactionDefinition 및 org.springframework.transaction.interceptor.TransactionAttribute javadoc을 μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.
    
    **νŠΈλžœμž­μ…˜ 관리**
    이 주석은 일반적으둜 org.springframework.transaction.PlatformTransactionManager에 μ˜ν•΄ κ΄€λ¦¬λ˜λŠ” μŠ€λ ˆλ“œ λ°”μš΄λ“œ νŠΈλžœμž­μ…˜κ³Ό ν•¨κ»˜ μž‘λ™ν•˜μ—¬ ν˜„μž¬ μ‹€ν–‰ μŠ€λ ˆλ“œ λ‚΄μ˜ λͺ¨λ“  데이터 μ•‘μ„ΈμŠ€ μž‘μ—…μ— νŠΈλžœμž­μ…˜μ„ λ…ΈμΆœν•©λ‹ˆλ‹€. μ°Έκ³ : 이것은 λ©”μ„œλ“œ λ‚΄μ—μ„œ μƒˆλ‘œ μ‹œμž‘λœ μŠ€λ ˆλ“œλ‘œ μ „νŒŒλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
    λ˜λŠ” 이 주석은 μŠ€λ ˆλ“œ 둜컬 λ³€μˆ˜ λŒ€μ‹  Reactor μ»¨ν…μŠ€νŠΈλ₯Ό μ‚¬μš©ν•˜λŠ” org.springframework.transaction.ReactiveTransactionManager에 μ˜ν•΄ κ΄€λ¦¬λ˜λŠ” λ°˜μ‘ νŠΈλžœμž­μ…˜μ„ ꡬ뢄할 수 μžˆμŠ΅λ‹ˆλ‹€. 결과적으둜 λͺ¨λ“  μ°Έμ—¬ 데이터 μ•‘μ„ΈμŠ€ μž‘μ—…μ€ λ™μΌν•œ λ¦¬μ•‘ν‹°λΈŒ νŒŒμ΄ν”„λΌμΈμ˜ λ™μΌν•œ Reactor μ»¨ν…μŠ€νŠΈ λ‚΄μ—μ„œ μ‹€ν–‰λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
    
    Since:1.2
    See Also :org.springframework.transaction.interceptor.TransactionAttribute, org.springframework.transaction.interceptor.DefaultTransactionAttribute, org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
    μž‘κ°€:콜린 μ‚ΌνŒ”λ ˆμ•„λˆ„, 유λ₯΄κ² ν™€λŸ¬, μƒ˜ λΈŒλž˜λ„Œ, 마크 νŒ”λ£¨μΉ˜
    */
    

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    // μ‚¬μš©ν•  νŠΈλžœμž­μ…˜ κ΄€λ¦¬μž
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    String[] label() default {};

    // 선택적 μ „νŒŒ μ„€μ •
    Propagation propagation() default Propagation.REQUIRED;

    // 선택적 격리 μˆ˜μ€€
    Isolation isolation() default Isolation.DEFAULT;

    // νŠΈλžœμž­μ…˜ νƒ€μž„ 아웃
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    String timeoutString() default "";

    // 읽기/μ“°κΈ° vs 읽기 μ „μš© νŠΈλžœμž­μ…˜
    boolean readOnly() default false;

    // 둀백이 μˆ˜ν–‰λ˜μ–΄μ•Ό ν•˜λŠ”, 선택적인 μ˜ˆμ™Έ 클래슀의 λ°°μ—΄
    Class<? extends Throwable>[] rollbackFor() default {};

    // 둀백이 μˆ˜ν–‰λ˜μ–΄μ•Ό ν•˜λŠ”, 선택적인 μ˜ˆμ™Έ 클래슀 μ΄λ¦„μ˜ λ°°μ—΄
    String[] rollbackForClassName() default {};

    // 둀백이 μˆ˜ν–‰λ˜μ§€ μ•Šμ•„μ•Ό ν•˜λŠ”, 선택적인 μ˜ˆμ™Έ 클래슀의 λ°°μ—΄
    Class<? extends Throwable>[] noRollbackFor() default {};

    // 둀백이 μˆ˜ν–‰λ˜μ§€ μ•Šμ•„μ•Ό ν•˜λŠ”, 선택적인 μ˜ˆμ™Έ 클래슀 μ΄λ¦„μ˜ λ°°μ—΄
    String[] noRollbackForClassName() default {};

}

4-1. νŠΈλžœμž­μ…˜μ˜ 격리 μˆ˜μ€€ (Isolation Level)

  1. DEFAULT :데이터 λ² μ΄μŠ€μ—μ„œ μ„€μ •λœ κΈ°λ³Έ 격리 μˆ˜μ€€μ„ λ”°λ¦…λ‹ˆλ‹€.

  2. READ_UNCOMMITED: νŠΈλžœμž­μ…˜μ΄ 아직 μ»€λ°‹λ˜μ§€ μ•Šμ€ 데이터λ₯Ό 읽을 수 μžˆμŠ΅λ‹ˆλ‹€.

  3. READ_COMMITED: Dirty Read λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ Commit 된 λ°μ΄ν„°λ§Œ 읽을 수 μžˆμŠ΅λ‹ˆλ‹€.

  4. REPEATABLE READ: νŠΈλžœμž­μ…˜μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ μ‘°νšŒν•œ λͺ¨λ“  데이터에 shared lock이 κ±Έλ¦¬λ―€λ‘œ νŠΈλžœμž­μ…˜μ΄ μ’…λ£Œλ  λ•ŒκΉŒμ§€ λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ€ κ·Έ μ˜μ—­μ— ν•΄λ‹Ήν•˜λŠ” 데이터λ₯Ό μˆ˜μ •ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

  5. SERIALIZABLE: κ°€μž₯ μ—„κ²©ν•œ νŠΈλžœμž­μ…˜ κ²©λ¦¬μˆ˜μ€€μœΌλ‘œ, μ™„λ²½ν•œ 읽기 일관성 λͺ¨λ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. 이 격리 μˆ˜μ€€μ—μ„œλŠ” PHANTOM READ μƒνƒœκ°€ λ°œμƒν•˜μ§€ μ•Šμ§€λ§Œ λ™μ‹œμ„± 처리 μ„±λŠ₯이 κΈ‰κ²©νžˆ λ–¨μ–΄μ§ˆ 수
    μžˆμŠ΅λ‹ˆλ‹€.

4-2. νŠΈλžœμž­μ…˜ μ „νŒŒ μ˜΅μ…˜ (Propagation)

  1. REQUIRED : 이미 μ‹œμž‘λœ νŠΈλžœμž­μ…˜μ΄ 있으면 μ°Έμ—¬ν•˜κ³ , μ—†μœΌλ©΄ μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•©λ‹ˆλ‹€. (λ””ν΄νŠΈ 속성)
  2. SUPPORTS : 이미 μ‹œμž‘λœ νŠΈλžœμž­μ…˜μ΄ 있으면 μ°Έμ—¬ν•˜κ³ , μ—†μœΌλ©΄ νŠΈλžœμž­μ…˜ 없이 μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  3. REQUIRED_NEW : 항상 μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•©λ‹ˆλ‹€. 이미 진행쀑인 νŠΈλžœμž­μ…˜μ΄ μžˆλ‹€λ©΄ μž μ‹œ 보λ₯˜μ‹œν‚΅λ‹ˆλ‹€.
  4. MANDATORY : 이미 μ‹œμž‘λœ νŠΈλžœμž­μ…˜μ΄ 있으면 μ°Έμ—¬ν•˜κ³ , μ—†μœΌλ©΄ μƒˆλ‘œμš΄ νŠΈλžœμƒ‰μ…˜μ„ μ‹œμž‘ν•˜λŠ” λŒ€μ‹  μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œν‚΅λ‹ˆλ‹€. ν˜Όμžμ„œλŠ” λ…λ¦½μ μœΌλ‘œ μˆ˜ν–‰λ˜λ©΄ μ•ˆλ˜λŠ” κ²½μš°μ— μ‚¬μš©λ©λ‹ˆλ‹€.
  5. NOT_SUPPORTED : νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•˜μ§€ μ•Šκ³  μ²˜λ¦¬ν•˜λ„λ‘ ν•©λ‹ˆλ‹€. 이미 진행쀑인 νŠΈλžœμž­μ…˜μ΄ μžˆλ‹€λ©΄ μž μ‹œ 보λ₯˜μ‹œν‚΅λ‹ˆλ‹€.
  6. NEVER : νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•˜μ§€ μ•Šλ„λ‘ κ°•μ œμ‹œν‚΅λ‹ˆλ‹€. 이미 진행쀑인 νŠΈλžœμž­μ…˜ λ˜ν•œ ν—ˆμš©ν•˜μ§€ μ•ŠμœΌλ©°, μžˆλ‹€λ©΄ μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œν‚΅λ‹ˆλ‹€.
  7. NESTED : 이미 싀행쀑인 νŠΈλžœμž­μ…˜μ΄ μžˆλ‹€λ©΄ μ€‘μ²©ν•˜μ—¬ νŠΈλžœμž­μ…˜μ„ μ§„ν–‰ν•©λ‹ˆλ‹€. λΆ€λͺ¨ νŠΈλžœμž­μ…˜μ€ 쀑첩 νŠΈλžœμž­μ…˜μ— 영ν–₯을 μ£Όμ§€λ§Œ 쀑첩 νŠΈλžœμž­μ…˜μ€ λΆ€λͺ¨ νŠΈλžœμž­μ…˜μ— 영ν–₯을 주지 μ•ŠμŠ΅λ‹ˆλ‹€.

4-3. readOnly μ˜΅μ…˜

  • readOnly 속성을 톡해 νŠΈλžœμž­μ…˜μ„ 읽기 μ „μš©μœΌλ‘œ μ„€μ •ν•  수 μžˆλ‹€.
  • JPA의 경우, ν•΄λ‹Ή μ˜΅μ…˜μ„ true 둜 μ„€μ •ν•˜κ²Œ 되면 νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λ˜μ–΄λ„ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό ν”ŒλŸ¬μ‹œν•˜μ§€ μ•ŠλŠ”λ‹€. ν”ŒλŸ¬μ‹œν•  λ•Œ μˆ˜ν–‰λ˜λŠ” μ—”ν‹°ν‹°μ˜ μŠ€λƒ…μƒ· 비ꡐ 둜직이 μˆ˜ν–‰λ˜μ§€ μ•ŠμœΌλ―€λ‘œ μ„±λŠ₯을 ν–₯상 μ‹œν‚¬ 수
    μžˆλ‹€.

참고자료

https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-declarative

https://jongmin92.github.io/2018/04/08/Spring/toby-5/#:~:text=νŠΈλžœμž­μ…˜ λ™κΈ°ν™”λž€ UserServiceμ—μ„œ,κ°€ μ‚¬μš©ν•˜κ²Œ ν•˜λŠ” 것이닀

https://programmer-study.tistory.com/19

https://sup2is.github.io/2021/11/11/about-spring-transaction.html

https://towardsdatascience.com/database-basics-acid-transactions-bf4d38bd8e26

μ˜ˆμƒ 질문/λ‹΅λ³€

1. μ„ μ–Έμ „ νŠΈλžœμž­μ…˜ 방식을 μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ”?

λΉ„μ¦ˆλ‹ˆμŠ€ 둜직이 νŠΈλžœμž­μ…˜ 처리λ₯Ό ν•„μš”λ‘œ ν•  λ•Œ, νŠΈλžœμž­μ…˜ 처리 μ½”λ“œμ™€ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직이 κ³΅μ‘΄ν•œλ‹€λ©΄ μ½”λ“œ 쀑볡이 λ°œμƒν•˜κ³  λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ— μ§‘μ€‘ν•˜κΈ° μ–΄λ ΅λ‹€. λ”°λΌμ„œ νŠΈλžœμž­μ…˜ μ²˜λ¦¬μ™€ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ 뢄리할 수 μžˆλŠ” 선언적
νŠΈλžœμž­μ…˜ 방식을 자주 μ‚¬μš©ν•œλ‹€.

2. @Transactional의 λ™μž‘ μ›λ¦¬λŠ”?

@Transactional을 λ©”μ†Œλ“œ λ˜λŠ” ν΄λž˜μŠ€μ— λͺ…μ‹œν•˜λ©΄

AOPλ₯Ό 톡해

Target이 μƒμ†ν•˜κ³  μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€ λ˜λŠ” Target 객체λ₯Ό μƒμ†ν•œ Proxy 객체가 μƒμ„±λ˜λ©°,

Proxy 객체의 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄

Target λ©”μ†Œλ“œ μ „ ν›„λ‘œ νŠΈλžœμž­μ…˜ 처리λ₯Ό μˆ˜ν–‰ν•œλ‹€.

3. @Transactional을 μ‚¬μš©ν•  λ•Œ μ£Όμ˜ν•  점은?

could not initialize proxy[com.wooteco.sokdak.hashtag.domain.Hashtag#3]-no Session
        org.hibernate.LazyInitializationException:could not initialize proxy[com.wooteco.sokdak.hashtag.domain.Hashtag#3]-no Session
        ...

Proxy 객체의 Target Methodκ°€ λ‚΄λΆ€ λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄ μ‹€μ œ λ©”μ†Œλ“œκ°€ 호좜되기 λ•Œλ¬Έμ—

Inner Methodμ—μ„œ @Transactional μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ μš©λ˜μ§€ μ•ŠλŠ” 것을 μ£Όμ˜ν•΄μ•Ό ν•œλ‹€.

@Transactional μ–΄λ…Έν…Œμ΄μ…˜μ„ 뢙이면 νŠΈλžœμž­μ…˜ 처리λ₯Ό μœ„ν•΄ Proxy 객체λ₯Ό μƒμ„±ν•˜λŠ”λ°,

ProxyλŠ” Target Classλ₯Ό μƒμ†ν•˜μ—¬ μƒμ„±λœλ‹€.

λ”°λΌμ„œ 상속이 λΆˆκ°€λŠ₯ν•œ Private λ©”μ†Œλ“œμ˜ 경우 @Transactional μ–΄λ…Έν…Œμ΄μ…˜μ„ μ μš©ν•  수 μ—†λ‹€.

4. Service 객체의 AλΌλŠ” λ©”μ†Œλ“œ λ‚΄λΆ€μ—μ„œ 둜컬 νŠΈλžœμž­μ…˜ 3κ°œκ°€ μ‘΄μž¬ν•œλ‹€κ³  ν•  λ•Œ, A λ©”μ†Œλ“œμ— @Transactional을 μ μš©ν•˜λ©΄ μ–΄λ–€ μš”μ²­ 흐름이 λ°œμƒν•˜λŠ”μ§€ μ„€λͺ…ν•˜λΌ.

νŠΈλžœμž­μ…˜ μ „νŒŒ μˆ˜μ€€μ— 따라 달라진닀.

λ§Œμ•½ κΈ°λ³Έ μ˜΅μ…˜μΈ REQUIREDλ₯Ό κ°€μ Έκ°„λ‹€λ©΄ 둜컬 νŠΈλžœμž­μ…˜ 3κ°œκ°€ λͺ¨λ‘ λΆ€λͺ¨ νŠΈλžœμž­μ…˜μΈ A에 ν•©λ₯˜ν•˜μ—¬ μˆ˜ν–‰λœλ‹€.

κ·Έλž˜μ„œ λΆ€λͺ¨ νŠΈλžœμž­μ…˜μ΄λ‚˜ 둜컬 νŠΈλžœμž­μ…˜ 3κ°œλ‚˜ λͺ¨λ‘ 같은 νŠΈλžœμž­μ…˜μ΄λ―€λ‘œ μ–΄λŠ ν•˜λ‚˜μ˜ λ‘œμ§μ—μ„œλ“  λ¬Έμ œκ°€ λ°œμƒν•˜λ©΄ μ „λΆ€ 둀백이 λœλ‹€.

profile
🌱 ν•¨κ»˜ μžλΌλŠ” μ€‘μž…λ‹ˆλ‹€ πŸš€ rerub0831@gmail.com

0개의 λŒ“κΈ€