Spring DB Part I
Spring DB Part I
์ํ๋์ ์คํ๋ง DB 1ํธ - ๋ฐ์ดํฐ ์ ๊ทผ ํต์ฌ ์๋ฆฌ ๊ฐ์๋ฅผ ์์ฝํ ๋ด์ฉ์ ๋๋ค.
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 ๋๋ผ์ด๋ฒ๊ฐ ๋จผ์ ์คํ๋ ๊ฒฝ์ฐ, ์ฒ๋ฆฌํ ์ ์๋ค๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ฒ ๋๊ณ , ๋ค์ ๋๋ผ์ด๋ฒ์๊ฒ ์์๊ฐ ์ ๋ฌ
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();
}
}
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;
}
}
๋ฑ๋ก
@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); // ์ปค๋ฅ์
์์ฑ ์๊ฐ ๋๊ธฐ
}
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)
: ํธ๋์ญ์ ์ ์ฑ๊ณต์ ์ผ๋ก ๋๋ด๋ฉด ๊ทธ ๊ฒฐ๊ณผ๊ฐ ํญ์ ๊ธฐ๋ก๋์ด์ผ ํ๋ค.์ค๊ฐ์ ์์คํ ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ก๊ทธ ๋ฑ์ ์ฌ์ฉํด์ ์ฑ๊ณตํ ํธ๋์ญ์ ๋ด์ฉ์ ๋ณต๊ตฌํด์ผ ํ๋ค.
ํธ๋์ญ์ ์ ์ฌ์ฉ ์์
๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ํ๋ ค๋ฉด 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;
๊ฐ์ ์ธ์ ์ ์ฌ์ฉํ๊ธฐ ์ํด ํธ๋์ญ์ ์ ์ฌ์ฉํ๋ ๋์ ๊ฐ์ ์ปค๋ฅ์ ์ ์ ์งํด์ผ ํ๋ค.
๊ฐ์ฅ ๋จ์ํ ๋ฐฉ๋ฒ์ ์ปค๋ฅ์ ์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ๋ ๋ฐฉ๋ฒ
๊ณผ๊ฑฐ ์๋ฒ์์์ ํธ๋์ ์ ์ ์ฉ์ ์๋น์ค ๊ณ์ธต์ด ๋งค์ฐ ์ง์ ๋ถํด์ง๊ณ ์๊ฐ๋ณด๋ค ๋งค์ฐ ๋ณต์กํ ์ฝ๋๋ฅผ ์๊ตฌ..
๊ธฐ์กด ํธ๋์ญ์ ์ ๋ฌธ์ ์
JDBC ๊ตฌํ ๊ธฐ์ ์ด ์๋น์ค ๊ณ์ธต์ ๋์๋๋ ๋ฌธ์
๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ JDBC ๊ตฌํ ๊ธฐ์ ์์ธ๊ฐ ์๋น์ค ๊ณ์ธต์ผ๋ก ์ ํ
try, catch, finally .. ์ ์ฌํ ์ฝ๋์ ๋ฐ๋ณต
Transaction Problem
Spring Transaction Manager
ํธ๋์ญ์ ์ถ์ํ
PlatformTransactionManager
interfaceJdbcTransactionManager
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 ์ํ
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) {..}
}
Transaction AOP
TransactionalProxy ๋์ ์ ํตํด ํธ๋์ญ์ ์ฒ๋ฆฌ ๊ฐ์ฒด์ ๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ ์๋น์ค ๊ฐ์ฒด๋ฅผ ๋ช ํํ๊ฒ ๋ถ๋ฆฌ
@Transactional
์ ํธ๋์ญ์ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๊ณณ์ ์ถ๊ฐํด์ฃผ๋ฉด, ์คํ๋ง์ ํธ๋์ญ์ AOP๊ฐ ํธ๋์ญ์ ์ด ์ ์ฉ๋ ํ๋ก์๋ฅผ ์์ฑํ๊ณ ์๋์ผ๋ก ํธ๋์ญ์ ์ฒ๋ฆฌTransactionalProxy
๋ฅผ ๋์ ํ๋ฉด@Transactional
์ด ๋ถ์ด ์๋ ๋ฉ์๋๋ ํด๋์ค์ Spring์ด ํด๋น ์๋น์ค ๋ก์ง์ ์์๋ฐ์์ ์๋์ผ๋ก ํธ๋์ญ์ ์ฝ๋๋ฅผ ์์ฑxxxService$$EnhancerBySpringCGLIB$$..
ํธ๋์ญ์
AOP ๋์ ํ๋ฆ

Transaction์ด ์ ์ฉ๋ ํด๋์ค/๋ฉ์๋ ํธ์ถ
Transaction์ด ์ ์ฉ๋
Spring AOP Proxy ํธ์ถ
Spring Container์ ๋ฑ๋ก๋
Transaction Manager
ํ๋ํธ๋์ญ์ ์์
. transactionManager.getTransaction()transactionManager๋ ๋ด๋ถ์์ DataSource๋ฅผ ์ฌ์ฉํด
์ปค๋ฅ์ ์์ฑ
์ปค๋ฅ์ ์
์๋ ์ปค๋ฐ ๋ชจ๋๋ก ๋ณ๊ฒฝ
ํด์ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ๋์ญ์ ์์. setAutoCommit(false)์ปค๋ฅ์ ์
TransactionSynchronizationManager
์ ๋ณด๊ดTransactionSynchronizationManager๋
ThreadLocal
์ ์ปค๋ฅ์ ์ ๋ณด๊ดThreadLocal: ๋ฉํฐ ์ฐ๋ ๋ ํ๊ฒฝ์์๋ ์์ ํ๊ฒ ์ปค๋ฅ์ ๋ณด๊ด
Spring AOP Proxy์์ ์ค์ ๋น์ฆ๋์ค ๋ก์ง์ ์คํํ๋ฉด์ ๋ฆฌํฌ์งํ ๋ฆฌ์
๋ฉ์๋๋ค์ ํธ์ถ
(์ปค๋ฅ์ ์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ ํ์๊ฐ ์์ด์ง)๋ฆฌํฌ์งํ ๋ฆฌ๋ DataSourceUtils.getConnection()์ ํตํด
TransactionSynchronizationManager
์ ๋ณด๊ด๋์ปค๋ฅ์ ์ ๊บผ๋ด์ ์ฌ์ฉ
๊ฐ์ ์ปค๋ฅ์ ์ ์ฌ์ฉํ๊ณ , ํธ๋์ญ์ ๋ ์ ์ง
ํ๋ํ ์ปค๋ฅ์ ์ ์ฌ์ฉํด์
SQL
์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฌ ๋ฐ ์คํ
๋น์ฆ๋์ค ๋ก์ง์ด ๋๋๊ณ
ํธ๋์ญ์ ์ ์ข ๋ฃ
๋ฅผ ์ํดTransactionSynchronizationManager
๋ฅผ ํตํ๋๊ธฐํ๋ ์ปค๋ฅ์ ์ ํ๋
ํ๋ํ ์ปค๋ฅ์ ์ ํตํด ์ปค๋ฐ/๋กค๋ฐฑ ํ ํธ๋์ญ์ ์ข ๋ฃ
์ ์ฒด
๋ฆฌ์์ค
(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());
}
}
Java Excaption

Object
: ๋ชจ๋ ๊ฐ์ฒด์ ์ต์์ ๋ถ๋ชจThrowable
: ์ต์์ ์์ธ์์ ์์ธ๋ฅผ ์ก์ผ๋ฉด ๊ทธ ํ์ ์์ธ(Error..)๊น์ง ํจ๊ป ์ก์ผ๋ฏ๋ก, Throwable ์์ธ๋ ์ก์ง ๋ง๊ณ , Exception๋ถํฐ ์ก์.
Error
: ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ณต๊ตฌ ๋ถ๊ฐ๋ฅํ ์์คํ ์์ธ (๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ์ด๋ ์ฌ๊ฐํ ์์คํ ์ค๋ฅ)unchecked exception
Exception
: ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง์์ ์ฌ์ฉํ ์ ์๋ ์ค์ง์ ์ธ ์ต์์ ์์ธException๊ณผ ๊ทธ ํ์ ์์ธ๋ ๋ชจ๋ ์ปดํ์ผ๋ฌ๊ฐ ์ฒดํฌํ๋ checked exception
์ปดํ์ผ๋ฌ๊ฐ ์ฒดํฌํด ์ฃผ๊ธฐ ๋๋ฌธ์ ์ก๊ฑฐ๋ ๋์ง๊ฑฐ๋ ํ๋๋ฅผ ํ์๋ก ์ ํ
๋จ, RuntimeException์ ์์ธ
RuntimeException
: ์ปดํ์ผ๋ฌ๊ฐ ์ฒดํฌํ์ง ์๋ unchecked exceptionRuntimeException๊ณผ ๊ทธ ์์ ์์ธ๋ ๋ชจ๋ unchecked exception
์์ธ์ ๊ธฐ๋ณธ ๊ท์น
์์ธ๋ ์ก์์ ์ฒ๋ฆฌํ๊ฑฐ๋ ๋์ ธ์ผ ํจ.
์์ธ๋ฅผ ์ก๊ฑฐ๋ ๋์ง ๋ ์ง์ ํ ์์ธ๋ฟ๋ง ์๋๋ผ ์์ ์์ธ๋ค๋ ํจ๊ป ์ฒ๋ฆฌ
.
์์ธ ์ก๊ธฐ
try-catch
Repository ์์ธ ๋ฐ์ -> Service๋ก ์์ธ throws -> Service์์ ์์ธ ์ฒ๋ฆฌ -> ์ดํ ์ ์ ํ๋ฆ์ผ๋ก ๋์
์์ธ ๋์ง๊ธฐ
throws Exception
Repository ์์ธ ๋ฐ์ -> Service๋ก ์์ธ throws -> Controller๋ก ์์ธ throws
์์ธ๋ฅผ ์ฒ๋ฆฌํ์ง ๋ชปํ๊ณ ๊ณ์ ๋์ง๋ฉด main() ์ฐ๋ ๋์ ๊ฒฝ์ฐ ์์ธ ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ฉด์ ์์คํ ์ด ์ข ๋ฃ๋๊ณ , ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ WAS๊ฐ ํด๋น ์์ธ๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ๋๋ฐ, ์ฃผ๋ก ์ฌ์ฉ์์๊ฒ ์ง์ ํ ์ค๋ฅ ํ์ด์ง๋ฅผ ์ ๋ฌ
Checked Exception
์ปดํ์ผ๋ฌ๊ฐ ์์ธ๋ฅผ ์ฒดํฌํด์ฃผ๋ฉด, ์ก์์ ์ฒ๋ฆฌํ๊ฑฐ๋, ๋ฐ์ผ๋ก ๋์ง๋๋ก ์ ์ธ
์์ธ๋ฅผ ์ก์์ ์ฒ๋ฆฌํ ์ ์์ ๊ฒฝ์ฐ์๋ ์์ธ๋ฅผ throws๋ก ๋์ ธ์ค์ผ ํจ.
์ฅ์ : ์ค์๋ก ์์ธ๋ฅผ ๋๋ฝํ์ง ์๋๋ก ์ปดํ์ผ๋ฌ๋ฅผ ํตํด ๋ฌธ์ ๋ฅผ ์ก์์ฃผ๋ ์์ ์ฅ์น
๋จ์ : ๋ชจ๋ ์ฒดํฌ ์์ธ๋ฅผ ๋ฐ๋์ ์ก๊ฑฐ๋ ๋์ง๋๋ก ์ฒ๋ฆฌํด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์
ํฌ๊ฒ ์ ๊ฒฝ์ฐ๊ณ ์ถ์ง ์์ ์์ธ๊น์ง ๋ชจ๋ ์ฑ๊ฒจ์ผ ํ๊ณ , ์์กด๊ด๊ณ์ ๋ฐ๋ฅธ ๋จ์ ๋ ์กด์ฌ
ํ์ฉ
\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 ์ ์ธ ์๋ต ๊ฐ๋ฅ
๋จ์ : ์ปดํ์ผ๋ฌ๊ฐ ์์ธ ๋๋ฝ์ ์ก์์ฃผ์ง ์์ผ๋ฏ๋ก, ์ค์๋ก ์์ธ๋ฅผ ๋๋ฝํ ์ ์์
ํ์ฉ
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
์๋น์ค ๊ณ์ธต์ ๊ฐ๊ธ์ ํน์ ๊ตฌํ ๊ธฐ์ ์ ์์กดํ์ง ์๊ณ , ์์ํ๊ฒ ์ ์งํ๋ ๊ฒ์ด ์ข๋ค.
์์ธ์ ๋ํ ์์กด(์์ธ ๋์)์ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฐํ์ ์์ธ์ ์ธํฐํ์ด์ค๋ฅผ ์ ์ฉํด ๋ณด์.
์ ๊ทผ ์์ธ ์์ฑ
Service Layer์์ ํน์ ๊ธฐ์ ์ ์์กด์ ์ธ ์์ธ(ex. SQLException)๋ฅผ ์ก์์ ์ฒ๋ฆฌํ๊ณ ์ถ์ ๊ฒฝ์ฐ, RuntimeException ์์ธ๋ฅผ ์์๋ฐ์ ์ปค์คํ ์์ธ๋ฅผ Repository Layer์์ ๋ณํํด์ ์ฒ๋ฆฌํ ์ ์์
๋จ, SQL ErrorCode๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง๋ค ๋ค๋ฅด๋ฏ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ข ์์
์คํ๋ง์ ์์ธ ์ถ์ํ
์คํ๋ง์ ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ ๋ํ ์ผ๊ด๋ ์์ธ ์ถ์ํ๋ฅผ ์ ๊ณต

์คํ๋ง์ด ์ ๊ณตํ๋ ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ ๋ชจ๋ ์์ธ๋ ๋ฐํ์ ์์ธ
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>
ํ ํ๋ฆฟ ์ฝ๋ฐฑ ํจํด
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