Spring DB Part I

Spring DB Part I

์˜ํ•œ๋‹˜์˜ ์Šคํ”„๋ง DB 1ํŽธ - ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ํ•ต์‹ฌ ์›๋ฆฌ ๊ฐ•์˜๋ฅผ ์š”์•ฝํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

Project

Intro

H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •

Download

์‹คํ–‰

  • ์‹คํ–‰ ๊ถŒํ•œ: chmod 755 h2.sh

  • ์‹คํ–‰: ./h2.sh

  • mv.db ํŒŒ์ผ ์ƒ์„ฑ: jdbc:h2:~/test

  • ์ ‘์†: jdbc:h2:tcp://localhost/~/test

JDBC

Java Database Connectivity

  • ์ž๋ฐ”์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ์ž๋ฐ” API

Server <-> DB

  • Connection ์—ฐ๊ฒฐ: ์ฃผ๋กœ TCP/IP๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปค๋„ฅ์…˜ ์—ฐ๊ฒฐ

  • SQL ์ „๋‹ฌ: ์„œ๋ฒ„๋Š” DB๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” SQL์„ ์ปค๋„ฅ์…˜์œผ๋กœ DB์— ์ „๋‹ฌ

  • Response: DB๋Š” ์ „๋‹ฌ๋œ SQL์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์‘๋‹ต -> ์„œ๋ฒ„๋Š” ์‘๋‹ต ๊ฒฐ๊ณผ ํ™œ์šฉ

JDBC ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค

  • java.sql.Connection: ์—ฐ๊ฒฐ

  • java.sql.Statement: SQL์„ ๋‹ด์€ ๋‚ด์šฉ

  • java.sql.ResultSet: SQL ์š”์ฒญ ์‘๋‹ต

JDBC ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ 

  • SQL Mapper

    • Spring JdbcTemplate

    • MyBatis

  • ORM

    • JPA

    • hibernate

    • eclipse link

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ

  • JDBC๋Š” java.sql.Connection ํ‘œ์ค€ ์ปค๋„ฅ์…˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜

    • H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“œ๋ผ์ด๋ฒ„๋Š” JDBC Connection ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ org.h2.jdbc.JdbcConnection ๊ตฌํ˜„์ฒด ์ œ๊ณต

  • JDBC๊ฐ€ ์ œ๊ณตํ•˜๋Š” DriverManager ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋“ฑ๋ก๋œ DB ๋“œ๋ผ์ด๋ฒ„๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ , ์ปค๋„ฅ์…˜์„ ํš๋“ํ•˜๋Š” ๊ธฐ๋Šฅ ์ œ๊ณต

.

DriverManager ์ปค๋„ฅ์…˜ ์š”์ฒญ ํ๋ฆ„

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง์—์„œ ์ปค๋„ฅ์…˜์ด ํ•„์š”ํ•˜๋ฉด DriverManager.getConnection() ํ˜ธ์ถœ

  • DriverManager ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋“ฑ๋ก๋œ ๋“œ๋ผ์ด๋ฒ„ ๋ชฉ๋ก์„ ์ž๋™์œผ๋กœ ์ธ์‹

    • ๋“œ๋ผ์ด๋ฒ„๋“ค์—๊ฒŒ ์ˆœ์„œ๋Œ€๋กœ URL, d์ด๋ฆ„, ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ ์ ‘์†์ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋„˜๊ฒจ ์ปค๋„ฅ์…˜์„ ํš๋“ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธ

    • ๊ฐ๊ฐ์˜ ๋“œ๋ผ์ด๋ฒ„๋Š” URL ์ •๋ณด๋ฅผ ์ฒดํฌํ•ด์„œ ๋ณธ์ธ์ด ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์š”์ฒญ์ธ์ง€ ํ™•์ธ

  • ์ฐพ์€ ์ปค๋„ฅ์…˜ ๊ตฌํ˜„์ฒด๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜

    • ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ ๊ฒฝ์šฐ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—ฐ๊ฒฐํ•ด์„œ ์ปค๋„ฅ์…˜์„ ํš๋“ํ•˜๊ณ  ์ด ์ปค๋„ฅ์…˜์„ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜

    • ๋ฐ˜๋ฉด URL์ด jdbc:h2 ๋กœ ์‹œ์ž‘ํ–ˆ๋Š”๋ฐ MySQL ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ๋จผ์ € ์‹คํ–‰๋  ๊ฒฝ์šฐ, ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋˜๊ณ , ๋‹ค์Œ ๋“œ๋ผ์ด๋ฒ„์—๊ฒŒ ์ˆœ์„œ๊ฐ€ ์ „๋‹ฌ

commit


getConnection() & close()

DriverManager

@Slf4j
public class MemberRepository {
    private void close(Connection con, Statement stmt, ResultSet rs) {

        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                log.info("error", e);
            }
        }

        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                log.info("error", e);
            }
        }

        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                log.info("error", e);
            }
        }
    }

    private Connection getConnection() {
        return DBConnectionUtil.getConnection();
    }
}

commit

DataSource

@Slf4j
public class MemberRepository {

    private final DataSource dataSource;

    public MemberRepositoryV1(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    private void close(Connection con, Statement stmt, ResultSet rs) {
        JdbcUtils.closeResultSet(rs);
        JdbcUtils.closeStatement(stmt);
        JdbcUtils.closeConnection(con);
    }

    private Connection getConnection() throws SQLException {
        Connection con = dataSource.getConnection();
        log.info("get connection={}, class={}", con, con.getClass());
        return con;
    }
}

commit


๋“ฑ๋ก

@Slf4j
public class MemberRepository {

    public Member save(Member member) throws SQLException {
        String sql = "insert into member(member_id, money) values(?, ?)";

        Connection con = null;
        PreparedStatement pstmt = null;

        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql); // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „๋‹ฌํ•  SQL๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•  ๋ฐ์ดํ„ฐ๋“ค์„ ์ค€๋น„
            pstmt.setString(1, member.getMemberId());
            pstmt.setInt(2, member.getMoney());
            pstmt.executeUpdate(); // ์ค€๋น„๋œ SQL์„ ์ปค๋„ฅ์…˜์„ ํ†ตํ•ด ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์ „๋‹ฌ
            return member;
        } catch (SQLException e) {
            log.error("db error", e);
            throw e;
        } finally {
            close(con, pstmt, null);
        }
    }
}

์กฐํšŒ

public Member findById(String memberId) throws SQLException {
    String sql = "select * from member where member_id = ?";

    Connection con = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;

    try {
        con = getConnection();
        pstmt = con.prepareStatement(sql);
        pstmt.setString(1, memberId);

        rs = pstmt.executeQuery();
        if (rs.next()) {
            Member member = new Member();
            member.setMemberId(rs.getString("member_id"));
            member.setMoney(rs.getInt("money"));
            return member;
        } else {
            throw new NoSuchElementException("member not found memberId=" + memberId);
        }
    } catch (SQLException e) {
        log.error("db error", e);
        throw e;
    } finally {
        close(con, pstmt, rs);
    }
}

์ˆ˜์ •, ์‚ญ์ œ

public void update(String memberId, int money) throws SQLException {
    String sql = "update member set money=? where member_id=?";

    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        con = getConnection();
        pstmt = con.prepareStatement(sql);
        pstmt.setInt(1, money);
        pstmt.setString(2, memberId);
        int resultSize = pstmt.executeUpdate();
        log.info("resultSize={}", resultSize);
    } catch (SQLException e) {
        log.error("db error", e);
        throw e;
    } finally {
        close(con, pstmt, null);
    }
}

public void delete(String memberId) throws SQLException {
    String sql = "delete from member where member_id=?";

    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        con = getConnection();
        pstmt = con.prepareStatement(sql);
        pstmt.setString(1, memberId);
        pstmt.executeUpdate();
    } catch (SQLException e) {
        log.error("db error", e);
        throw e;
    } finally {
        close(con, pstmt, null);
    }
}

Connection Pool & DataSource

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ํš๋“ ๊ณผ์ •

  • ์„œ๋ฒ„์—์„œ DB ๋“œ๋ผ์ด๋ฒ„๋ฅผ ํ†ตํ•ด ์ปค๋„ฅ์…˜ ์กฐํšŒ

  • DB ๋“œ๋ผ์ด๋ฒ„๋Š” DB์™€ TCP/IP ์ปค๋„ฅ์…˜ ์—ฐ๊ฒฐ (3 way handshake ๋™์ž‘ ๋ฐœ์ƒ)

  • TCP/IP ์ปค๋„ฅ์…˜์ด ์—ฐ๊ฒฐ๋˜๋ฉด, ID/PW์™€ ๊ธฐํƒ€ ๋ถ€๊ฐ€์ •๋ณด๋ฅผ DB์— ์ „๋‹ฌ

  • DB๋Š” ID/PW๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€ ์ธ์ฆ์„ ์™„๋ฃŒํ•˜๊ณ , ๋‚ด๋ถ€ DB ์„ธ์…˜ ์ƒ์„ฑ

  • DB๋Š” ์ปค๋„ฅ์…˜ ์ƒ์„ฑ์ด ์™„๋ฃŒ๋˜์—ˆ๋‹ค๋Š” ์‘๋‹ต ์ „๋‹ฌ

  • DB ๋“œ๋ผ์ด๋ฒ„๋Š” ์ปค๋„ฅ์…˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜

ConnectionPool

์ปค๋„ฅ์…˜์„ ๊ด€๋ฆฌํ•˜๋Š” ์ˆ˜์˜์žฅ(!)

  • DriverManager ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ๋งค๋ฒˆ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์‘๋‹ต ์†๋„ ์ €ํ•˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ปค๋„ฅ์…˜์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด๋‘๊ณ  ์‚ฌ์šฉ

ConnectionPool ์ดˆ๊ธฐํ™”

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ์ ์— ํ•„์š”ํ•œ ๋งŒํผ์˜ ์ปค๋„ฅ์…˜์„ ๋ฏธ๋ฆฌ ํ™•๋ณดํ•ด์„œ ํ’€์— ๋ณด๊ด€

    • ๊ธฐ๋ณธ๊ฐ’์€ ๋ณดํ†ต 10๊ฐœ

ConnectionPool ์—ฐ๊ฒฐ ์ƒํƒœ

  • ์ปค๋„ฅ์…˜ ํ’€์— ๋“ค์–ด ์žˆ๋Š” ์ปค๋„ฅ์…˜์€ TCP/IP๋กœ DB์™€ ์ปค๋„ฅ์…˜์ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ์ƒํƒœ

    • ์–ธ์ œ๋“ ์ง€ SQL์„ DB์— ์ „๋‹ฌ ๊ฐ€๋Šฅ

ConnectionPool ์‚ฌ์šฉ

  • ์ปค๋„ฅ์…˜ ํ’€์„ ํ†ตํ•ด ์ด๋ฏธ ์ƒ์„ฑ๋˜์–ด ์žˆ๋Š” ์ปค๋„ฅ์…˜์„ ๊ฐ์ฒด ์ฐธ์กฐ๋กœ ์–ป์–ด์„œ ์‚ฌ์šฉ

  • ์ปค๋„ฅ์…˜์„ ์š”์ฒญํ•˜๋ฉด ์ปค๋„ฅ์…˜ ํ’€์€ ์ž์‹ ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ปค๋„ฅ์…˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋ฐ˜ํ™˜

  • ์ปค๋„ฅ์…˜ ํ’€๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•ด์„œ SQL์„ DB์— ์ „๋‹ฌํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌ

  • ์ปค๋„ฅ์…˜์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜๋ฉด ์ปค๋„ฅ์…˜์„ ์ข…๋ฃŒํ•˜์ง€ ์•Š๊ณ  ๋‹ค์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปค๋„ฅ์…˜ ํ’€์— ๋ฐ˜ํ™˜

DataSource

์ปค๋„ฅ์…˜์„ ํš๋“ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ถ”์ƒํ™” ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค

  • ์ปค๋„ฅ์…˜ ํ’€ ์˜คํ”ˆ์†Œ์Šค commons-dbcp2, tomcat-jdbc pool, HikariCP์— ์ง์ ‘ ์˜์กดํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, DataSource ์ธํ„ฐํŽ˜์ด์Šค์—๋งŒ ์˜์กดํ•˜๋ฉด ๋œ๋‹ค!

DriverManager

DriverManager

  • ์ปค๋„ฅ์…˜์„ ํš๋“ํ•  ๋•Œ ๋งˆ๋‹ค URL/USERNAME/PASSWORD ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ณ„์† ์ „๋‹ฌ

DataSourceDriverManager

  • ๋ฐ˜๋ฉด, ์ฒ˜์Œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋งŒ ํ•„์š”ํ•œ ํŒŒ๋ฆฌ๋ฏธํ„ฐ๋ฅผ ๋„˜๊ธฐ๊ณ , ์ปค๋„ฅ์…˜์„ ํš๋“ํ•  ๋•Œ๋Š” ๋‹จ์ˆœํžˆ dataSource.getConnection() ๋งŒ ํ˜ธ์ถœ

  • ์„ค์ •๊ณผ ์‚ฌ์šฉ์˜ ๋ถ„๋ฆฌ๊ฐ€ ๋ช…ํ™•

@Test
void driverManager() throws SQLException {
    Connection con1 = DriverManager.getConnection(URL, USERNAME, PASSWORD);
    Connection con2 = DriverManager.getConnection(URL, USERNAME, PASSWORD);
    log.info("connection={}, class={}", con1, con1.getClass());
    log.info("connection={}, class={}", con2, con2.getClass());
}

@Test
void dataSourceDriverManager() throws SQLException {
    DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
    useDataSource(dataSource);
}

private void useDataSource(DataSource dataSource) throws SQLException {
    Connection con1 = dataSource.getConnection();
    Connection con2 = dataSource.getConnection();
    log.info("connection={}, class={}", con1, con1.getClass());
    log.info("connection={}, class={}", con2, con2.getClass());
}

Connection Pool

  • ์ปค๋„ฅ์…˜ ํ’€์€ ๋ณ„๋„์˜ ์“ฐ๋ ˆ๋“œ ์‚ฌ์šฉํ•ด์„œ ์ปค๋„ฅ์…˜ ํ’€์— ์ปค๋„ฅ์…˜์„ ์ฑ„์šด๋‹ค.

  • DriverManagerDataSource ๋Š” ํ•ญ์ƒ ์ƒˆ๋กœ์šด ์ปค๋„ฅ์…˜์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐ˜๋ฉด, ์ปค๋„ฅ์…˜ ํ’€์€ ์ปค๋„ฅ์…˜์„ ์žฌ์‚ฌ์šฉ

@Test
void dataSourceConnectionPool() throws SQLException, InterruptedException {
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setJdbcUrl(URL);
    dataSource.setUsername(USERNAME);
    dataSource.setPassword(PASSWORD);
    dataSource.setMaximumPoolSize(10);
    dataSource.setPoolName("MyPool");
    
    useDataSource(dataSource);
    Thread.sleep(1000); // ์ปค๋„ฅ์…˜ ์ƒ์„ฑ ์‹œ๊ฐ„ ๋Œ€๊ธฐ
}

HikariCP

Transaction

DB์—์„œ ํŠธ๋žœ์žญ์…˜์€ ํ•˜๋‚˜์˜ ์ž‘์—…๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๋ณด์žฅ

  • ์ปค๋ฐ‹(Commit) : ๋ชจ๋“  ์ž‘์—…์ด ์„ฑ๊ณตํ•ด์„œ DB์— ์ •์ƒ ๋ฐ˜์˜ํ•˜๋Š” ๊ฒƒ

  • ๋กค๋ฐฑ(Rollback) : ์ž‘์—…์ด ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•ด์„œ ์ž‘์—… ์ด์ „์œผ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๊ฒƒ

ํŠธ๋žœ์žญ์…˜ ACID

ํŠธ๋žœ์žญ์…˜์€ ์›์ž์„ฑ(Atomicity), ์ผ๊ด€์„ฑ(Consistency), ๊ฒฉ๋ฆฌ์„ฑ(Isolation), ์ง€์†์„ฑ(Durability)์„ ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.

  • ์›์ž์„ฑ(Atomicity) : ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰ํ•œ ์ž‘์—…๋“ค์€ ๋งˆ์น˜ ํ•˜๋‚˜์˜ ์ž‘์—…์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋ชจ๋‘ ์„ฑ๊ณต ํ•˜๊ฑฐ๋‚˜ ๋ชจ๋‘ ์‹คํŒจํ•ด์•ผ ํ•œ๋‹ค.

  • ์ผ๊ด€์„ฑ(Consistency) : ๋ชจ๋“  ํŠธ๋žœ์žญ์…˜์€ ์ผ๊ด€์„ฑ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ •ํ•œ ๋ฌด๊ฒฐ์„ฑ ์ œ์•ฝ ์กฐ๊ฑด์„ ํ•ญ์ƒ ๋งŒ์กฑํ•ด์•ผ ํ•œ๋‹ค.

  • ๊ฒฉ๋ฆฌ์„ฑ(Isolation) : ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ํŠธ๋žœ์žญ์…˜๋“ค์ด ์„œ๋กœ์—๊ฒŒ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋„๋ก ๊ฒฉ๋ฆฌํ•œ๋‹ค.

    • ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ๋ชปํ•˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

    • ํŠธ๋žœ์žญ์…˜ ๊ฐ„์— ๊ฒฉ๋ฆฌ์„ฑ์„ ์™„๋ฒฝํžˆ ๋ณด์žฅํ•˜๋ ค๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๊ฑฐ์˜ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ANSI ํ‘œ์ค€์€ ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ 4๋‹จ๊ณ„๋กœ ๋‚˜๋ˆ„์–ด ์ •์˜

    • ๊ฒฉ๋ฆฌ์„ฑ์€ ๋™์‹œ์„ฑ๊ณผ ๊ด€๋ จ๋œ ์„ฑ๋Šฅ ์ด์Šˆ๋กœ ์ธํ•ด ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€(Isolation level)์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

      • READ UNCOMMITED(์ปค๋ฐ‹๋˜์ง€ ์•Š์€ ์ฝ๊ธฐ)

      • READ COMMITTED(์ปค๋ฐ‹๋œ ์ฝ๊ธฐ)

      • REPEATABLE READ(๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ์ฝ๊ธฐ)

      • SERIALIZABLE(์ง๋ ฌํ™” ๊ฐ€๋Šฅ)

  • ์ง€์†์„ฑ(Durability) : ํŠธ๋žœ์žญ์…˜์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚ด๋ฉด ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ํ•ญ์ƒ ๊ธฐ๋ก๋˜์–ด์•ผ ํ•œ๋‹ค.

    • ์ค‘๊ฐ„์— ์‹œ์Šคํ…œ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋กœ๊ทธ ๋“ฑ์„ ์‚ฌ์šฉํ•ด์„œ ์„ฑ๊ณตํ•œ ํŠธ๋žœ์žญ์…˜ ๋‚ด์šฉ์„ ๋ณต๊ตฌํ•ด์•ผ ํ•œ๋‹ค.

ํŠธ๋žœ์žญ์…˜ ACID

@Transactional ์ž˜ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ

ํŠธ๋žœ์žญ์…˜์˜ ์‚ฌ์šฉ ์˜ˆ์‹œ

  • ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜์˜ํ•˜๋ ค๋ฉด commit ์„ ํ˜ธ์ถœํ•˜๊ณ ,

  • ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜์˜ํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด rollback ์„ ํ˜ธ์ถœ

  • ์ปค๋ฐ‹์„ ํ˜ธ์ถœํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ž„์‹œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ -> ํ•ด๋‹น ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘

์ž๋™์ปค๋ฐ‹๊ณผ ์ˆ˜๋™์ปค๋ฐ‹

์ž๋™ ์ปค๋ฐ‹

  • ๊ฐ๊ฐ์˜ ์ฟผ๋ฆฌ ์‹คํ–‰ ์งํ›„ ์ž๋™์œผ๋กœ ์ปค๋ฐ‹ ํ˜ธ์ถœ

  • ์ปค๋ฐ‹์ด๋‚˜ ๋กค๋ฐฑ์„ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ํŽธ๋ฆฌํ•จ

  • ํ•˜์ง€๋งŒ, ์›ํ•˜๋Š” ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ์„ ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋‹จ์  ์กด์žฌ

set autocommit true; -- default

์ˆ˜๋™ ์ปค๋ฐ‹

  • ์ˆ˜๋™ ์ปค๋ฐ‹ ๋ชจ๋“œ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘

  • ์ดํ›„ commit, rollback ํ˜ธ์ถœ ํ•„์š”

  • ์ˆ˜๋™/์ž๋™ ์ปค๋ฐ‹ ๋ชจ๋“œ๋Š” ํ•œ๋ฒˆ ์„ค์ •ํ•˜๋ฉด ํ•ด๋‹น ์„ธ์…˜์—์„œ ๊ณ„์† ์œ ์ง€ (์ค‘๊ฐ„ ๋ณ€๊ฒฝ๋„ ๊ฐ€๋Šฅ)

set autocommit false;
-- ...
commit;

Lock

  • ์„ธ์…˜์ด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋™์•ˆ ์ปค๋ฐ‹ or ๋กค๋ฐฑ ์ „๊นŒ์ง€ ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋„๋ก ๋ฝ์„ ์ œ๊ณต

  • ๋‹ค๋ฅธ ์„ธ์…˜์€ ๋ฝ์„ ํš๋“ํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ

    • ๋ฝ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ๋„˜์–ด๊ฐ€๋ฉด ๋ฝ ํƒ€์ž„์•„์›ƒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ(๋ฝ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ์„ค์ • ๊ฐ€๋Šฅ)

Lock Timeout ์‹œ๊ฐ„ ์„ค์ •

SET LOCK_TIMEOUT <milliseconds>

Lock Timeout Error

Timeout trying to lock table {0}; SQL statement:
...

์กฐํšŒ์™€ ๋ฝ

  • ์ผ๋ฐ˜์ ์ธ ์กฐํšŒ๋Š” ๋ฝ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์ง€๋งŒ,

  • ๋ฝ์„ ํš๋“ํ•ด์„œ ๋ณ€๊ฒฝ์„ ๋ง‰๊ณ  ์‹ถ๋‹ค๋ฉด, select .. for update ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉ

    • ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ์‹œ์ ๊นŒ์ง€ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ณ€๊ฒฝํ•˜์ง€ ๋ชปํ•˜๋„๋ก ๊ฐ•์ œ๋กœ ๋ง‰์•„์•ผ ํ•  ๊ฒฝ์šฐ ์‚ฌ์šฉ

    • ํ•ด๋‹น ์„ธ์…˜์ด ์กฐํšŒ ์‹œ์ ์— ๋ฝ์„ ๊ฐ€์ ธ๊ฐ€๋ฒ„๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค(ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‹œ ๋ฝ ๋ฐ˜๋‚ฉ)

๊ณผ๊ฑฐ ํŠธ๋žœ์žญ์…˜ ์ ์šฉ

  • ํŠธ๋žœ์žญ์…˜์€ ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ๋ถ€ํ„ฐ ์‹œ์ž‘

    • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ž˜๋ชป๋˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ๋ถ€๋ถ„์„ ํ•จ๊ป˜ ๋กค๋ฐฑํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

  • ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ์ปค๋„ฅ์…˜์ด ํ•„์š”. set autocommit false;

  • ๊ฐ™์€ ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋™์•ˆ ๊ฐ™์€ ์ปค๋„ฅ์…˜์„ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.

    • ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์€ ์ปค๋„ฅ์…˜์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ๊ณผ๊ฑฐ ์„œ๋ฒ„์—์„œ์˜ ํŠธ๋žœ์ ์…˜ ์ ์šฉ์€ ์„œ๋น„์Šค ๊ณ„์ธต์ด ๋งค์šฐ ์ง€์ €๋ถ„ํ•ด์ง€๊ณ  ์ƒ๊ฐ๋ณด๋‹ค ๋งค์šฐ ๋ณต์žกํ•œ ์ฝ”๋“œ๋ฅผ ์š”๊ตฌ..

commit


๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์˜ ๋ฌธ์ œ์ 


Transaction Problem

Spring Transaction Manager

ํŠธ๋žœ์žญ์…˜ ์ถ”์ƒํ™”

  • PlatformTransactionManager interface

    • JdbcTransactionManager

    • JpaTransactionManager

    • HibernateTransactionManager

    • EtcTransactionManager

public interface PlatformTransactionManager extends TransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;
}

๋ฆฌ์†Œ์Šค ๋™๊ธฐํ™”

  • ํŠธ๋žœ์žญ์…˜์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํŠธ๋žœ์žญ์…˜์˜ ์‹œ์ž‘๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.

    • ๊ณผ๊ฑฐ์—๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ปค๋„ฅ์…˜์„ ์ „๋‹ฌํ–ˆ์ง€๋งŒ

    • ์Šคํ”„๋ง์€ org.springframework.transaction.support.TransactionSynchronizationManager๋ฅผ ํ†ตํ•ด ThreadLocal๋กœ ์ปค๋„ฅ์…˜์„ ๋™๊ธฐํ™”

      • TransactionManager๋Š” ๋‚ด๋ถ€์—์„œ TransactionSynchronizationManager๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , TransactionManager๋ฅผ ํ†ตํ•ด ์ปค๋„ฅ์…˜์„ ํš๋“

      • ThreadLocal์„ ์‚ฌ์šฉํ•ด์„œ ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ์ƒํ™ฉ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ปค๋„ฅ์…˜์„ ๋™๊ธฐํ™”๊ฐ€ ๊ฐ€๋Šฅ

.

  • ๋™์ž‘ ๋ฐฉ์‹

    • 1.TransactionManager๋Š” dataSource๋ฅผ ํ†ตํ•ด ์ปค๋„ฅ์…˜์„ ๋งŒ๋“ค๊ณ  ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘

    • 2.TransactionManager๋Š” ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋œ ์ปค๋„ฅ์…˜์„ TransactionSynchronizationManager์— ๋ณด๊ด€

    • 3.Repository๋Š” TransactionSynchronizationManager์— ๋ณด๊ด€๋œ ์ปค๋„ฅ์…˜์„ ๊บผ๋‚ด์„œ ์‚ฌ์šฉ

    • 4.ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋˜๋ฉด TransactionManager๋Š” TransactionSynchronizationManager์— ๋ณด๊ด€๋œ ์ปค๋„ฅ์…˜์„ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์„ ์ข…๋ฃŒํ•˜๊ณ , ์ปค๋„ฅ์…˜๋„ ๋‹ซ์Œ

TransactionManager

ํŠธ๋žœ์žญ์…˜ ๋™๊ธฐํ™”๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด DataSourceUtils๋ฅผ ์‚ฌ์šฉ

DataSourceUtils.getConnection()

private Connection getConnection() throws SQLException {
    Connection con = DataSourceUtils.getConnection(dataSource);
    return con;
}
  • TransactionSynchronizationManager๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์ปค๋„ฅ์…˜์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ์ปค๋„ฅ์…˜์„ ๋ฐ˜ํ™˜

  • ์ปค๋„ฅ์…˜์ด ์—†๋Š” ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ์ปค๋„ฅ์…˜์„ ์ƒ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜

DataSourceUtils.releaseConnection()

private void close(Connection con, Statement stmt, ResultSet rs) {
    //...
    DataSourceUtils.releaseConnection(con, dataSource);
}
  • ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋™๊ธฐํ™”๋œ ์ปค๋„ฅ์…˜์€ ์ปค๋„ฅ์…˜์„ ๋‹ซ์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์œ ์ง€

  • TransactionSynchronizationManager๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์ปค๋„ฅ์…˜์ด ์—†๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ์ปค๋„ฅ์…˜์„ ๋‹ซ์Œ

  • commit(status), rollback(status) ํ˜ธ์ถœ ์‹œ ์•Œ์•„์„œ release ์ˆ˜ํ–‰

commit

Transaction Template

ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด ์ ์šฉ์„ ์œ„ํ•ด TransactionTemplate ํ…œํ”Œ๋ฆฟ ํด๋ž˜์Šค ์ž‘์„ฑ

  • Transaction์˜ ๋ฐ˜๋ณต๋˜๋Š” try, catch, finally ์ฝ”๋“œ ์ œ๊ฑฐ

  • ๋‹จ, ์„œ๋น„์Šค ๋กœ์ง์— ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๋‹จ์ ์ด ์กด์žฌ

public class TransactionTemplate {
    private PlatformTransactionManager transactionManager;

    // ์‘๋‹ต๊ฐ’์ด ์žˆ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉ
    public <T> T execute(TransactionCallback<T> action) {..}
    // ์‘๋‹ต๊ฐ’์ด ์—†์„ ๊ฒฝ์šฐ ์‚ฌ์šฉ
    void executeWithoutResult(Consumer<TransactionStatus> action) {..}
}

commit

Transaction AOP

TransactionalProxy ๋„์ž…์„ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ๊ฐ์ฒด์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ ์„œ๋น„์Šค ๊ฐ์ฒด๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ

  • @Transactional์„ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ณณ์— ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด, ์Šคํ”„๋ง์˜ ํŠธ๋žœ์žญ์…˜ AOP๊ฐ€ ํŠธ๋žœ์žญ์…˜์ด ์ ์šฉ๋œ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ž๋™์œผ๋กœ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ

  • TransactionalProxy๋ฅผ ๋„์ž…ํ•˜๋ฉด @Transactional์ด ๋ถ™์–ด ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋‚˜ ํด๋ž˜์Šค์— Spring์ด ํ•ด๋‹น ์„œ๋น„์Šค ๋กœ์ง์„ ์ƒ์†๋ฐ›์•„์„œ ์ž๋™์œผ๋กœ ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑ

    • xxxService$$EnhancerBySpringCGLIB$$..

commit

ํŠธ๋žœ์žญ์…˜ AOP ๋™์ž‘ ํ๋ฆ„

Result
  1. Transaction์ด ์ ์šฉ๋œ ํด๋ž˜์Šค/๋ฉ”์„œ๋“œ ํ˜ธ์ถœ

  2. Transaction์ด ์ ์šฉ๋œ Spring AOP Proxy ํ˜ธ์ถœ

  3. Spring Container์— ๋“ฑ๋ก๋œ Transaction Manager ํš๋“

  4. ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘. transactionManager.getTransaction()

  5. transactionManager๋Š” ๋‚ด๋ถ€์—์„œ DataSource๋ฅผ ์‚ฌ์šฉํ•ด ์ปค๋„ฅ์…˜ ์ƒ์„ฑ

  6. ์ปค๋„ฅ์…˜์„ ์ˆ˜๋™ ์ปค๋ฐ‹ ๋ชจ๋“œ๋กœ ๋ณ€๊ฒฝํ•ด์„œ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘. setAutoCommit(false)

  7. ์ปค๋„ฅ์…˜์„ TransactionSynchronizationManager์— ๋ณด๊ด€

  8. TransactionSynchronizationManager๋Š” ThreadLocal์— ์ปค๋„ฅ์…˜์„ ๋ณด๊ด€

    • ThreadLocal: ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ปค๋„ฅ์…˜ ๋ณด๊ด€

  9. Spring AOP Proxy์—์„œ ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‹คํ–‰ํ•˜๋ฉด์„œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ๋ฉ”์„œ๋“œ๋“ค์„ ํ˜ธ์ถœ (์ปค๋„ฅ์…˜์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง)

  10. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” DataSourceUtils.getConnection()์„ ํ†ตํ•ด TransactionSynchronizationManager์— ๋ณด๊ด€๋œ ์ปค๋„ฅ์…˜์„ ๊บผ๋‚ด์„œ ์‚ฌ์šฉ

    • ๊ฐ™์€ ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•˜๊ณ , ํŠธ๋žœ์žญ์…˜๋„ ์œ ์ง€

  11. ํš๋“ํ•œ ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•ด์„œ SQL์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „๋‹ฌ ๋ฐ ์‹คํ–‰

  12. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋๋‚˜๊ณ  ํŠธ๋žœ์žญ์…˜์„ ์ข…๋ฃŒ๋ฅผ ์œ„ํ•ด TransactionSynchronizationManager๋ฅผ ํ†ตํ•œ ๋™๊ธฐํ™”๋œ ์ปค๋„ฅ์…˜์„ ํš๋“

    • ํš๋“ํ•œ ์ปค๋„ฅ์…˜์„ ํ†ตํ•ด ์ปค๋ฐ‹/๋กค๋ฐฑ ํ›„ ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ

  13. ์ „์ฒด ๋ฆฌ์†Œ์Šค(TransactionSynchronizationManager, ThreadLocal, setAutoCommit(true), con.close()..) ์ •๋ฆฌ

SpringBoot ์ž๋™ ๋ฆฌ์†Œ์Šค ๋“ฑ๋ก

๊ธฐ์กด์—๋Š” ๋ฐ์ดํ„ฐ์†Œ์Šค์™€ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ XML๋กœ ๋“ฑ๋กํ•˜๊ฑฐ๋‚˜ ์ง์ ‘ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผ ํ–ˆ์ง€๋งŒ, SpringBoot๋ฅผ ํ†ตํ•ด ๋งŽ์€ ๋ถ€๋ถ„์ด ์ž๋™ํ™”

์ž๋™ ๋“ฑ๋ก

DataSource

  • application.properties์— ์žˆ๋Š” ์†์„ฑ์„ ์‚ฌ์šฉํ•ด์„œ DataSource๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์Šคํ”„๋ง ๋นˆ์— ์ž๋™์œผ๋กœ ๋“ฑ๋ก

    • ์ง์ ‘ DataSource๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜์ง€ ์•Š์Œ

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.username=sa
spring.datasource.password=

TransactionManager

  • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์ ์ ˆํ•œ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €(PlatformTransactionManager)๋ฅผ ์ž๋™์œผ๋กœ ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋ก

    • ์ž๋™ ๋“ฑ๋ก ์Šคํ”„๋ง ๋นˆ ์ด๋ฆ„: transactionManager

    • DataSource์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ง์ ‘ TransactionManager๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜์ง€ ์•Š์Œ

  • ์ž๋™์œผ๋กœ ๋“ฑ๋ก๋˜๋Š” ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋Š” ํ˜„์žฌ ๋“ฑ๋ก๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณด๊ณ  ํŒ๋‹จ

    • JDBC: DataSourceTransactionManager

    • JPA: JpaTransactionManager

    • JDBC + JPA: JpaTransactionManager

@Slf4j
@SpringBootTest
class MemberServiceV3_4Test {

    @TestConfiguration
    static class TestConfig {

        private final DataSource dataSource;

        public TestConfig(DataSource dataSource) {
            this.dataSource = dataSource;
        }

        @Bean
        MemberRepositoryV3 memberRepositoryV3() {
            return new MemberRepositoryV3(dataSource);
        }

        @Bean
        MemberServiceV3_3 memberServiceV3_3() {
            return new MemberServiceV3_3(memberRepositoryV3());
        }
    }
}
  • SpringBoot๊ฐ€ application.properties์— ์ง€์ •๋œ ์†์„ฑ์„ ์ฐธ๊ณ ํ•ด์„œ ๋ฐ์ดํ„ฐ์†Œ์Šค์™€ ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑ

  • ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด SpringBoot๊ฐ€ ๋งŒ๋“ค์–ด์ค€ ๋ฐ์ดํ„ฐ์†Œ์Šค ๋นˆ์„ ์ฃผ์ž… ๊ฐ€๋Šฅ

์ง์ ‘ ๋“ฑ๋ก

@TestConfiguration
static class TestConfig {
    @Bean
    DataSource dataSource() {
        return new DriverManagerDataSource(URL, USERNAME, PASSWORD);
    }

    @Bean
    PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    MemberRepositoryV3 memberRepositoryV3() {
        return new MemberRepositoryV3(dataSource());
    }

    @Bean
    MemberServiceV3_3 memberServiceV3_3() {
        return new MemberServiceV3_3(memberRepositoryV3());
    }
}

Configure a DataSource

Common Application Properties

Java Excaption

Result
  • Object : ๋ชจ๋“  ๊ฐ์ฒด์˜ ์ตœ์ƒ์œ„ ๋ถ€๋ชจ

  • Throwable : ์ตœ์ƒ์œ„ ์˜ˆ์™ธ

    • ์ƒ์œ„ ์˜ˆ์™ธ๋ฅผ ์žก์œผ๋ฉด ๊ทธ ํ•˜์œ„ ์˜ˆ์™ธ(Error..)๊นŒ์ง€ ํ•จ๊ป˜ ์žก์œผ๋ฏ€๋กœ, Throwable ์˜ˆ์™ธ๋Š” ์žก์ง€ ๋ง๊ณ , Exception๋ถ€ํ„ฐ ์žก์ž.

  • Error : ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ ์˜ˆ์™ธ (๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์ด๋‚˜ ์‹ฌ๊ฐํ•œ ์‹œ์Šคํ…œ ์˜ค๋ฅ˜)

    • unchecked exception

  • Exception : ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์งˆ์ ์ธ ์ตœ์ƒ์œ„ ์˜ˆ์™ธ

    • Exception๊ณผ ๊ทธ ํ•˜์œ„ ์˜ˆ์™ธ๋Š” ๋ชจ๋‘ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ฒดํฌํ•˜๋Š” checked exception

    • ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ฒดํฌํ•ด ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์žก๊ฑฐ๋‚˜ ๋˜์ง€๊ฑฐ๋‚˜ ํ•˜๋‚˜๋ฅผ ํ•„์ˆ˜๋กœ ์„ ํƒ

    • ๋‹จ, RuntimeException์€ ์˜ˆ์™ธ

  • RuntimeException : ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ฒดํฌํ•˜์ง€ ์•Š๋Š” unchecked exception

    • RuntimeException๊ณผ ๊ทธ ์ž์‹ ์˜ˆ์™ธ๋Š” ๋ชจ๋‘ unchecked exception

์˜ˆ์™ธ์˜ ๊ธฐ๋ณธ ๊ทœ์น™

  • ์˜ˆ์™ธ๋Š” ์žก์•„์„œ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋˜์ ธ์•ผ ํ•จ.

  • ์˜ˆ์™ธ๋ฅผ ์žก๊ฑฐ๋‚˜ ๋˜์งˆ ๋•Œ ์ง€์ •ํ•œ ์˜ˆ์™ธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ž์‹ ์˜ˆ์™ธ๋“ค๋„ ํ•จ๊ป˜ ์ฒ˜๋ฆฌ

.

  • ์˜ˆ์™ธ ์žก๊ธฐ try-catch

    • Repository ์˜ˆ์™ธ ๋ฐœ์ƒ -> Service๋กœ ์˜ˆ์™ธ throws -> Service์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ -> ์ดํ›„ ์ •์ƒ ํ๋ฆ„์œผ๋กœ ๋™์ž‘

  • ์˜ˆ์™ธ ๋˜์ง€๊ธฐ throws Exception

    • Repository ์˜ˆ์™ธ ๋ฐœ์ƒ -> Service๋กœ ์˜ˆ์™ธ throws -> Controller๋กœ ์˜ˆ์™ธ throws

    • ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๊ณ  ๊ณ„์† ๋˜์ง€๋ฉด main() ์“ฐ๋ ˆ๋“œ์˜ ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด์„œ ์‹œ์Šคํ…œ์ด ์ข…๋ฃŒ๋˜๊ณ , ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฒฝ์šฐ WAS๊ฐ€ ํ•ด๋‹น ์˜ˆ์™ธ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ, ์ฃผ๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ง€์ •ํ•œ ์˜ค๋ฅ˜ ํŽ˜์ด์ง€๋ฅผ ์ „๋‹ฌ

Checked Exception

์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ์ฒดํฌํ•ด์ฃผ๋ฉด, ์žก์•„์„œ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜, ๋ฐ–์œผ๋กœ ๋˜์ง€๋„๋ก ์„ ์–ธ

์˜ˆ์™ธ๋ฅผ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์„ ๊ฒฝ์šฐ์—๋Š” ์˜ˆ์™ธ๋ฅผ throws๋กœ ๋˜์ ธ์ค˜์•ผ ํ•จ.

  • ์žฅ์ : ์‹ค์ˆ˜๋กœ ์˜ˆ์™ธ๋ฅผ ๋ˆ„๋ฝํ•˜์ง€ ์•Š๋„๋ก ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ํ†ตํ•ด ๋ฌธ์ œ๋ฅผ ์žก์•„์ฃผ๋Š” ์•ˆ์ „ ์žฅ์น˜

  • ๋‹จ์ : ๋ชจ๋“  ์ฒดํฌ ์˜ˆ์™ธ๋ฅผ ๋ฐ˜๋“œ์‹œ ์žก๊ฑฐ๋‚˜ ๋˜์ง€๋„๋ก ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€

    • ํฌ๊ฒŒ ์‹ ๊ฒฝ์“ฐ๊ณ  ์‹ถ์ง€ ์•Š์€ ์˜ˆ์™ธ๊นŒ์ง€ ๋ชจ๋‘ ์ฑ™๊ฒจ์•ผ ํ•˜๊ณ , ์˜์กด๊ด€๊ณ„์— ๋”ฐ๋ฅธ ๋‹จ์ ๋„ ์กด์žฌ

commit

ํ™œ์šฉ

\1. ๊ธฐ๋ณธ์ ์œผ๋กœ Unchecked(Runtime) Exception๋ฅผ ์‚ฌ์šฉํ•˜์ž.

  • Checked Exception์€ Service, Controller์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋Š” ์˜ˆ์™ธ๋ฅผ throws ์„ ์–ธ์œผ๋กœ ๊ณ„์† ๋˜์ง€๋‹ค๋ณด๋ฉด, ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์˜ˆ์™ธ, ์˜์กด ๊ด€๊ณ„ ๋ฌธ์ œ ๋ฐœ์ƒ

    • ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์˜ˆ์™ธ: ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ServletFilter, SpringInterceptor, Spring ControllerAdvice๋ฅผ ํ†ตํ•ด ์ผ๊ด€์„ฑ์žˆ๊ฒŒ ๊ณตํ†ต์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์ž. (์‹ค๋ฌด์˜ ๋Œ€๋ถ€๋ถ„์˜ ์˜ˆ์™ธ๋“ค์€ ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ ์˜ˆ์™ธ)

    • ์˜์กด ๊ด€๊ณ„ ๋ฌธ์ œ: ์ฒ˜๋ฆฌํ•  ์ˆ˜๋„ ์—†๋Š” SQLException์— ์˜์กดํ•˜์—ฌ ๊ธฐ์ˆ ์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์˜์กด ์ฝ”๋“œ๋ฅผ ์ „๋ถ€ ๊ณ ์ณ์ฃผ์–ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ(OCP, DI ์œ„๋ฐ˜). -> Exception์„ ๋˜์ ธ์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์ง€๋งŒ, ๋ชจ๋“  ์˜ˆ์™ธ๋ฅผ ๋‹ค ๋‹จ์ง€๊ธฐ ๋–„๋ฌธ์— ์ฒดํฌ ์˜ˆ์™ธ๋ฅผ ์ฒดํฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ๋ฌดํšจํ™”

\2. ์ฒดํฌ ์˜ˆ์™ธ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ƒ ์˜๋„์ ์œผ๋กœ ๋˜์ง€๋Š” ์˜ˆ์™ธ๋ฅผ ์žก์•„์„œ ๋ฐ˜๋“œ์‹œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜์ž.

  • ๊ณ„์ขŒ ์ด์ฒด ์‹คํŒจ ์˜ˆ์™ธ

  • ๊ฒฐ์ œ์‹œ ํฌ์ธํŠธ ๋ถ€์กฑ ์˜ˆ์™ธ

  • ๋กœ๊ทธ์ธ ID, PW ๋ถˆ์ผ์น˜ ์˜ˆ์™ธ

Unchecked Exception

์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ฒดํฌํ•˜์ง€ ์•Š๋Š” ์˜ˆ์™ธ

์ฒดํฌ ์˜ˆ์™ธ์™€ ์–ธ์ฒดํฌ ์˜ˆ์™ธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ผํ•˜์ง€๋งŒ,

  • Checked Exception: ์˜ˆ์™ธ๋ฅผ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ํ•ญ์ƒ throws ์„ ์–ธ ํ•„์š”

  • Unchecked Exception: ์˜ˆ์™ธ๋ฅผ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•„๋„ throws ์ƒ๋žต ๊ฐ€๋Šฅ

์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์„ ๋•Œ ์˜ˆ์™ธ๋ฅผ ๋ฐ–์œผ๋กœ ๋˜์ง€๋Š”๋ฐ, throws๋ฅผ ํ•„์ˆ˜๋กœ ์„ ์–ธํ•ด์•ผ ํ•˜๋Š”๊ฐ€ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€์˜ ์ฐจ์ด๊ฐ€ ํผ

  • ์žฅ์ : ์‹ ๊ฒฝ์“ฐ๊ณ  ์‹ถ์ง€ ์•Š์€ ์–ธ์ฒดํฌ ์˜ˆ์™ธ๋Š” ๋ฌด์‹œํ•˜๊ณ  throws ์„ ์–ธ ์ƒ๋žต ๊ฐ€๋Šฅ

  • ๋‹จ์ : ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์˜ˆ์™ธ ๋ˆ„๋ฝ์„ ์žก์•„์ฃผ์ง€ ์•Š์œผ๋ฏ€๋กœ, ์‹ค์ˆ˜๋กœ ์˜ˆ์™ธ๋ฅผ ๋ˆ„๋ฝํ•  ์ˆ˜ ์žˆ์Œ

commit

ํ™œ์šฉ

  • CheckedException์ด ๋ฐœ์ƒํ•˜๋ฉด RuntimeException์œผ๋กœ ์ „ํ™˜ํ•ด์„œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€์ž.

  • ์‹œ์Šคํ…œ์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋Š” ๋Œ€๋ถ€๋ถ„ ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅ ์˜ˆ์™ธ์ด๋ฏ€๋กœ, Runtime Exception์„ ์‚ฌ์šฉํ•˜๋ฉด ์„œ๋น„์Šค๋‚˜ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์˜ˆ์™ธ๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๊ณ  ๊ณตํ†ต์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋Š” ์˜ˆ์™ธ๋Š” ๋ฌด์‹œํ•˜๋ฉด ๋˜๋ฏ€๋กœ, ์˜ˆ์™ธ๋ฅผ ๊ฐ•์ œ๋กœ ์˜์กดํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

  • RuntimeException์€ ๋†“์น  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์„œํ™”๊ฐ€ ์ค‘์š”

JPA EntityManager

/**
* Make an instance managed and persistent.
* @param entity entity instance
* @throws EntityExistsException if the entity already exists.
* @throws IllegalArgumentException if the instance is not an
* entity
* @throws TransactionRequiredException if there is no transaction when
* invoked on a container-managed entity manager of that is of type
* <code>PersistenceContextType.TRANSACTION</code>
*/
public void persist(Object entity);

JdbcTemplate

/**
* Issue a single SQL execute, typically a DDL statement.
* @param sql static SQL to execute
* @throws DataAccessException if there is any problem
*/
void execute(String sql) throws DataAccessException;

Stack Trace

์˜ˆ์™ธ๋ฅผ ์ „ํ™˜ํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ๊ธฐ์กด ์˜ˆ์™ธ๋ฅผ ํฌํ•จํ•ด์•ผ ํ•˜์ž

  • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด.. ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ํ™•์ธํ•  ๋•Œ ์ƒ๋‹จ์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์—†๋Š” ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ ๋ฐœ์…

  • ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•  ๋•Œ ๋งˆ์ง€๋ง‰ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์˜ˆ์™ธ๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋กœ๊ทธ์— ์Šคํƒ ํŠธ๋ ˆ์ด์Šค ์ถœ๋ ฅ ๊ฐ€๋Šฅ

@Test
void printEx() {
    Controller controller = new Controller();
    try {
        controller.request();
    } catch (Exception e) {
        log.info("ex", e);
    }
}

Spring Exception

  • ์„œ๋น„์Šค ๊ณ„์ธต์€ ๊ฐ€๊ธ‰์  ํŠน์ • ๊ตฌํ˜„ ๊ธฐ์ˆ ์— ์˜์กดํ•˜์ง€ ์•Š๊ณ , ์ˆœ์ˆ˜ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

  • ์˜ˆ์™ธ์— ๋Œ€ํ•œ ์˜์กด(์˜ˆ์™ธ ๋ˆ„์ˆ˜)์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ์™€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ ์šฉํ•ด ๋ณด์ž.

commit

์ ‘๊ทผ ์˜ˆ์™ธ ์ƒ์„ฑ

Service Layer์—์„œ ํŠน์ • ๊ธฐ์ˆ ์— ์˜์กด์ ์ธ ์˜ˆ์™ธ(ex. SQLException)๋ฅผ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ, RuntimeException ์˜ˆ์™ธ๋ฅผ ์†์ƒ๋ฐ›์€ ์ปค์Šคํ…€ ์˜ˆ์™ธ๋ฅผ Repository Layer์—์„œ ๋ณ€ํ™˜ํ•ด์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ

  • ๋‹จ, SQL ErrorCode๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ๋‹ค ๋‹ค๋ฅด๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ข…์†์ 

commit

์Šคํ”„๋ง์˜ ์˜ˆ์™ธ ์ถ”์ƒํ™”

์Šคํ”„๋ง์€ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์— ๋Œ€ํ•œ ์ผ๊ด€๋œ ์˜ˆ์™ธ ์ถ”์ƒํ™”๋ฅผ ์ œ๊ณต

Result
  • ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์˜ ๋ชจ๋“  ์˜ˆ์™ธ๋Š” ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ

  • DataAccessException

    • NonTransient Exception

      • ์ผ์‹œ์ ์ด์ง€ ์•Š์€ ์˜ˆ์™ธ, ๊ฐ™์€ SQL์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜๋ณต ์‹คํ–‰ํ•˜๋ฉด ์‹คํŒจ

      • ex. SQL ๋ฌธ๋ฒ• ์˜ค๋ฅ˜, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐฐ ๋“ฑ

    • Transient Exception

      • ์ผ์‹œ์ ์ธ ์˜ˆ์™ธ, ํ•˜์œ„ ์˜ˆ์™ธ๋Š” ๋™์ผํ•œ SQL์„ ๋‹ค์‹œ ์‹œ๋„ํ–ˆ์„ ๋•Œ ์„ฑ๊ณตํ•  ๊ฐ€๋Šฅ์„ฑ ์กด์žฌ

      • ex. ์ฟผ๋ฆฌ ํƒ€์ž„์•„์›ƒ, ๋ฝ ๊ด€๋ จ ์˜ค๋ฅ˜ ๋“ฑ

๊ฐ ์˜ˆ์™ธ๋Š” ํŠน์ • ๊ธฐ์ˆ ์— ์ข…์†๋˜์ง€ ์•Š๊ฒŒ ์„ค๊ณ„

  • ํŠน์ • ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋ฅผ ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ์˜ˆ์™ธ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ•  ์ˆ˜ํ–‰

  • ์˜ˆ์™ธ ๋ณ€ํ™˜๊ธฐ๋ฅผ ํ†ตํ•ด์„œ SQLException์˜ ErrorCode์— ๋งž๋Š” ์ ์ ˆํ•œ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์˜ˆ์™ธ๋กœ ๋ณ€ํ™˜

  • Service/Controller Layer์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋ฉด ํŠน์ • ๊ธฐ์ˆ ์— ์ข…์†์ ์ธ SQLException ๋Œ€์‹  ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉ

    • Repository

      public class MemberRepositoryImpl implements MemberRepository {
        private final DataSource dataSource;
        private final SQLExceptionTranslator exTranslator;
      
        public MemberRepositoryImpl(DataSource dataSource) {
            this.dataSource = dataSource;
            this.exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
        }
      
        //...
      
        @Override
        public Member save(Member member) {
            try {
                // save logic
            } catch (SQLException e) {
                throw exTranslator.translate("save", sql, e);
            } finally {
              // close
            }
        }
      }
    • Service

      try {
        MemberRepository.save(member);
      } catch (DuplicateKeyException e) {
        //..
      }

์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” SQL ์˜ˆ์™ธ ๋ณ€ํ™˜๊ธฐ

SQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
DataAccessException resultEx = exTranslator.translate("explanation", sql, e);
// => ์ ์ ˆํ•œ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์˜ ์˜ˆ์™ธ๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๋ฐ˜ํ™˜

assertThat(resultEx.getClass()).isEqualTo(BadSqlGrammarException.class);

SQL ErrorCode

  • SQL ErrorCode๋ฅผ sql-error-codes.xml ํŒŒ์ผ์— ๋Œ€์ž…ํ•ด์„œ ์–ด๋–ค ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์˜ˆ์™ธ๋กœ ์ „ํ™˜ํ•ด์•ผ ํ• ์ง€ ํƒ์ƒ‰

<bean id="H2" class="org.springframework.jdbc.support.SQLErrorCodes">
  <property name="badSqlGrammarCodes">
    <value>42000,42001,42101,42102,42111,42112,42121,42122,42132</value>
  </property>
  <property name="duplicateKeyCodes">
    <value>23001,23505</value>
  </property>
</bean>
<bean id="MySQL" class="org.springframework.jdbc.support.SQLErrorCodes">
  <property name="badSqlGrammarCodes">
    <value>1054,1064,1146</value>
  </property>
  <property name="duplicateKeyCodes">
    <value>1062</value>
  </property>
</bean>

commit

ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด

  • JDBC์˜ ๋ฐ˜๋ณต ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ JdbcTemplate

    • ์ปค๋„ฅ์…˜ ์กฐํšŒ, ์ปค๋„ฅ์…˜ ๋™๊ธฐํ™”

    • PeparedStatement ์ƒ์„ฑ ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ

    • ์ฟผ๋ฆฌ ์‹คํ–‰

    • ๊ฒฐ๊ณผ ๋ฐ”์ธ๋”ฉ

    • ์˜ˆ์™ธ ๋ฐœ์ƒ์‹œ ์Šคํ”„๋ง ์˜ˆ์™ธ ๋ณ€ํ™˜๊ธฐ ์‹คํ–‰

    • ๋ฆฌ์†Œ์Šค ์ข…๋ฃŒ

  • ํŠธ๋žœ์žญ์…˜์„ ์œ„ํ•œ ์ปค๋„ฅ์…˜ ๋™๊ธฐํ™”, ์Šคํ”„๋ง ์˜ˆ์™ธ ๋ณ€ํ™˜๊ธฐ๋„ ์ž๋™ ์‹คํ–‰

@Slf4j
public class MemberRepository implements MemberRepository {
    private final JdbcTemplate template;

    public MemberRepository(DataSource dataSource) {
        template = new JdbcTemplate(dataSource);
    }

    @Override
    public Member save(Member member) {
        String sql = "insert into member(member_id, money) values(?, ?)";
        template.update(sql, member.getMemberId(), member.getMoney());
        return member;
    }

    @Override
    public Member findById(String memberId) {
        String sql = "select * from member where member_id = ?";
        return template.queryForObject(sql, memberRowMapper(), memberId);
    }

    @Override
    public void update(String memberId, int money) {
        String sql = "update member set money=? where member_id=?";
        template.update(sql, money, memberId);
    }

    @Override
    public void delete(String memberId) {
        String sql = "delete from member where member_id=?";
        template.update(sql, memberId);
    }

    private RowMapper<Member> memberRowMapper() {
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setMemberId(rs.getString("member_id"));
            member.setMoney(rs.getInt("money"));
            return member;
        };
    }
}

Last updated