JiwonDev

Spring DB ๊ธฐ์ˆ  ํŒŒํ—ค์น˜๊ธฐ #1

by JiwonDev

 

 

์Šคํ”„๋ง DB 1ํŽธ - ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ํ•ต์‹ฌ ์›๋ฆฌ ๊ฐ•์˜ - ์ธํ”„๋Ÿฐ

๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ DB ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ธฐ์ˆ ์„ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์ดํ•ดํ•˜๊ณ , ์™„์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง DB ์ ‘๊ทผ ๊ธฐ์ˆ ์˜ ์›๋ฆฌ์™€ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ณ , ๋” ๊นŠ์ด์žˆ๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., ๋ฐฑ์—”

www.inflearn.com


Java์˜ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ๊ธฐ์ˆ ์€ 1997๋…„ JDBC (JDK 1.1)์— ์‹œ์ž‘๋˜์–ด 20๋…„์˜ ์—ญ์‚ฌ๋ฅผ ๊ฑฐ์น˜๋ฉฐ ๋ฐœ์ „ํ•ด์™”์Šต๋‹ˆ๋‹ค.

Oracle, MySql, PostgreSql ๊ฐ™์€ ๋ฒค๋”๋ฅผ ์ œ์™ธํ•˜๊ณ  Java์˜ DB ์ ‘๊ทผ๊ธฐ์ˆ ๋งŒ ๊ณต๋ถ€ํ•œ๋‹คํ•ด๋„ ๊ทธ ๊ทœ๋ชจ๊ฐ€ ๋„ˆ๋ฌด ์ปค์ ธ ์™œ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€, ๋‚ด๋ถ€์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ์ดํ•ดํ•˜๊ธฐ๋Š” ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฒŒ๋‹ค๊ฐ€ ๊ธฐํƒ€ ๋„๊ตฌ๋“ค Hibernate Envers, Hypersistence(vladmihalcea), flyway ... ๋‹จ์ˆœ ์‚ฌ์šฉ๋ฒ•๋งŒ ์ฐพ์•„๋„ ๋์ด ์•ˆ๋ณด์ด์ฃ 

 

 

 

๐Ÿ€ ์ž๋ฐ” ์ฝ”๋“œ๋กœ DB๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

try {
    // JDBC 4.0 ๋ถ€ํ„ฐ๋Š” META-INF/services/java.sql.Driver๋ฅผ ์ฝ์–ด DriverManager๋ฅผ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค.
    // ํ•„์š”ํ•˜๋‹ค๋ฉด Class.forName("com.my.db.jdbc.Driver") ์™€ ๊ฐ™์ด ์ง์ ‘ DriverManager์— ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
    connection = DriverManager.getConnection(jdbcUrl, username, password)
    println("Database connection established.")
 
    // Statement ์ƒ์„ฑ
    statement = connection.createStatement()
 
    // SQL ์ฟผ๋ฆฌ ์‹คํ–‰
    val sql = "SELECT * FROM your_table"
    resultSet = statement.executeQuery(sql)
 
    // ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ
    while (resultSet.next()) {
        // ์ฒซ ๋ฒˆ์งธ ์ปฌ๋Ÿผ ๊ฐ’์„ String์œผ๋กœ ์ฝ๊ธฐ
        val firstColumnValue = resultSet.getString(1)
        println(firstColumnValue)
    }
} catch (e: Exception) {
    e.printStackTrace()
} finally {
    // DB ์ปค๋„ฅ์…˜์„ ๋‹ซ์•„์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. Java7 ๋ถ€ํ„ฐ๋Š” try-resource ๋ฌธ (kotlin ์—์„  use)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŽธํ•ฉ๋‹ˆ๋‹ค.
    resultSet?.close()
    statement?.close()
    connection?.close()
}

 

 

 

 

๐Ÿ€ JDBC

์ž๋ฐ”์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ JDBC(Java DataBase Connectivity) API ๋‹จ ํ•˜๋‚˜ ๋ฟ์ด๋‹ค. ์ด๋Š” ์ž๋ฐ”์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” DB ์—ฐ๊ฒฐ ์ธํ„ฐํŽ˜์ด์Šค์ด๊ณ  ์‹ค์ œ ๊ตฌํ˜„์ฒด๋Š” ๊ณต์‹ DB ๋ฒค๋”์‚ฌ (Oracle, MS..)์—์„œ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‚˜๋งŒ์˜ Driver๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋™์ผํ•˜๊ฒŒ JDBC Driver ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋“ฑ๋กํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

JDBC๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ์Šต๋‹ˆ๋‹ค.

DataAccess๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๋งŒ๋“  ์ฝ”๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค (DAO, Data Access Object, Repository ๋“ฑ์œผ๋กœ๋„ ๋ถˆ๋ฆฐ๋‹ค.)

  • JDBC Driver: ์‹ค์ œ DB์™€ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ๋‹ด๋‹นํ•œ๋‹ค.
  • DataSource : ๊ฐ์ข… ์„ค์ • ๋ฐ ์ปค๋„ฅ์…˜ ํ’€์„ ๊ด€๋ฆฌํ•œ๋‹ค.
    ** ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋‹ˆ์ง€๋งŒ, ๋‹จ์ˆœํžˆ ํ•œ๋ฒˆ ์—ฐ๊ฒฐ ํ•˜๋Š”๊ฑฐ๋ผ๋ฉด DriverManger ์œ ํ‹ธ์„ ์ง์ ‘ ๊บผ๋‚ด ๊ฐ„๋‹จํžˆ ์ปค๋„ฅ์…˜์„ ์–ป๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
์‹ค์ œ ๊ตฌํ˜„์ฒด๋Š” ์ž๋ฐ”์˜ ๊ณต์‹ DB ๋ฒค๋”์‚ฌ(MS, Oracle...)๊ฐ€ ๋งŒ๋“ค์–ด์„œ ์ œ๊ณตํ•˜๋ฉฐ Java1.4 ์ดํ›„ ๋ฒ„์ „ ์ž๋ฐ” ์„ค์น˜์‹œ META-INF/services/java.sql.Driver ๊ฒฝ๋กœ์— ํฌํ•จ๋˜์–ด์žˆ๋‹ค. 

์ฐธ๊ณ ๋กœ ๊ทธ ์ด์ „์—” Driver ๊ตฌํ˜„์ฒด๋ฅผ ์ง์ ‘ ๋กœ๋“œํ•ด์„œ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ์ปดํŒŒ์ผ ์‹œ์ ์— ๋ชจ๋“  DB Driver ํด๋ž˜์Šค๋ฅผ ์ฝ๋Š”๊ฑด ๋ถˆํ•„์š”ํ•˜๋ฏ€๋กœ Class.forName("oracle.jdbc.driver.OracleDriver") ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด ๋Ÿฐํƒ€์ž„์— ํ•„์š”ํ•œ DB ๊ตฌํ˜„์ฒด๋งŒ ํฌํ•จ ๋˜๋„๋ก ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค.

DriverManager์— ๋“ฑ๋ก๋œ JDBC Driver๋Š” ๋“œ๋ผ์ด๋ฒ„ ๊ฐ์ฒด ์ƒ์„ฑ์‹œ์ ์— static {..} ์œผ๋กœ ํด๋ž˜์Šค๋ฅผ ์ฝ์–ด์™€์„œ ๋“ฑ๋ก๋œ๋‹ค.

 

 

 

 

๐Ÿ€ Java DataSource  ์™€ DB Connection Pool (DBCP)

์‚ฌ์šฉ์ž๋Š” JDBC Connection ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด DB์™€ ํ†ต์‹ ์„ ํ•ฉ๋‹ˆ๋‹ค.

DB๋Š” ์ปค๋„ฅ์…˜์— ํด๋ผ์ด์–ธํŠธ์šฉ ์„ธ์…˜์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ์š”์ฒญ์„ ๋ฐ›๊ณ  ์›ํ•˜๋Š” ์‘๋‹ต์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์ž๋ฐ” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉ๋˜๋Š” Connection๊ณผ Session์˜ ์˜๋ฏธ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

< ์ปค๋„ฅ์…˜์ด ์—ฐ๊ฒฐ๋˜๋ฉด ์„ธ์…˜์ด ๋งŒ๋“ค์–ด์ง€๊ฒŒ ๋˜๊ณ , ์„ธ์…˜์— ์ž‘์—…์„ ์š”์ฒญํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋œ๋‹ค. >
- ์ปค๋„ฅ์…˜(Connection)์€ ํด๋ผ์ด์–ธํŠธ ํ”„๋กœ์„ธ์Šค์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค๊ฐ„์˜ ๋ฌผ๋ฆฌ์ ์ธ TCP/IP ํ†ต์‹  ๊ฒฝ๋กœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
- ์„ธ์…˜(Sessions)์€ ๋…ผ๋ฆฌ์ ์ธ ๊ฐœ๋…์œผ๋กœ ํ•œ ์œ ์ €์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๋ณดํ†ต ์ปค๋„ฅ์…˜๊ณผ 1:1 ์œผ๋กœ ๋Œ€์‘๋œ๋‹ค.
- ํ”„๋กœ์„ธ์Šค(Process)๋Š” ์‹ค์ œ ์„œ๋ฒ„์— ์ƒ์„ฑ๋˜๋Š” ์šด์˜์ฒด์ œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

๋‹ค๋งŒ ํ•ด๋‹น DB๊ฐ€ ์ž์ฒด์ ์ธ Connection Pool์ด๋‚˜ Shared Server Mode ๊ฐ™์€ ๊ฑธ ์ œ๊ณตํ•ด์ค€๋‹ค๋ฉด ํ•œ ์ปค๋„ฅ์…˜์— ์—ฌ๋Ÿฌ๊ฐœ์˜ ์„ธ์…˜์ด ๋ถ™์„ ์ˆ˜๋„ ์žˆ๋‹ค.

DataSource๋Š” DB ์ปค๋„ฅ์…˜ ํ’€๋ง๊ณผ ์„œ๋ฒ„ ์„ค์ •์„ ๋‹ด๋‹นํ•˜๊ณ , JDBC Driver๋Š” ์‹ค์ œ ๋ฒค๋”(Oracle, MySql..) ์™€ ํ†ต์‹ ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

 

์ฐธ๊ณ ๋กœ ์ž๋ฐ” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉ๋˜๋Š” Connection๊ณผ Session์˜ ์˜๋ฏธ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

< ์ปค๋„ฅ์…˜์ด ์—ฐ๊ฒฐ๋˜๋ฉด ์„ธ์…˜์ด ๋งŒ๋“ค์–ด์ง€๊ฒŒ ๋˜๊ณ , ์„ธ์…˜์— ์ž‘์—…์„ ์š”์ฒญํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋œ๋‹ค. >
- ์ปค๋„ฅ์…˜(Connection)์€ ํด๋ผ์ด์–ธํŠธ ํ”„๋กœ์„ธ์Šค์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค๊ฐ„์˜ ๋ฌผ๋ฆฌ์ ์ธ TCP/IP ํ†ต์‹  ๊ฒฝ๋กœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
- ์„ธ์…˜(Sessions)์€ ๋…ผ๋ฆฌ์ ์ธ ๊ฐœ๋…์œผ๋กœ ํ•œ ์œ ์ €์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๋ณดํ†ต ์ปค๋„ฅ์…˜๊ณผ 1:1 ์œผ๋กœ ๋Œ€์‘๋œ๋‹ค.
- ํ”„๋กœ์„ธ์Šค(Process)๋Š” ์‹ค์ œ ์„œ๋ฒ„์— ์ƒ์„ฑ๋˜๋Š” ์šด์˜์ฒด์ œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

๋‹ค๋งŒ ํ•ด๋‹น DB๊ฐ€ ์ž์ฒด์ ์ธ Connection Pool์ด๋‚˜ Shared Server Mode ๊ฐ™์€ ๊ฑธ ์ œ๊ณตํ•ด์ค€๋‹ค๋ฉด ํ•œ ์ปค๋„ฅ์…˜์— ์—ฌ๋Ÿฌ๊ฐœ์˜ ์„ธ์…˜์ด ๋ถ™์„ ์ˆ˜๋„ ์žˆ๋‹ค.

Server์˜ Connection ๊ฐ์ฒด <-> ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค Client Connection (TCP)

 

 

์œ„ ๊ทธ๋ฆผ์„ ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ๋‹ค์‹œ ๊ทธ๋ ค๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ์ปค๋„ฅ์…˜ ํ’€์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด DataSource ๋ฅผ ์‚ฌ์šฉํ•˜์ง€์•Š๊ณ  DriverManager ๋งŒ์œผ๋กœ ๊ฐ„๋‹จํžˆ ์—ฐ๊ฒฐ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

๋‹ค๋งŒ ์–ด๋–ค ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋“  ์ž๋ฐ”๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด JDBC Driver ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผํ•˜๋Š”๊ฑด ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

  • <DataSource ๋˜๋Š” DriverManager ์ง์ ‘ ์‚ฌ์šฉ> ๋‹น์—ฐํžˆ ์Œฉ JDBC ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ด์„œ ์ปค๋„ฅ์…˜์„ ์–ป์–ด์™€๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • <Spring JDBC Template> ๋‚ด๋ถ€ ํ•„๋“œ๋กœ DataSource๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
  • <MyBatis (๊ตฌ iBatis)> SqlSessionFactory (spring mybatis๋Š” SqlSessionFactoryBean) ์— DataSource๋ฅผ ๋“ฑ๋กํ•ด๋‘๊ณ  ์„ธ์…˜์ธ SqlSessionTemplate ๋ฅผ ์ด์šฉํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
  • < JPA > EntityManagerFactory์— DataSource๋ฅผ ๋“ฑ๋กํ•ด๋‘๊ณ  ์„ธ์…˜์ธ EntityManager๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•œ๋‹ค.
  • < Spring Data JPA > ๊ตฌํ˜„์ฒด(SimpleJpaRepository)๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด EntityManager๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•œ๋‹ค. ํ•ด๋‹น ๋นˆ์€ Spring AOP ํ”„๋ก์‹œ๋ฅผ ๊ฑฐ์ณ ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์— ๋งž์ถฐ ์ ์ ˆํ•œ ์ปค๋„ฅ์…˜์— ํ• ๋‹น๋œ๋‹ค.

์ปค๋„ฅ์…˜๊ณผ ์ปค๋„ฅ์…˜ ํ’€์— ๋Œ€ํ•˜์—ฌ ๋” ์ž์„ธํ•˜๊ฒŒ ์•Œ๊ณ ์‹ถ๋‹ค๋ฉด ์•„๋ž˜ ๊ธ€์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

 

JDBC Connection ์— ๋Œ€ํ•œ ์ดํ•ด, HikariCP ์„ค์ • ํŒ

JDBC๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์•„์˜ˆ ๋ชจ๋ฅธ๋‹ค๋ฉด [10๋ถ„ ํ…Œ์ฝ”ํ†ก] ์ฝ”์ฝ”๋‹ฅ์˜ JDBC ์˜์ƒ์„ ํ•œ๋ฒˆ ๋ณด๊ณ  ์˜ค์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค. ๐Ÿ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์‹œ์ž‘์€ ๊ฒฐ๊ตญ JDBC JVM์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์Šคํ”„๋ง์ด๊ฑด JPA๋ฅผ ์“ฐ๊ฑด ๊ฒฐ๊ตญ ๋งˆ์ง€๋ง‰์—” JDBC (J

jiwondev.tistory.com

๋ณดํ†ต DriverManager๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ผญ ํ•„์š”ํ•˜๋‹ค๋ฉด ์Šคํ”„๋ง์˜ DriverManagerDataSource ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

 

 

๐Ÿ€ Transaction

ํŠธ๋žœ์žญ์…˜์— ๊ด€ํ•œ ๊ธฐ์ดˆ ๋‚ด์šฉ์€ ์ด๋ฏธ ๊ธ€์ด ์žˆ์œผ๋ฏ€๋กœ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค.

2021.08.10 - [๐Ÿ‘€๊ธฐ๋ณธ ์ง€์‹/CS ๊ธฐ๋ณธ์ง€์‹] - ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ์ˆ˜์ค€(DB Isolation Level)

2022.08.15 - [๐Ÿ‘€๊ธฐ๋ณธ ์ง€์‹/Java ๊ธฐ๋ณธ์ง€์‹] - ์˜์‹์˜ ํ๋ฆ„๋Œ€๋กœ ์จ๋ณด๋Š” DB ํŠธ๋žœ์žญ์…˜ ์ด์•ผ๊ธฐ

 

 

 

 

๐Ÿ€ Spring Transaction ์ถ”์ƒํ™”

JDBC๋ฅผ ์ด์šฉํ•˜๋ฉด DB์˜ ์ƒ์„ธ ์ŠคํŽ™์„ ๋ชฐ๋ผ๋„ Java ์ฝ”๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ, ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ธฐ์—” ์—ฌ๋Ÿฌ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • JDBC๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด try-catch-finally ๊ฐ™์€ ํŠน์ • ํŒจํ„ด์˜ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค.
  • Connection ๊ฐ™์€ ์ž์›์ด ๋ˆ„์ˆ˜๋˜์ง€ ์•Š๋„๋ก ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. 
    JDBC ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์ˆจ๊ธฐ๋ ค๊ณ ํ•ด๋„, Transaction ์„ค์ • ๋ฐ ๋™๊ธฐํ™” ๋•Œ๋ฌธ์— ๊ฐ•์ œ๋กœ ์„œ๋น„์Šค์ฝ”๋“œ์™€ ์„ž์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • JDBC์˜ `throws SQLException` ์„ ์–ด๋”˜๊ฐ€์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

 

ํ•ด๋‹น ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•ด ์Šคํ”„๋ง์—์„  ํŠธ๋žœ์žญ์…˜์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”์ƒํ™” ํ•ด๋‘์—ˆ์Šต๋‹ˆ๋‹ค. 

์ •ํ™•ํžˆ๋Š” PlatformTransactionManager <- AbstractPlatformTransactionManager <- { ๊ตฌํ˜„์ฒด } ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ์Šต๋‹ˆ๋‹ค.

public interface PlatformTransactionManager{

    TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
            throws TransactionException;


    void commit(TransactionStatus status) throws TransactionException;


    void rollback(TransactionStatus status) throws TransactionException;

}

public interface AbstractPlatformTransactionManager extends PlatformTransactionManager{
    /**
     * begin, commit, rollback ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์‹ค์ œ ๋™์ž‘์€ ์•„๋ž˜ ๋ฉ”์„œ๋“œ์— ์œ„์ž„ํ•œ๋‹ค 
     * (EX commit()-> processCommit() -> doCommit())
     */    
    protected void doCommit(DefaultTransactionStatus status) throws TransactionException;

    protected void doRollback(DefaultTransactionStatus status) throws TransactionException;
	
    protected void doBegin(Object transaction, TransactionDefinition definition)
            throws TransactionException;
}

 

 

์ด์ œ ๊ฐœ๋ฐœ์ž๋Š” PlatformTransactionManager ๋ฅผ ๊บผ๋‚ด์„œ getTransaction, commit, rollback๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋ณดํ†ต try-catch ์—†์ด ํŽธ๋ฆฌํ•˜๊ฒŒ ์“ฐ๋ ค๊ณ  ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ํ…œํ”Œ๋ฆฟ ํด๋ž˜์Šค(TransactionTemplate)๋กœ ๋ž˜ํ•‘ํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    // DataSource๋กœ PlatformTransactionManager ์ƒ์„ฑ
    return new DataSourceTransactionManager(dataSource);
}

@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
    // PlatformTransactionManager ์œผ๋กœ TransactionTemplate ์ƒ์„ฑ 
    return new TransactionTemplate(transactionManager);
}
@Service
@RequiredArgsConstructor
public class MyService {
    private final TransactionTemplate transactionTemplate;
    
    /** โญ๏ธ Java8 ๋žŒ๋‹ค๋กœ ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋ฅผ ์“ฐ๋ฉด ๋งค์šฐ ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค. */
    void myTransactionalLambda() {
        transactionTemplate.executeWithoutResult(status -> { /*..ํŠธ๋žœ์žญ์…˜ ๋กœ์ง..*/ });
        
        var result = transactionTemplate.execute(status -> { /*..ํŠธ๋žœ์žญ์…˜ ๋กœ์ง..*/ return null; });
    }
    
    /** ์ต๋ช…ํด๋ž˜์Šค๋กœ ์‚ฌ์šฉํ•  ๋• ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค */
    MyResultDto myTransactionalMethod() {
        return transactionTemplate.execute(new TransactionCallback<MyResultDto>() {
            @Override
            public MyResultDto doInTransaction(TransactionStatus status) {
                // ์—ฌ๊ธฐ์— ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
                // ํ•„์š”ํ•œ ๊ฒฝ์šฐ status.setRollbackOnly(); ํ˜ธ์ถœ๋กœ ๋กค๋ฐฑ์„ ์ง€์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
                MyResultDto result = new MyResultDto();
                return result;
            }
        });
    }

    void myTransactionalMethod() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                // ์—ฌ๊ธฐ์— ํŠธ๋žœ์žญ์…˜์„ ์ ์šฉํ•  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
                // ํ•„์š”ํ•œ ๊ฒฝ์šฐ status.setRollbackOnly(); ํ˜ธ์ถœ๋กœ ๋กค๋ฐฑ์„ ์ง€์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
            }
        });
    }
}

 

 

์‹ค์ œ ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ๊ทธ๋ฆผ์œผ๋กœ ๊ทธ๋ ค๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
์ฐธ๊ณ ๋กœ ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ DB Connection์„ ๋ฌผ๊ณ ์žˆ์„ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ๋ฐ˜๋Œ€๋Š” ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ๋„ ์—„์ฒญ ๋ณต์žกํ•ด์ง€๊ณ  ์Šค๋ ˆ๋“œ ๊ฒฝํ•ฉ๋ฌธ์ œ๋„ ์žˆ๊ธฐ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์• ์ดˆ์— ์ปค๋„ฅ์…˜ํ’€๋กœ ๊ด€๋ฆฌํ•˜๋Š”๋ฐ ํ•œ ์ปค๋„ฅ์…˜์„ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ์‚ฌ์šฉํ•  ํ•„์š”๋„ ์—†๊ตฌ์š”.

 

๋ณต์žกํ•ด๋ณด์ผ์ˆ˜ ์žˆ๋Š”๋ฐ ์—ญํ• ์— ๋”ฐ๋ผ ๋ถ„๋ฆฌํ•ด๋ณด๋ฉด ์ƒ๊ฐ๋ณด๋‹ค ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด DataSourceTransactionManager.doBegin() ๋ฅผ ๊นŒ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

/**
 * doBegin: ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์Œฉ JDBC ์—์„œ๋Š”์‹œ์ž‘ ๋ฉ”์„œ๋“œ๋Š” ๋”ฐ๋กœ ์—†๊ณ  
   setAutoCommit(false) ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
 * 
 * @param transaction ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์— ๊ด€ํ•œ ์„ค์ • ์ •๋ณด ๋ฐ Connection ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. 
   ๊ตฌํ˜„์ฒด์— ๋”ฐ๋ผ TransactionObject ํƒ€์ž…์ด ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.
 * (** Connection ์ž์› ํ• ๋‹น ๋ฐ ํšŒ์ˆ˜๋Š” TransactionSynchronizationManager ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.)
 
 * @param definition ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ, ๊ฒฉ๋ฆฌ, ํƒ€์ž„์•„์›ƒ, ์ฝ๊ธฐ ์ „์šฉ๋“ฑ์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
 */
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;
    
try {
    /**
     * 1. ์‚ฌ์šฉ์ค‘์ธ Connection์ด ์—†์œผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
     * 2. ์‚ฌ์šฉ์ค‘์ธ Connection์ด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์‚ฌ์šฉ(๋™๊ธฐํ™”)์ค‘์ด๋ผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
     *
     * ์ฆ‰ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” Connection์ด ์‚ฌ์šฉ์ค‘์ด ์•„๋‹ˆ๋ผ๋ฉด ํ•ด๋‹น ์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
     */
    if (!txObject.hasConnectionHolder() ||
            txObject.getConnectionHolder().isSynchronizedWithTransaction()) {

        Connection newCon = obtainDataSource().getConnection();

        // ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋œ Connection์„ ConnectionHolder๋ฅผ ์ด์šฉํ•ด ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
        // ConnectionHolder ๋Š” ์ฐธ์กฐ ์นด์šดํŠธ๋ฅผ ์ด์šฉํ•œ ์ปค๋„ฅ์…˜ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ, rollback ์ง€์›, DB ํŠธ๋žœ์žญ์…˜ ์ƒํƒœ ์ €์žฅ๋“ฑ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
        txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
    }


    // ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ํ•ด๋‹น Connection ์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ฒŒ {๋™๊ธฐํ™”=true} ๋กœ ์„ค์ •ํ•œ ๋’ค ์ปค๋„ฅ์…˜์„ ๊บผ๋‚ด์˜ต๋‹ˆ๋‹ค.
    txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
    con = txObject.getConnectionHolder().getConnection();

    // ํŠธ๋žœ์žญ์…˜ ์ •์˜์— ๋”ฐ๋ผ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. prepareConnectionForTransaction ๊นŒ๋ณด๋ฉด ๊ทธ๋ƒฅ JDBC ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
    Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
    txObject.setPreviousIsolationLevel(previousIsolationLevel);
    txObject.setReadOnly(definition.isReadOnly());

    // ํŠธ๋žœ์žญ์…˜์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๊ธฐ๋•Œ๋ฌธ์—, autoCommit=true ์œผ๋กœ ์„ค์ • ๋œ ๊ฒฝ์šฐ ์ž„์‹œ๋กœ false ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
    // ์ด๋Š” setMustRestoreAutoCommit ์— ๊ธฐ๋กํ•ด๋’€๋‹ค๊ฐ€ ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๋Š” ์‹œ์ ์— ๊ธฐ์กด autoCommit ์„ค์ •์œผ๋กœ ์›์ƒ ๋ณต๊ท€๋ฉ๋‹ˆ๋‹ค.
    if (con.getAutoCommit()) {
        txObject.setMustRestoreAutoCommit(true);
        con.setAutoCommit(false);
    }

    // ์ปค๋„ฅ์…˜ ์—ฐ๊ฒฐ์„ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค. ๊นŒ๋ณด๋ฉด ๊ทธ๋ƒฅ JDBC๋กœ "SET TRANSACTION READ ONLY" ๊ฐ™์€ ๊ฑธ ์„ค์ •ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
    prepareTransactionalConnection(con, definition);
    txObject.getConnectionHolder().setTransactionActive(true);

    // ํŠธ๋žœ์žญ์…˜ ์ •์˜์— ๋”ฐ๋ผ ํƒ€์ž„์•„์›ƒ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    int timeout = determineTimeout(definition);
    if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
        txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
    }

    // ConnectionHolder๊ฐ€ ์—ฌ๊ธฐ์—์„œ ์ฒซ ์ƒ์„ฑ๋˜์—ˆ๋‹ค๋ฉด TransactionSyncManager ์— ๋“ฑ๋กํ•ด ์ž์›์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    // Map (key=Datasource, value=ConnectionHolder)
    if (txObject.isNewConnectionHolder()) {
        TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
    }
}

catch (Throwable ex) {
    // ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์—ฐ๊ฒฐ์„ ํ•ด์ œํ•˜๊ณ  TransactionSyncManager ์— ์ž์› ํ•ด์ œ๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
    if (txObject.isNewConnectionHolder()) {
        DataSourceUtils.releaseConnection(con, obtainDataSource());
        txObject.setConnectionHolder(null, false);
    }
    throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}

 

 

 

๐Ÿค” @Transactional์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฑฐ์—์š”?

์ด๋Š” ์Šคํ”„๋ง AOP ๋ฅผ ํŠธ๋žœ์žญ์…˜์— ์ ์šฉํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋™์ž‘๊ณผ์ •์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

2022.03.09 - [๐ŸŒฑ Spring Framework/Spring Core] - ์ž๋ฐ” AOP์˜ ๋ชจ๋“  ๊ฒƒ(Spring AOP & AspectJ)

  • 1๏ธโƒฃ @EnableTransactionManagement ๋กœ ์Šคํ”„๋ง ํŠธ๋žœ์žญ์…˜ AOP๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.
    * @SpringBootApplication -> @EnableAutoConfiuration์— ์˜ํ•ด EnableTransactionManagement ์ž๋™ ํ™œ์„ฑํ™” ๋จ


  • 2๏ธโƒฃ ํ™œ์„ฑํ™”๋˜๋ฉด @Import(TransactionManagementConfigurationSelector.class) ์— ์˜ํ•ด ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.
    ์šฐ๋ฆฌ๋Š” ์Šคํ”„๋ง ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์•„๋ž˜ ์ฝ”๋“œ์— ๋”ฐ๋ผ ProxyTransactionManagementConfiguration ๊ฐ€ ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	/** {์Šคํ”„๋ง ํ”„๋ก์‹œ ๊ฐ์ฒด} or {AspectJ ํ”„๋ก์‹œ ๊ฐ์ฒด} ์ค‘ ํ•˜๋‚˜๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

}

 

  • 3๏ธโƒฃ ProxyTransactionManagementConfiguration ์œผ๋กœ ํŠธ๋žœ์žญ์…˜ AOP์— ํ•„์š”ํ•œ ๊ตฌํ˜„ ํด๋ž˜์Šค๋“ค์ด ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.
/** Advice (ํ”„๋ก์‹œ๋กœ ์ ์šฉํ•  ์‹ค๋™์ž‘ ์ฝ”๋“œ) ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” Advisor ํด๋ž˜์Šค, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ AOP(BeanFactory)์—์„œ ์ ์šฉ๋จ*/
BeanFactoryTransactionAttributeSourceAdvisor;

/** ์‚ฌ์šฉ์ž ํŠธ๋žœ์žญ์…˜ ์„ค์ •(TransactionAttributeSource)๋ฅผ ์ฝ์–ด๋“ค์ด๋Š” ํด๋ž˜์Šค, ์–ด๋…ธํ…Œ์ด์…˜์„ ์ฝ๋Š” ๊ตฌํ˜„์ฒด ์‚ฌ์šฉ */
AnnotationTransactionAttributeSource;

/** PointCut(์ ์šฉ ์กฐ๊ฑด), ์ฆ‰ invoke() ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์„œ AOP์„ ์ ์šฉํ•  ์กฐ๊ฑด์„ ์ง€์ •ํ•˜๋Š” ํด๋ž˜์Šค */
TransactionInterceptor;

 

transactionInterceptor ์ฝ”๋“œ๋ฅผ ๋ณด์‹œ๋ฉด `setTransactionManager(this.txManager)` ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์— PlatformTransactionManager ๊ฐ€ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ด์ œ @Transactional ์„ ์‚ฌ์šฉํ•˜๋ฉด AOP๋ฅผ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜์ด ๊ฑธ๋ฆฌ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


์„ค์ •๋ถ€๋ถ„์ด ๋ณต์žกํ•˜์ง€๋งŒ ๋™์ž‘์€ ์ƒ๊ฐ๋ณด๋‹ค ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

  1. ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋ฉ”์„œ๋“œ์— @Transactional ๋ช…์‹œ
  2. ํด๋ž˜์Šค๊ฐ€ ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜๋Š” ์‹œ์ ์— Spring BeanPostProcessor ์— ์˜ํ•œ AOP ํ”„๋ก์‹œ ์ƒ์„ฑ 
  3. PlatformTransactionManager ์œผ๋กœ ๋ฉ”์„œ๋“œ ์‹œ์ž‘์ „, ํ›„์— ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ ์ง‘์–ด๋„ฃ๊ธฐ

 

 

 

๐Ÿค” ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ์˜ต์…˜..? ์ด๋Ÿฐ๊ฒŒ JDBC์— ์žˆ์—ˆ๋˜๊ฐ€์š”?

ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ๋ ˆ๋ฒจ(Transaction Isolation)์€ JDBC์—์„œ๋„ ์†์‰ฝ๊ฒŒ ์„ค์ •๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

// isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);


๊ทธ๋Ÿฐ๋ฐ ์ „ํŒŒ์˜ต์…˜ (Transaction Propagation)์€ ์„ค์ •์— ๋”ฐ๋ผ ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์„ ์—ฐ์žฅํ•˜๊ฑฐ๋‚˜, ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์„ ๋งŒ๋“œ๋Š” ๋“ฑ์˜ ๊ธฐ๋Šฅ์ธ๋ฐ 

์ด๋Š” ๋‹น์—ฐํžˆ JDBC ์ž์ฒด์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์€ ์•„๋‹™๋‹ˆ๋‹ค. TransactionDefinition ์„ค์ •๊ฐ’์— ๋”ฐ๋ผ ์Šคํ”„๋ง์ด ๊ตฌํ˜„ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

@Transactional ์ด๋‚˜ TxTemplate์— ๋ช…์‹œํ•œ๊ฑธ ์ฝ์–ด์„œ ์ „ํŒŒ ์˜ต์…˜์„ TransactionDefinition ๊ฐ์ฒด๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

๊ตฌํ˜„ ๋‚ด์šฉ์€ ๊นŒ๋ด์•ผ๊ฒ ์ง€๋งŒ, JDBC์˜ savePoint ๋“ฑ์„ ์ ์ ˆํ•˜๊ฒŒ ํ™œ์šฉํ•˜๋ฉด ๋ถˆ๊ฐ€๋Šฅํ•œ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. 

์ถœ์ฒ˜&nbsp;https://jeong-pro.tistory.com/228

 

์ฐธ๊ณ ๋กœ JDBC savePoint ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

// try-with-resources ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋ฆฌ์†Œ์Šค ํ•ด์ œ
try (Connection conn = DriverManager.getConnection(url, user, password)) {
    conn.setAutoCommit(false);

    // ์ฒซ ๋ฒˆ์งธ ์—…๋ฐ์ดํŠธ
    try (PreparedStatement pstmt1 = conn.prepareStatement(sql1)) { pstmt1.executeUpdate(); }

    // Savepoint ์„ค์ •
    Savepoint savepoint1 = conn.setSavepoint("Savepoint1");

    // ๋‘ ๋ฒˆ์งธ ์—…๋ฐ์ดํŠธ
    try (PreparedStatement pstmt2 = conn.prepareStatement(sql2)) { pstmt2.executeUpdate(); }

    conn.commit();
} catch (SQLException e) {
    try {
        if (savepoint1 != null) {
            // Savepoint๊ฐ€ ์žˆ๋‹ค๋ฉด, ๊ฑฐ๊ธฐ๋กœ ๋กค๋ฐฑ ( savePoint ์ดํ›„ ์ž‘์—…๋งŒ ๋กค๋ฐฑ๋จ )
            conn.rollback(savepoint1);
            
            conn.commit(); // ๋‹ค์‹œ ์ปค๋ฐ‹
        }
    } catch (SQLException se) {
       /* .. */
    }
}

 

 

๊ฐ™์ด๋ณด๋ฉด ์ข‹์€ ๊ธ€
2021.08.18 - [๐ŸŒฑ Spring Framework/Spring Core] - @Transactional ์˜ ๋™์ž‘์›๋ฆฌ, ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €

2021.08.18 - [๐ŸŒฑ Spring Framework/Spring Core] - DB์™€ @Transactional์„ ์‚ฌ์šฉํ• ๋•Œ ์ž์ฃผ ๋‚˜์˜ค๋Š” ์‹ค์ˆ˜

 

 

 

 

๐Ÿ€ ์ž๋ฐ”์˜ Exception ์ฒ˜๋ฆฌ

  • Throwable: throw๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค
  • Throwable->Error: ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋Š” JVM ์ž์ฒด ์—๋Ÿฌ (๋ฉ”๋ชจ๋ฆฌ๋ถ€์กฑ, ์‹œ์Šคํ…œ OS ์˜ค๋ฅ˜)
  • Throwable->Exception: ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์—๋Ÿฌ
  • Throwable->Exception -> RuntimeException: ์–ธ์ฒดํฌ๋“œ ์˜ˆ์™ธ, ์ฆ‰ ๋Ÿฐํƒ€์ž„์—์„œ ์ฒ˜๋ฆฌํ•ด์•ผํ•˜๋Š” ์˜ˆ์™ธ

 

์ž๋ฐ”์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ์œ„ ๋ฉ”์„œ๋“œ๋กœ ๋˜์ ธ์ง„๋‹ค.

๋๊นŒ์ง€ ์ฒ˜๋ฆฌํ•˜์ง€์•Š์œผ๋ฉด main() ์Šค๋ ˆ๋“œ๊นŒ์ง€ ์˜ˆ์™ธ๊ฐ€ ์ „๋‹ฌ๋˜๊ณ  ์ด๋Š” ์‹œ์Šคํ…œ์ด ์ข…๋ฃŒ๋œ๋‹ค. ์›น ์„œ๋ฒ„๋ผ tomcat ๋“ฑ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ main ๊นŒ์ง€ ๊ฐ€์ง€์•Š๊ณ  WAS๊ฐ€ ๋ฐ›์•„์„œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

๋‹จ์ˆœํžˆ ์„ค๋ช…๋งŒ ๋“ค์œผ๋ฉด RuntimeException์„ ์‚ฌ์šฉํ•˜์ง€์•Š๊ณ , ์ „๋ถ€ ์ฒดํฌ ์˜ˆ์™ธ (throws ๋กœ ๋ช…์‹œํ•˜๊ณ  try-catch๊ฐ€ ๊ฐ•์ œ๋˜๋Š” ์˜ˆ์™ธ)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŽธํ•  ๊ฒƒ ๊ฐ™์ง€๋งŒ, ํŠน์ • Exception ๊ฐ์ฒด์— ์˜์กด์„ฑ์ด ์ƒ๊ธฐ๊ฒŒ๋˜๊ณ  ์ฝ”๋“œ ์ž์ฒด๊ฐ€ ๋”๋Ÿฌ์›Œ์ง„๋‹ค.



๊ทธ๋ž˜์„œ ๋ณดํ†ต ์›น ์„œ๋ฒ„์˜ ๊ฒฝ์šฐ RuntimeException์„ ๋งŽ์ด ํ™œ์šฉํ•œ๋‹ค.

์‹ค์ œ๋กœ ์ฒดํฌ ์ต์…‰์…˜์œผ๋กœ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ•์ œํ•ด๋ด์•ผ ์ฝ”๋“œ์ƒ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฒŒ ์—†์–ด์„œ ์˜ˆ์™ธ๋ฅผ try{..} catch ๋กœ ์•„๋ฌด ์ฒ˜๋ฆฌ๋„ ํ•˜์ง€์•Š๊ณ  ๋ฌด์‹œํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋„ ๋งŽ์•˜์—ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ์—๋Ÿฌ๋Š” ์ตœ์ƒ์œ„์—์„œ ControllerAdvice (๋˜๋Š” main ์ฝ”๋“œ) ์—์„œ๋งŒ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š”๊ฒŒ ๋งž๋‹ค.

๋ณดํ†ต RuntimeException์„ ์ƒ์†ํ•œ MyCommonException ๋“ฑ์„ ๋งŒ๋“ค์–ด์„œ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

 

2021.07.07 - [๐ŸŒฑBackend/Java] - #7 Exception (์˜ˆ์™ธ์ฒ˜๋ฆฌ)

 

#7 Exception (์˜ˆ์™ธ์ฒ˜๋ฆฌ)

์ž๋ฐ”์—์„œ์˜ ์˜ค๋ฅ˜, ์˜ˆ์™ธ ์ž๋ฐ”์—์„œ ์˜ˆ์™ธ, ์˜ค๋ฅ˜๋Š” java.lang.Throwable ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฉฐ ๊ตฌํ˜„ํ•˜๋ฉฐ 3๊ฐ€์ง€ ์ข…๋ฅ˜๋กœ ๋‚˜๋‰œ๋‹ค. Error ์ž๋ฐ” ํ”„๋กœ๊ทธ๋žจ ๋ฐ–์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ. ํ”„๋กœ์„ธ์Šค์— ์˜ํ–ฅ, ๋” ์ด์ƒ ์‹คํ–‰ ๋ถˆ๊ฐ€

jiwondev.tistory.com

 

 

 

๐Ÿ€ ์Šคํ”„๋ง์˜ DB Exception ์ฒ˜๋ฆฌ

์Šคํ”„๋ง๋„ ์ด์™€ ๋น„์Šทํ•˜๊ฒŒ DB์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ์ถ”์ƒํ™”ํ•ด๋‘์—ˆ๋‹ค. ( org.springframework.dao.DataAccessException )
์—ฌ๋‹ด์œผ๋กœ DB ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์Šคํ”„๋ง์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์ด ์˜ˆ์™ธ๋ฅผ ์ด๋Ÿฐ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Transient๋Š” ์ผ์‹œ์ ์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. ์ฆ‰ DB ๋‚˜ SQL ์ž์ฒด ๋ฌธ์ œ๊ฐ€ ์•„๋‹Œ {ํƒ€์ž„์•„์›ƒ, ๋ฝ}๊ณผ ๊ฐ™์€ ์˜ˆ์™ธ๊ฐ€ ์—ฌ๊ธฐ์— ์†ํ•œ๋‹ค.

 

์˜ˆ์™ธ๊ฐ€ ์ •์˜ ๋˜์–ด์žˆ๋‹คํ•œ๋“ค, ๋‚ด๊ฐ€ ๋ชจ๋“  DB์˜ ์ต์…‰์…˜์„ DataAccessException ์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์–ด์•ผํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์€ ๊ทธ๋Œ€๋กœ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์Šคํ”„๋ง์—์„  SQLExceptionTranslator ๊ฐ์ฒด๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

SQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);

// exTranslator.translate(String task, String sql, Exception e)
// ์ฐธ๊ณ ๋กœ String task ๋Š” ์ผ์ข…์˜ ์ฃผ์„์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์˜ ๊ฒฝ์šฐ "Hibernate operation: " ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค.
DataAccessException resultEx = exTranslator.translate("select", sql, e);

// ๋ณ€ํ™˜ํ•œ ์˜ˆ์™ธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋˜์งˆ ์ˆ˜๋„ ์žˆ๋‹ค.
throw resultEx;
์‹ค์ œ JDBCTemplate ๋“ฑ์„ ๊นŒ๋ณด๋ฉด, ์˜ˆ์™ธ๊ฐ€ ํ„ฐ์กŒ์„ ๋•Œ ๋“ฑ๋ก๋œ ExceptionTranslator๋กœ ์˜ˆ์™ธ๋ฅผ ๋ณ€ํ™˜ํ•œ๋‹ค.

 

์Šคํ”„๋ง์œผ๋กœ DB๋ฅผ ์‚ฌ์šฉํ•ด๋ดค๋‹ค๋ฉด ์ƒ๊ฐ๋ณด๋‹ค ์—๋Ÿฌ ์ข…๋ฅ˜๊ฐ€ ์ƒ์„ธํ•˜๊ฒŒ ๊ตฌ๋ถ„๋˜์–ด์žˆ๋Š”๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” ์Šคํ”„๋ง์ด ๊ฐ DB ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—๋Ÿฌ๋ณ„๋กœ class SqlErrorCodes ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•ด๋‘์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋Š” org.springframework.jdbc.support.sql-error-codes.xml ํŒŒ์ผ์„ ํ†ตํ•ด ๋นˆ์ด ์ƒ์„ฑ๋œ๋‹ค.

๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ๊นŒ๋ณด๋ฉด SQLErrorCodeSQLExceptionTranslator ๊ฐ€ ๋“ฑ๋ก๋œ ์˜ˆ์™ธ๋ฅผ ์ฝ์–ด์„œ switch ๋ถ„๊ธฐ๋ฅผ ์ณ์ค€๋‹ค.

 

์ฐธ๊ณ ๋กœ ์—ฌ๋Ÿฌ ๊ณผ์ •์„ ๊ฑฐ์ณค์Œ์—๋„ ์ •์˜๋œ ์—๋Ÿฌ๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด, ์—๋Ÿฌ  ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  DataAccessException์„ ์ƒ์†ํ•œ UncategorizedSQLException ์„ ๋˜์ง„๋‹ค. ๋ฌผ๋ก  ์ด๋Š” ๊ตฌํ˜„์ฒด์— ๋”ฐ๋ผ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

 

์–ด์ฐŒ๋ณด๋ฉด ๋‹น์—ฐํ•œ๊ฑฐ์ง€๋งŒ, ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ํŽธํ•˜๊ฒŒ ์“ฐ๋ ค๋ฉด Spring์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

Spring์˜ ์˜์กด์„ฑ๊นŒ์ง€ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋ฅผ ์ฐธ์กฐํ•ด์„œ ์ง์ ‘ ์ž๋ฐ”์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด๋„ ๋˜์ง€๋งŒ, ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋น„์Šค๋Š” DB ๋ฅผ ์ถ”์ƒํ™”ํ•œ ๋ถ€๋ถ„์„ ๋„๋ฉ”์ธ ๋กœ์ง์ฒ˜๋Ÿผ ์ˆœ์ˆ˜ํ•œ Java (POJO)๋กœ ๊ฐ€์ ธ๊ฐˆ ํ•„์š”๋Š” ์—†๋‹ค. Spring Framework๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค๋ฉด ๋Œ€๋ถ€๋ถ„ ํฌ๊ฒŒ ์‹ ๊ฒฝ์“ฐ์ง€์•Š์•„๋„ ๋˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.

 

 

๐Ÿ€ ์—ฌ๋‹ด

๊ทผ๋ฐ ์Šคํ”„๋ง์ด ์ด ๋ชจ๋“ ๊ฑธ ๋‹คํ•ด์ค€ JdbcTemplte ์„ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ์‚ฌ์‹ค์ƒ ์‹ค์ œ ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ๋กœ ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋ฉด ๊ทธ๋ ‡๊ฒŒ ์—„์ฒญ ํŽธ๋ฆฌํ•˜์ง€๋Š” ์•Š๋‹ค.

@Repository
class EmployeeRepository(private val jdbcTemplate: JdbcTemplate) {

    fun findEmployeesWithJoin(minSalary: Double, maxSalary: Double, departmentName: String, positionTitle: String): List<Employee> {
        val sql = """
            SELECT 
                e.*, 
                d.id as department_id, d.name as department_name, 
                p.id as position_id, p.title as position_title
            FROM employees e
            INNER JOIN departments d ON e.department_id = d.id
            INNER JOIN positions p ON e.position_id = p.id
            WHERE e.salary BETWEEN ? AND ?
              AND d.name = ?
              AND p.title = ?
            ORDER BY e.lastName, e.firstName
        """

        return jdbcTemplate.query(
            sql,
            arrayOf(minSalary, maxSalary, departmentName, positionTitle),
            EmployeeRowMapper()
        )
    }
}

class EmployeeRowMapper : RowMapper<Employee> {
    override fun mapRow(rs: ResultSet, rowNum: Int): Employee {
        return Employee(
            id = rs.getLong("id"),
            firstName = rs.getString("firstName"),
            lastName = rs.getString("lastName"),
            // ๊ธฐํƒ€ Employee ํ•„๋“œ
            department = Department(
                id = rs.getLong("department_id"),
                name = rs.getString("department_name")
                // ๊ธฐํƒ€ Department ํ•„๋“œ
            ),
            position = Position(
                id = rs.getLong("position_id"),
                title = rs.getString("position_title")
                // ๊ธฐํƒ€ Position ํ•„๋“œ
            )
            // ๊ธฐํƒ€ ํ•„๋“œ
        )
    }
}

 

๋ฌผ๋ก  ORM (ex JPA + QueryDsl ์กฐํ•ฉ)์œผ๋กœ ๋Œ€๋ถ€๋ถ„ ํ•ด๊ฒฐ๋˜์ง€๋งŒ, ํŠน์ • DB์— ์ข…์†๋œ ๊ณ ๊ธ‰ ์ฟผ๋ฆฌ๋‚˜ ํ†ต๊ณ„ ์ฟผ๋ฆฌ์˜ ๊ฒฝ์šฐ ๊ทธ๊ฒŒ ๋ถˆ๊ฐ€๋Šฅ ํ•œ ๊ฒฝ์šฐ๋„ ๋งŽ๋‹ค. ๊ทธ๋Ÿฐ ์ฟผ๋ฆฌ์—” JPA ๊ฐ€ ๋˜‘๋ฐ”๋กœ ์ƒ์„ฑํ•˜๋Š”์ง€ ๋ฏฟ๊ธฐ ํž˜๋“  ๊ฒƒ๋„ ์žˆ๊ณ 


์ด๋Ÿฐ ๋ฅ˜์˜ ๊ธฐ์ˆ ๋กœ ์—ฌ๋Ÿฌ๊ฐ€์ง€๋ฅผ ์ฐพ์•„๋ดค์—ˆ๋Š”๋ฐ, ์ „๋ถ€ ์žฅ๋‹จ์ ์ด ์žˆ์–ด ์• ๋งคํ–ˆ์—ˆ๋‹ค.

๋ฌผ๋ก  DB๋กœ ํ•ด๊ฒฐํ•˜์ง€์•Š๊ณ  ๊ฒ€์ƒ‰์ด๋ฉด Elasticsearch, ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ๋Š” Hadoop์— ๋ฐ€์–ด๋„ฃ๊ณ  Google BigQuery๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋Š”๋ฐ ๊ทธ๊ฑด ๊ธฐ์—…์ด ํฐ ๊ฒฝ์šฐ์—๋งŒ ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•์ด๋‹ˆ ์ž ๊น ๋ฐ€์–ด๋‘๊ณ , ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค. 

  • Spring JDBC Template (์œ„ ์˜ˆ์ œ์ฝ”๋“œ)
  • Jooq ( native sql์„ ๋งˆ์น˜ JPA์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ )
  • MyBatis ( ์„ ํ˜ธํ•˜์ง€๋Š” ์•Š์ง€๋งŒ Java8 ์ดํ•˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์œ ์ผํ•œ ์„ ํƒ์ง€ ๐Ÿฅฒ )
  • JPA + ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋งŒ EntityManager.createNativeQuery ์‚ฌ์šฉ

 

 

์ด๋Ÿฐ ๊ณ ๋ฏผ์„ ๋‚˜๋งŒ ํ•œ๊ฒŒ ์•„๋‹Œ์ง€ SpringBoot 3.0 ์— JDBC Client ๋ผ๋Š”๊ฒŒ ์ถ”๊ฐ€๋˜์—ˆ๋”๋ผ. ์ƒ๊ฐ๋ณด๋‹ค ์“ธ๋งŒํ•ด๋ณด์—ฌ์„œ ๋‚˜์ค‘์— ์จ๋ณด๊ณ  ๊ธ€๋กœ ํ›„๊ธฐ๋ฅผ ์จ๋ณผ ์˜ˆ์ •์ด๋‹ค.

@Repository
class EmployeeRepository(private val jdbcAggregateOperations: JdbcAggregateOperations) {

    fun findEmployeeById(employeeId: Long): Employee? {
        return jdbcAggregateOperations.findById(employeeId, Employee::class.java)
    }

    fun findAllEmployees(): List<Employee> {
        return jdbcAggregateOperations.findAll(Employee::class.java)
    }
}

 

 

์ฐธ๊ณ ๋กœ Jpa์—์„œ ์‚ฌ์šฉํ•˜๋˜ ๊ธฐ๋Šฅ๋“ค์ค‘, Spring Data์—๋งŒ ์˜์กดํ•˜๊ณ  ์žˆ๋Š” ๊ธฐ๋Šฅ๋“ค๋„ ๋งŽ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด Spring Data์˜ Criteria๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค

์ถœ์ฒ˜-&nbsp;www.youtube.com/watch?v=RKxQWQJftAg

/** Spring Data์˜ Criteria ํ™œ์šฉ */
@Repository
class EmployeeRepository(private val jdbcAggregateTemplate: JdbcAggregateTemplate) {

    fun findEmployeesWithJoin(minSalary: Double, maxSalary: Double, departmentName: String, positionTitle: String): List<Employee> {
        val query = Query.query(
            Criteria.where("e.salary").between(minSalary, maxSalary)
                .and("d.name").`is`(departmentName)
                .and("p.title").`is`(positionTitle)
        ).sort(Sort.by("e.lastName").and(Sort.by("e.firstName")))

        return jdbcAggregateTemplate.find(query, Employee::class.java, "employees e")
            .innerJoin("departments d", "e.department_id = d.id")
            .innerJoin("positions p", "e.position_id = p.id")
            .all()
    }
}

 

๊ถ๊ธˆํ•˜๋‹ค๋ฉด ์•„๋ž˜ ๋งํฌ๋ฅผ ํ•œ๋ฒˆ์ฏค ๊ผญ ๋ณด๋Š”๊ฑธ ์ถ”์ฒœํ•œ๋‹ค.

์„ค๋ช… - https://www.baeldung.com/spring-6-jdbcclient-api

์œ ํŠœ๋ธŒ - ํ•˜์ฐฎ์€์˜คํ›„๋‹˜ JdbcClient์™€ record๋ฅผ ๊ฒฐํ•ฉํ•˜๋ฉด ์‹œ๋„ˆ์ง€๊ฐ€ ์ปค์ง‘๋‹ˆ๋‹ค.

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ