πŸ“Œ Java νŠΈλžœμž­μ…˜(Transaction) κ°œλ… 및 ν™œμš©

My Pale Blue DotΒ·2025λ…„ 3μ›” 18일
0

JAVA

λͺ©λ‘ 보기
25/35
post-thumbnail

πŸ“… μž‘μ„±μΌ: 2025-03-18

πŸ“ 1. νŠΈλžœμž­μ…˜(Transaction) κ°œλ…

βœ… (1) νŠΈλžœμž­μ…˜μ΄λž€?

νŠΈλžœμž­μ…˜(Transaction)μ΄λž€ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‹€ν–‰λ˜λŠ” ν•˜λ‚˜μ˜ 논리적인 μž‘μ—… λ‹¨μœ„λ₯Ό μ˜λ―Έν•œλ‹€.
즉, μ—¬λŸ¬ 개의 SQL 연산이 ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μœΌλ‘œ λ¬Άμ—¬ μ‹€ν–‰λ˜λ©°,
λͺ¨λ“  μž‘μ—…μ΄ 성곡해야 μ΅œμ’…μ μœΌλ‘œ 컀밋(commit)되고,
ν•˜λ‚˜λΌλ„ μ‹€νŒ¨ν•˜λ©΄ λ‘€λ°±(rollback)λ˜μ–΄ μ›λž˜ μƒνƒœλ‘œ λ³΅κ΅¬λœλ‹€.

βœ… (2) νŠΈλžœμž­μ…˜μ˜ 4κ°€μ§€ νŠΉμ„± (ACID)

νŠΈλžœμž­μ…˜μ΄ μ˜¬λ°”λ₯΄κ²Œ λ™μž‘ν•˜λ €λ©΄ ACID 원칙을 λ§Œμ‘±ν•΄μ•Ό ν•œλ‹€.

νŠΉμ„±μ„€λͺ…
Atomicity (μ›μžμ„±)νŠΈλžœμž­μ…˜μ€ λͺ¨λ‘ μ„±κ³΅ν•˜κ±°λ‚˜, λͺ¨λ‘ μ‹€νŒ¨ν•΄μ•Ό 함
Consistency (일관성)νŠΈλžœμž­μ…˜μ΄ μ‹€ν–‰λœ ν›„ λ°μ΄ν„°λ² μ΄μŠ€κ°€ μΌκ΄€λœ μƒνƒœλ₯Ό μœ μ§€ν•΄μ•Ό 함
Isolation (격리성)μ—¬λŸ¬ νŠΈλžœμž­μ…˜μ΄ λ™μ‹œμ— 싀행될 λ•Œ μ„œλ‘œ 영ν–₯을 μ£Όμ§€ μ•Šμ•„μ•Ό 함
Durability (지속성)νŠΈλžœμž­μ…˜μ΄ μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ˜λ©΄ 데이터가 영ꡬ적으둜 λ°˜μ˜λ˜μ–΄μ•Ό 함

βœ… 2. νŠΈλžœμž­μ…˜ 적용 방식

πŸ”Ή (1) μžλ™ 컀밋(Auto Commit) λͺ¨λ“œ

  • JDBC의 κΈ°λ³Έ μ„€μ •μ—μ„œλŠ” SQL μ‹€ν–‰ ν›„ μžλ™μœΌλ‘œ commit이 μˆ˜ν–‰λ¨.
  • INSERT, UPDATE, DELETE μ‹€ν–‰ μ‹œ μžλ™μœΌλ‘œ 변경사항이 μ €μž₯됨.
// 기본적으둜 Auto Commit λͺ¨λ“œκ°€ ν™œμ„±ν™”λ¨
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO users (name, age) VALUES ('Alice', 25)"); 
// μžλ™μœΌλ‘œ commit() μˆ˜ν–‰λ¨

πŸ”Ή (2) μˆ˜λ™ 컀밋(Manual Commit) λͺ¨λ“œ

  • setAutoCommit(false)λ₯Ό μ‚¬μš©ν•˜μ—¬ μžλ™ 컀밋을 λΉ„ν™œμ„±ν™”ν•˜κ³ ,
    commit() λ˜λŠ” rollback()을 직접 ν˜ΈμΆœν•˜μ—¬ νŠΈλžœμž­μ…˜μ„ κ΄€λ¦¬ν•œλ‹€.
Connection conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false); // μžλ™ 컀밋 λΉ„ν™œμ„±ν™”

try {
    PreparedStatement pstmt1 = conn.prepareStatement("UPDATE accounts SET balance = balance - 1000 WHERE id = 1");
    pstmt1.executeUpdate();

    PreparedStatement pstmt2 = conn.prepareStatement("UPDATE accounts SET balance = balance + 1000 WHERE id = 2");
    pstmt2.executeUpdate();

    conn.commit(); // λͺ¨λ“  μž‘μ—…μ΄ μ„±κ³΅ν•˜λ©΄ commit μˆ˜ν–‰
} catch (SQLException e) {
    conn.rollback(); // 였λ₯˜ λ°œμƒ μ‹œ rollback μˆ˜ν–‰
} finally {
    conn.setAutoCommit(true); // νŠΈλžœμž­μ…˜ μ’…λ£Œ ν›„ Auto Commit 원상 볡ꡬ
}

πŸ“Œ νŠΈλžœμž­μ…˜μ΄ μ μš©λ˜μ§€ μ•ŠμœΌλ©΄ 첫 번째 SQL이 μ‹€ν–‰λœ ν›„ 였λ₯˜κ°€ λ°œμƒν•˜λ©΄, A κ³„μ’Œμ—μ„œλŠ” μΆœκΈˆλ˜μ—ˆμ§€λ§Œ, B κ³„μ’Œμ—λŠ” μž…κΈˆλ˜μ§€ μ•ŠλŠ” 문제 λ°œμƒ
πŸ“Œ νŠΈλžœμž­μ…˜μ„ μ μš©ν•˜λ©΄ ν•˜λ‚˜λΌλ„ μ‹€νŒ¨ν•˜λ©΄ λͺ¨λ“  μž‘μ—…μ΄ λ‘€λ°±(rollback) λ˜μ–΄ 데이터 정합성을 μœ μ§€ κ°€λŠ₯


βœ… 3. νŠΈλžœμž­μ…˜ 격리 μˆ˜μ€€ (Transaction Isolation Level)

νŠΈλžœμž­μ…˜ 격리 μˆ˜μ€€(Isolation Level)은
λ™μ‹œμ— μ—¬λŸ¬ νŠΈλžœμž­μ…˜μ΄ 싀행될 λ•Œ, λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ˜ 변경사항을 μ–Όλ§ˆλ‚˜ ν—ˆμš©ν• μ§€λ₯Ό κ²°μ •ν•œλ‹€.

격리 μˆ˜μ€€μ„€λͺ…λ°œμƒν•  수 μžˆλŠ” 문제
READ UNCOMMITTEDλ‹€λ₯Έ νŠΈλžœμž­μ…˜μ΄ commitλ˜μ§€ μ•Šμ€ 데이터도 읽을 수 있음Dirty Read λ°œμƒ κ°€λŠ₯
READ COMMITTEDλ‹€λ₯Έ νŠΈλžœμž­μ…˜μ΄ commitν•œ λ°μ΄ν„°λ§Œ 읽을 수 있음Non-repeatable Read λ°œμƒ κ°€λŠ₯
REPEATABLE READ같은 νŠΈλžœμž­μ…˜μ—μ„œ 같은 데이터λ₯Ό μ—¬λŸ¬ 번 읽어도 값이 λ³€ν•˜μ§€ μ•ŠμŒPhantom Read λ°œμƒ κ°€λŠ₯
SERIALIZABLEλͺ¨λ“  νŠΈλžœμž­μ…˜μ„ 순차적으둜 μ‹€ν–‰μ„±λŠ₯ μ €ν•˜ κ°€λŠ₯
// νŠΈλžœμž­μ…˜ 격리 μˆ˜μ€€ μ„€μ •
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

βœ… 4. JDBCμ—μ„œ νŠΈλžœμž­μ…˜ 적용 예제

β–Ά (1) 단일 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ νŠΈλžœμž­μ…˜ 적용

import java.sql.*;

public class TransactionExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "1234";

        Connection conn = null;

        try {
            conn = DriverManager.getConnection(url, user, password);
            conn.setAutoCommit(false); // μžλ™ 컀밋 λΉ„ν™œμ„±ν™”

            // 첫 번째 SQL μ‹€ν–‰ (A κ³„μ’Œμ—μ„œ 1000원 좜금)
            PreparedStatement pstmt1 = conn.prepareStatement("UPDATE accounts SET balance = balance - 1000 WHERE id = 1");
            pstmt1.executeUpdate();

            // 두 번째 SQL μ‹€ν–‰ (B κ³„μ’Œμ— 1000원 μž…κΈˆ)
            PreparedStatement pstmt2 = conn.prepareStatement("UPDATE accounts SET balance = balance + 1000 WHERE id = 2");
            pstmt2.executeUpdate();

            conn.commit(); // μ„±κ³΅ν•˜λ©΄ 컀밋
            System.out.println("βœ… νŠΈλžœμž­μ…˜ 성곡: A β†’ B κ³„μ’Œ 이체 μ™„λ£Œ");

        } catch (SQLException e) {
            try {
                if (conn != null) conn.rollback(); // 였λ₯˜ λ°œμƒ μ‹œ λ‘€λ°±
                System.out.println("β›” 였λ₯˜ λ°œμƒ: νŠΈλžœμž­μ…˜ λ‘€λ°± μˆ˜ν–‰");
            } catch (SQLException rollbackEx) {
                rollbackEx.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }
        }
    }
}

πŸ”₯ 5. 정리

βœ… νŠΈλžœμž­μ…˜(Transaction)은 μ—¬λŸ¬ 개의 SQL μž‘μ—…μ„ ν•˜λ‚˜μ˜ μž‘μ—… λ‹¨μœ„λ‘œ λ¬Άμ–΄μ„œ μ‹€ν–‰ν•˜λŠ” κΈ°λŠ₯
βœ… νŠΈλžœμž­μ…˜μ΄ 적용되면 commit()을 ν˜ΈμΆœν•΄μ•Ό λ³€κ²½ 사항이 반영되며, rollback()으둜 μ·¨μ†Œ κ°€λŠ₯
βœ… JDBCμ—μ„œ setAutoCommit(false), commit(), rollback()을 ν™œμš©ν•˜μ—¬ νŠΈλžœμž­μ…˜μ„ 적용 κ°€λŠ₯
βœ… XA νŠΈλžœμž­μ…˜μ„ ν™œμš©ν•˜λ©΄ μ—¬λŸ¬ 개의 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‹€ν–‰λ˜λŠ” SQL을 ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μœΌλ‘œ 묢을 수 있음

profile
Here, My Pale Blue.🌏

0개의 λŒ“κΈ€