ํ์คํ ์น๐ ๊ฐ๋ฐ์ ์ง๋ง์ ๐ง๐ฝโ๐ป
โ ์ธ๊ณต์ง๋ฅ ๊ด์ฌ ๐ค
Categories
-
โฃ
โถ COMPUTER_SCIENCE
๐: 7 -
โฃ
โถ WEB
๐: 3 -
โฃ
โถ ETC
๐: 3-
โ
โฃ
ETCS
๐: 10 -
โ
โฃ
SUBBRAIN ๊ฐ๋ฐ๊ธฐ
๐: 5 -
โ
โ
YOS ๊ฐ๋ฐ๊ธฐ
๐: 1
-
โ
โฃ
-
โ
โถ AI
๐: 9-
โฃ
AITOOLS
๐: 3 -
โฃ
CV
๐: 2 -
โฃ
DEEP_LEARNING
๐: 1 -
โฃ
DATA_VIS
๐: 2 -
โฃ
GRAPH
๐: 1 -
โฃ
LIGHTWEIGHT
๐: 1 -
โฃ
MATH
๐: 1 -
โฃ
NLP
๐: 3 -
โ
STRUCTURED_DATA
๐: 2
-
โฃ
Spring5 ์ ๋ฌธ-DB ์ฐ๋
- ์์กด ๋ชจ๋ ์ถ๊ฐ ๋ฐ DB ์ค์
- DataSource ์ค์
- JdbcTemplate
- ์คํ๋ง ์ต์
์
๋ณํ ์ฒ๋ฆฌ
- ํธ๋์ญ์ ์ฒ๋ฆฌ : @Transactional
- ๋ก๊น
์ฒ๋ฆฌ
DB ์ฐ๋
_ ์ด๋ณด ์น ๊ฐ๋ฐ์๋ฅผ ์ํ ์คํ๋ง 5 ํ๋ก๊ทธ๋๋ฐ ์ ๋ฌธ _์ ์คํ๋ง ์ธ ์ก์ ์ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ ๋ฆฌํ ๋ด์ฉ์ ๋๋ค.
์์กด ๋ชจ๋ ์ถ๊ฐ ๋ฐ DB ์ค์
- Mysql๋ฅผ ๊ธฐ์ค์ผ๋ก ์งํ๋๋ฉฐ Maven์ด๋ Gradle์ ์ด์ฉํด ๋ค์๊ณผ ๊ฐ์ ์์กด ๋ชจ๋์ ์ถ๊ฐํ์.
- spring-jdbc : JdbcTemplate, ์คํ๋ง ํธ๋์ญ์
๊ธฐ๋ฅ ๋ฑ์ ๊ธฐ๋ฅ์ ์ ๊ณต
spring boot
์ ๊ฒฝ์ฐ: spring-boot-starter-jdbc
- tomcat-jdbc : DB ์ปค๋ฅ์ ํ ๊ธฐ๋ฅ์ ์ ๊ณต
- mysql-connector-java : MySQL ์ฐ๊ฒฐ์ ํ์ํ JDBC ๋๋ผ์ด๋ฒ๋ฅผ ์ ๊ณต
- spring-jdbc : JdbcTemplate, ์คํ๋ง ํธ๋์ญ์
๊ธฐ๋ฅ ๋ฑ์ ๊ธฐ๋ฅ์ ์ ๊ณต
- Mysql์ ๊ณต์ ๋ ํผ๋ฐ์ค๋ฅผ ์ด์ฉํด ์ค์นํ๊ฑฐ๋ Docker๋ฅผ ์ด์ฉํด ์์ฑํ์.
- ์ดํ DB ์๋ฒ์ ์ ์ํด ์ง์ SQL ๊ตฌ๋ฌธ์ ์ด์ฉํด DB, ์ฌ์ฉ์, ํ ์ด๋ธ ์์ฑ ๋ฐ ๊ถํ ์ค์ ์ ํ๋ค.
๋ง์ฝ ์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, src/main/resources/schema.sql, data.sql
ํ์ผ์ SQL ๊ตฌ๋ฌธ์ ์ ํ๋ฆฌ์ผ์ด์
์คํ์ ์๋์ผ๋ก ์คํํ๋ค.
create user 'spring5'@'localhost' identified by 'spring5';
create database spring5fs character set=utf8;
grant all privileges on spring5fs.* to 'spring5'@'localhost';
create table spring5fs.MEMBER (
ID int auto_increment primary key,
EMAIL varchar(255),
PASSWORD varchar(100),
NAME varchar(100),
REGDATE datetime,
unique key (EMAIL)
) engine=InnoDB character set = utf8;
- DB์ ํต์ ํ ๋ ์ฌ์ฉํ๋ ๋ฉ์๋๋ฅผ ๋ชจ์๋์ ๊ฐ์ฒด์ธ DAO(Data Access Object) ๋ํ ์ ์ํด์ค์ผ ํ๋ค.
์ถ๊ฐ๋ก ๊ตฌ์ฑ ํด๋์ค๋ก ํ๋จ์ memberDao
๋ฅผ ๋น์ผ๋ก ์ถ๊ฐํ์.
package spring;
import java.util.Collection;
public class MemberDao {
public Member selectByEmail(String email) {
return null;
}
public void insert(Member member) {}
public void update(Member member) {}
public Collection<Member> selectAll() {
return null;
}
}
DataSource ์ค์
JDBC API๋ DriverManager
๊ฐ์ฒด ํน์ DataSource
๊ฐ์ฒด๋ฅผ ํตํด ์ปค๋ฅ์
ํ์์ ์ฐ๊ฒฐ์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
DBMS ์ฐ๊ฒฐ์ ์์ฑํ๋ ์๊ฐ์ ์ค์ด๊ณ , ๋ฐ์ํ๋ ๋ถํ๋ฅผ ์ ํํ๊ธฐ ์ํด ์ผ์ ๊ฐ์์ DB ์ปค๋ฅ์ ์ ๋ฏธ๋ฆฌ ์์ฑํด๋๊ณ ์ด๋ฅผ ์ฌ์ฉ์ ๋ง๋ค ๋์ฌ, ๋ฐ๋ฉํ๋ ๋ฐฉ์
๊ฐ ์ปค๋ฅ์ ์ ์ฌ์ฉ ์ค์ธ ํ์ฑ ์ํ, ๋๊ธฐ ์ค์ธ ์ ํด ์ํ๊ฐ ์กด์ฌํ๋ค.
Tomcat JDBC Datasource ํด๋์ค
๋ชจ๋ Datasource ํด๋์ค๋ javax.sql.Datasource
๋ฅผ ๊ตฌํํด์ผ ํ๋ฉฐ, ์ฐ๋ฆฌ๋ Tomcat JDBC ๋ชจ๋์ Datasource ํด๋์ค ์ด์ฉํ ๊ฒ์ด๋ค.
/src/main/java/config/AppCtx.java
๋จผ์ DataSource
๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ๊ณ ์ฃผ์
์ ๋ฐ๋๋ก ํ๋ค.
์ด์ธ์ DB ์ค์ ์ ๋ค์์ด ์๋ค.
- ์ปค๋ฅ์
ํ ๋น ์ ๊ฒ์ฌ(
setTestOnBorrow
) - ์ปค๋ฅ์
์ ํจ ์ฟผ๋ฆฌ ์ง์ (
setValidationQuery("select 1")
) - ์ต์ ์ปค๋ฅ์
๊ฐฏ์(
setMinIdle
) - ์ต๋ ์ฐ๊ฒฐ ํ ๋น ๋๊ธฐ ์๊ฐ(
setMaxWait(default=30์ด)
) - ์ ํด ์ฐ๊ฒฐ ์ ๊ฑฐ ๋๊ธฐ ์๊ฐ(
setMinEvictableIdleTimeMillis(default=60์ด)
)
package config;
import org.apache.tomcat.jdbc.pool.DataSource;
//...
@Configuration
public class DbConfig {
@Bean(destroyMethod="close") // DataSource ๊ฐ์ฒด ์๋ฉธ ์ close ๋ฉ์๋(์ปค๋ฅ์
ํ ๋น์ฐ๊ธฐ)๋ฅผ ์คํ
public DataSource dataSource() {
DataSource ds = new DataSource(); // ๊ฐ์ฒด ์์ฑ
ds.setDriverClassName("com.mysql.jdbc.Driver"); // JDBC Mysql ๋๋ผ์ด๋ฒ ์ฌ์ฉ
ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8"); // URL ์ง์
ds.setUsername("spring5"); // ์ ์ ๋ช
์ค์
ds.setPassword("spring5"); // ํจ์ค์๋ ์ค์
ds.setInitialSize(2); // ์ด๊ธฐ ์ปค๋ฅ์
๊ฐ์ ์ง์
ds.setMaxActive(10); // ์ต๋ ์ปค๋ฅ์
๊ฐ์ ์ง์
ds.setTestWhileIdle(true); // ์ ํด ์ํ ์ฐ๊ฒฐ ์ฃผ๊ธฐ์ ์ผ๋ก ๊ฒ์ฌ
ds.setMinEvictableIdleTimeMillis(1000*60*3); // ์ต์ ์ ํด ์๊ฐ 3๋ถ
ds.setTimeBetweenEvictionRunsMillis(10*1000); // 10์ด ์ฃผ๊ธฐ ๊ฒ์ฌ
return ds;
}
}
์์ ๊ฐ์ด ์ค์ ํ๋ฉด ์๋์ ๊ฐ์ด ์ฐ๊ฒฐ์ ๊ฐ์ ธ์ ์ฌ์ฉํ ์ ์๋ค.
/src/main/java/dbquery/DbQuery.java
package dbquery;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
public class DbQuery {
private DataSource dataSource;
public DbQuery(DataSource dataSource) {
this.dataSource = dataSource;
}
public int count() {
Connection conn = null;
try {
conn = dataSource.getConnection(); // ์ฐ๊ฒฐ ๊ฐ์ ธ์ค๊ธฐ
try (Statement stmt = conn.createStatement(); // sql๋ฌธ ์์ฑ ์ค๋น๋ฅผ ์ํ Statement
ResultSet rs = stmt.executeQuery("select count(*) from MEMBER")) {
rs.next();
return rs.getInt(1); // ๊ฒฐ๊ณผ ๊ฐ์ธ ResultSEt
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
}
}
}
}
JdbcTemplate
Spring์ JDBC, JPA, MyBatis ๋ฑ ์ฌ๋ฌ ๊ธฐ์ ์ ์ฌ์ฉํ ์ ์์ง๋ง ์ด๋ฒ์๋ JdbcTemplate๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์.
JdbcTemplate ํด๋์ค๋ฅผ ์ด์ฉํ๋ฉด ์ผํ๋ฆฟ ๋ฉ์๋ ํจํด๊ณผ ์ ๋ต ํจํด์ ์ด์ฉํ์ฌ ๊ธฐ์กด JDB API์ ๊ตฌ์กฐ์ ๋ฐ๋ณต์ ์ค์ด๊ณ ์ฌ์ฌ์ฉ์ฑ์ ๋๋ฆด ์ ์๋ค.
์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
Member member;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost/spring5fs", "spring5", "spring5");
// ------โโ-----๋ฐ๋ณต๋๋ ์ฝ๋------โโ-----
pstmt = conn.prepareStatement("select * from MEMBER where EMAIL = ?");
pstmt.setString(1, email);
rs = pstmt.executeQuery();
if (rs.next()) {
member = new Member(rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE"));
member.setId(rs.getLong("ID"));
return member;
} else {
return null;
}
// ------โโ-----๋ฐ๋ณต๋๋ ์ฝ๋------โโ-----
} catch (SQLException e) {
e.printStackTrace();
throw e;
} finally {
if (rs != null)
try { rs.close(); } catch (SQLException e2) {}
if (pstmt != null)
try { pstmt.close(); } catch (SQLException e1) {}
if (conn != null)
try { conn.close(); } catch (SQLException e) {}
}
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where EMAIL = ?",
new RowMapper<Member>(){
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member(rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE"));
member.setId(rs.getLong("ID"));
return member;
}
},
email);
return results.isEmpty() ? null : results.get(0);
๊ธฐ์กด์ JDBC ์ฝ๋์ ๋นํด ํจ์ฌ ์ค์ด๋ ๋ค๋ ์ ์ ์ ์ ์๋ค.
JdbcTemplate
๋ฅผ ์ฌ์ฉํ๋ฉด ์งง์ ์ฝ๋๋ก ์์ฝ๊ฒ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ์ ์๋ค.
JdbcTemplate ์์ฑ
jdbc.core.JdbcTemplate
๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ dataSource
๊ฐ์ฒด๋ฅผ ํ ๋นํด ์ค ๋ค, ํด๋น DAO๋ฅผ ๋น ๊ฐ์ฒด๋ก ๋ฑ๋กํด์ฃผ์.
MemberDao
์ ๋ฌธ
package spring;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.sql.Timestamp;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate; // JdbcTemplate ์ํฌํธ
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
public class MemberDao {
private JdbcTemplate jdbcTemplate;
public MemberDao(DataSource dataSource) { // ์์ฑ์๋ก JdbcTemplate์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์
this.jdbcTemplate = new JdbcTemplate(dataSource); // ๊ตฌ์ฑ ํด๋์ค ์ถ๊ฐ ์ datasource๋ฅผ ๊ฑด๋ค์ค์ผ ํจ
}
public Member selectByEmail(String email) {
List<Member> results = jdbcTemplate.query( // query ๋ฉ์๋๋ฅผ ํตํ ์กฐํ
"select * from MEMBER where EMAIL = ?",
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
}, email);
return results.isEmpty() ? null : results.get(0);
}
public void insert(Member member) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() { // update ๋ฉ์๋๋ก insert๋ ์งํ
@Override
public PreparedStatement createPreparedStatement(Connection con)
throws SQLException {
// ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋ฐ์ Connection์ ์ด์ฉํด์ PreparedStatement ์์ฑ
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE) " +
"values (?, ?, ?, ?)",
new String[] { "ID" });
// ์ธ๋ฑ์ค ํ๋ผ๋ฏธํฐ ๊ฐ ์ค์
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4,
Timestamp.valueOf(member.getRegisterDateTime()));
// ์์ฑํ PreparedStatement ๊ฐ์ฒด ๋ฆฌํด
return pstmt;
}
}, keyHolder);
Number keyValue = keyHolder.getKey();
member.setId(keyValue.longValue());
}
public void update(Member member) {
jdbcTemplate.update(
"update MEMBER set NAME = ?, PASSWORD = ? where EMAIL = ?",
member.getName(), member.getPassword(), member.getEmail());
}
public List<Member> selectAll() {
List<Member> results = jdbcTemplate.query("select * from MEMBER",
(ResultSet rs, int rowNum) -> { // ๋๋ค์์ ์ด์ฉํ ๋์ฑ ์งง์ ๊ตฌํ, ์์ ์กฐํ ์ฟผ๋ฆฌ์ ์ฌ์ฉํ RowMapper์ ๊ฐ์ ๋ด์ฉ์ด๋ฏ๋ก ์ฌ์ฌ์ฉ์ผ๋ก ์๋ต๋ ๊ฐ๋ฅํ๋ค.
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
});
return results;
}
public int count() {
Integer count = jdbcTemplate.queryForObject( // queryforObject๋ฅผ ์ด์ฉํ ์กฐํ, ์ฃผ๋ก ๊ฒฐ๊ณผ ๊ฐ์ด ํ๋์ผ ๊ฒฝ์ฐ์ ์ฌ์ฉ
"select count(*) from MEMBER", Integer.class);
return count;
}
}
DAO๋ฅผ ๋น ๊ฐ์ฒด๋ก ๋ฑ๋กํ๊ธฐ
@Configuration
@EnableTransactionManagement
public class AppCtx {
@Bean
public MemberDao memberDao() {
return new MemberDao(dataSource());
}
}
DAO
vs@Repository
๋ง์ฝ, ์คํ๋ง MVC๋ฅผ ์ด์ฉํ๋ค๋ฉด, DAO
๋์ @Repository
์ด๋
ธํ
์ด์
์ ์ด์ฉํ ์ ์๊ฑฐ๋ ์์ด์ ์ฌ์ฉํ ์ ์๋ค.
DAO(Data Access Object)
- ๋น์ฆ๋์ค ๋ก์ง(service?)๊ณผ ํผ์์คํด์ค ๋ก์ง(DB ์ ๊ทผ ๋ก์ง)์ ๋ช ํํ ๋ถ๋ฆฌํ๊ธฐ ์ํ ๊ฐ์ฒด
- ์ธํฐํ์ด์ค๋ก ๋จผ์ ์ถ์ํํ๊ณ ,
DAOImpl
๊ฐ์ฒด๋ก ์ด๋ฅผ ๊ตฌํํ๋ ๋ฐฉ์์ด ์์ง๋ง ์ ์์์ฒ๋ผ ๊ฐ๋จํ๋ค๋ฉด ๊ณง๋ฐ๋ก ๊ตฌํํด๋ ๋๋ค. - DB ํ ์ด๋ธ, ์ฟผ๋ฆฌ์ ๋ฐ์ ํ ์ฐ๊ด ๋จ
Repository ํจํด
- ๋ง์ฝ, ๋๋ฉ์ธ์ด ๋ณต์กํด์ง๋ค๋ฉด, ์ฌ๋ฌ
DAO
๋ ๋ค๋ฅธ API ๋ฑ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํด์ผ ํ๋ค. - ์ด๋
Repository
๊ฐ์ฒด๋ฅผ ์์๋ก ์ถ๊ฐํด ์๋ก ๋ค๋ฅธ ์ฌ๋ฌDAO
๋ ๋ค๋ฅธ API๋ก DB์ ๊ฐ์ ํต์ ํ๋ค. - ์ฆ
DAO
๋ฅผ ์ฌ์ฉํ๋ ์์ ๊ณ์ธต ๊ฐ์ฒด์ด๋ค. - DB๋ณด๋ค๋ ๋น์ฆ๋์ค ๋ก์ง์ ๋์ฑ ๊ฐ๊น์ด ์ผ์ ํ๋ค.
- ๋ง์ฐฌ๊ฐ์ง๋ก Repository ์ธํฐํ์ด์ค๋ก ๋จผ์ ์ถ์ํํ๊ณ
RepositoryImpl
๋ก ๊ตฌํํด๋ ๋๋ค.
์ฆ, ๋ณต์กํ ๋๋ฉ์ธ ๊ตฌ์กฐ๋ผ๋ฉด Repository
ํจํด์ ์ฐ๋ฉด ์ข๋ค.
@Repository
๋ฅผ ์ด์ฉํ๋ฉด @Autowwired
๊ฐ์ ์ด๋
ธํ
์ด์
์ ์ด์ฉํด ์๋์ผ๋ก jdbc
๋ datasource
๋น ๊ฐ์ฒด๋ฅผ ํ ๋น๋ฐ์ ์ ์๋ค.
JdbcTemplate ์กฐํ ์ฟผ๋ฆฌ ์์ฑ ์คํ
RowMapper ์ธํฐํ์ด์ค
์ฟผ๋ฆฌ ์คํ ๊ฒฐ๊ณผ๋ฅผ ์๋ฐ ๊ฐ์ฒด๋ก ๋ณํํ ๋ ์ฌ์ฉํ๋ ์ธํฐํ์ด์ค๋ก, ์ด์ฉ ์ mapRow()
๋ฉ์๋๋ฅผ ์ง์ ๊ตฌํํด์ผ ํ๋ค.
RowMapper
์ธํฐํ์ด์ค
package org.springframework.jdbc.core.RowMapper;
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
SQL ์คํ ๊ฒฐ๊ณผ ResultSet
์์ ํ ํ ์ฉ, ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ ์๋ฐ ๊ฐ์ฒด๋ก ๋ณํํ๋ค. (์ฆ, ๋์ค๋ ๊ฒฐ๊ณผ๊ฐ ๋ฐฐ์ด ํํ์ฌ์ผ ํ๋ค.)
select
sql์ ์ํ RowMapper ๊ตฌํ ์์
List<Member> results = jdbcTemplate.query( // query ๋ฉ์๋๋ฅผ ํตํ ์กฐํ
"select * from MEMBER where EMAIL = ?",
new RowMapper<Member>() { //์์ ํด๋์ค๋ฅผ ์ด์ฉํ ์ฆ์ ์ค๋ฒ๋ผ์ด๋ฉ
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
}, email);
๊ตณ์ด RowMapper
์ธํฐํ์ด์ค์ mapRow
๋ฉ์๋๋ฅผ ์ง์ ๊ตฌํํ์ง ์๊ณ ํจ์ ์๊ทธ๋์ฒ๊ฐ ์ผ์นํ๊ฒ ๋๋ค์์ผ๋ก ๋์ฑ ์งง๊ฒ ๊ตฌํํ ์๋ ์๋ค.
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where EMAIL = ?",
(ResultSet rs, int rowNum) -> {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}, email);
RowMapper
ํจ์
๋ง์ฝ ๊ฐ์ mapRow
๋ก์ง์ ๊ฐ์ง๋ค๋ฉด, ๊ตฌํํ RowMapper
๋ฅผ ์๋ ๊ฐ์ด ํจ์ํํ ๋ค ์ฌํ์ฉํ ์๋ ์๋ค.
private Member mapRowToMember(ResultSet rs, int rowNum) {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
query() ๋ฉ์๋
query()
๋ฉ์๋๋ sql ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋ฐ์ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ณ ์์ ๋ฐฐ์ด RowMapper
๋ฅผ ์ด์ฉํด ResultSet
์ ๊ฒฐ๊ณผ๋ฅผ ์๋ฐ ๊ฐ์ฒด๋ก ๋ณํํ๋ค.
query
๋ฉ์๋์ ์๊ทธ๋์ฒ
List<T> query(String sql, RowMapper<T> rowMapper)
List<T> query(String sql, Object[] args, RowMapper<T> rowMapper)
List<T> query(String sql, RowMapper<T> rowMapper, Object... args)
List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper)
:PreparedStatementCreator
๋ ์๋ ์ฐธ์กฐ
๋ค์ ์ธ์ args
์ ๊ฐฏ์๋ String sql
์ ์ธ๋ฑ์ค ํ๋ผ๋ฏธํฐ(?
)์ ๊ฐฏ์์ ๋ฌ๋ ค์์ผ๋ฉฐ, ์์๋๋ก args
์ ๊ฐ๊ณผ sql
๋ด๋ถ์ ?
์์น์ ํฌ๋งทํ
๋๋ค.
query
๋ฉ์๋์ ์ธ๋ฑ์ค ํ๋ผ๋ฏธํฐ์ ์ด์ฉ ์์
String email = "asdf@asdf.com";
String name = "asdf";
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where EMAIL = ? and NAME = ?",
new MemberRowMapper(), // RowMapper ์ฌํ์ฉ
email, name);
// s0ql ๊ฒฐ๊ณผ๋ select * from MEMBER where EMAIL = "asdf@asdf.com" and NAME = "asdf"
๋ง์ฝ ๊ฒฐ๊ณผ๊ฐ ์๋ค๋ฉด ๊ธธ์ด๊ฐ 0์ธ List๋ฅผ ๋ฆฌํดํ๋ฉฐ, ๊ฒฐ๊ณผ๊ฐ ์ค์ง ํ๋๋ผ๋ฉด ๊ธธ์ด๊ฐ 1์ธ List๋ฅผ ๋ฆฌํดํ๋ค.
๋ฐ๋ผ์ ๋ง์ฝ, ๋จ ํ๋์ ๊ฒฐ๊ณผ๋ง ๊ธฐ๋ํ๋ ์ฟผ๋ฆฌ์ ๊ฒฝ์ฐ,
return results.isEmpty() ? null : results.get(0);
์ฒ๋ผ ๋ฆฌ์คํธ ๊ธธ์ด๊ฐ 0์ด๋ฉด ๊ฒฐ๊ณผ ์์, ๋๋ ์ฒซ๋ฒ์งธ ํญ๋ชฉ์ ๊ฐ์ ธ์์ผ ํ๊ฑฐ๋, ๋ค์์ ์๊ฐ๋ queryForObject()
๋ฉ์๋๋ฅผ ์ด์ฉํ๋ฉด ๋๋ค.
queryForObject() ๋ฉ์๋
queryForObject()
๋ฉ์๋ ์์
query()
๋ฉ์๋์ ๊ฐ์ด ์ธ๋ฑ์ค ํ๋ผ๋ฏธํฐ๋ฅผ ํ์ฉํ ์ ์๋ค.
double avg = queryForObject(
"select avg(height) from FURNITURE where TYPE=? and STATUS=?",
Double.class, // Row mapper ๋์ ๊ฒฐ๊ณผ๊ฐ์ ํ์
๋ง ์ง์ ํด์ฃผ๋ฉด OK
100, "S");
๋ง์ฝ ์ํ๋ ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๊ฐ ํ๋๋ฟ์ผ ๊ฒฝ์ฐ -> ๊ฒฐ๊ณผ ๊ฐ์ ํ์
์ ์ํ๋ ๋ฐฉ์์ผ๋ก ๋ฐ์ ์ ์๊ณ , ๊ฐ๋จํ๊ฒ ๊ตฌํ ๊ฐ๋ฅํ queryForObject()
๋ฉ์๋๋ฅผ ์ถ์ฒ
๋ง์ฝ, ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ 2๊ฐ ์ด์์ด๊ฑฐ๋ 0๊ฐ์ด๋ฉด ์ค๋ฅ๋ฅผ ์ผ์ผํจ๋ค.
queroyForObject()
์ ๋ฉ์๋
T queryForObject(String sql, Class<T> requiredType)
T queryForObject(String sql, Class<T> requiredType, Object... args)
T queryForObject(String sql, RowMapper<T> rowMapper)
T queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
query()
๋ฉ์๋์ฒ๋ผ rowMapper
๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์ด๋๋ ๋ฆฌ์คํธ๊ฐ ์๋ rowMapper์ ๋ฆฌํด ํ์
์ผ๋ก ๋๋๋ ค ์ค๋ค.
JdbcTemplate ๋ณ๊ฒฝ ์ฟผ๋ฆฌ ์์ฑ ์คํ
SQL์ INSERT, UPDATE, DELETE
์ฟผ๋ฆฌ ๋ฑ์ update()
๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌ ๊ตฌํํ๋ค.
update()
๋ฉ์๋ ์ฌ์ฉ
int update(String sql)
int update(String sql, Object... args)
int update(PreparedStatementCreator psc)
์ญ์๋ ์ธ๋ฑ์ค ํ๋ผ๋ฏธํฐ(?
)๋ฅผ ์ง์ํ๋ค.
int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)
์ถ๊ฐ๋ก ์๋์ ์๊ฐ๋ PreparedStatement
์ KeyHolder
๋ฅผ ์ด์ฉํ ์ ์๋ค.
update()
๋ฉ์๋ ์์
jdbcTemplate.update(
"udpate MEMBER set NAME = ?, PASSWORD = ? where EMAIL = ?",
member.getName(), member.getPassword(), member.getEmail());
PreparedStatement ์ธํฐํ์ด์ค ์ด์ฉ
์ ๋งค๋ชจํธํ ?
์ธ๋ฑ์ค ํ๋ผ๋ฏธํฐ ๋์ , PreparedStatement
๋ฅผ ์ด์ฉํ ์๋ ์๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก PrepareStatementCreator
์ธํฐํ์ด์ค์ createPreparedSatement
๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ์ฌ ๊ตฌํํ๋ฉฐ, ๋ค์์ด ์์์ด๋ค.
PreparedStatement
์ด์ฉ ์์
import java.sql.PreparedStatement;
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
// ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋ฐ์ Connection์ ์ด์ฉํด์ PreparedStatement ์์ฑ
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE) values (?, ?, ?, ?)");
// ์ธ๋ฑ์ค ํ๋ผ๋ฏธํฐ์ ๊ฐ ์ค์
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4, Timestamp.valueOf(member.getRegisterDateTime()));
// ์์ฑํ PreparedStatement ๊ฐ์ฒด ๋ฆฌํด
return pstmt;
}
});
์ด ๋ฐฉ์์ ์์ ๋ฐฐ์ ๋ query()
๋ฉ์๋์์๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
KeyHolder๋ฅผ ์ด์ฉํ ์๋ ์์ฑ ํค๊ฐ ๊ตฌํ๊ธฐ
๋ง์ ํ ์ด๋ธ์์ ํ ์ถ๊ฐ ์, ์ฃผ์ ํค์ธ ID๋ฅผ ์๋์ผ๋ก ์ฆ๊ฐ์ํค๋ ๋ฐฉ์์ผ๋ก ์ด์ฉํ๋ฏ๋ก, ์ฝ๋ ์์์ ์๋ก ์์ฑ๋ ํ์ ๋ํ ID๋ฅผ ์ ์ ์๋ค.
update()
๋ฉ์๋์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ณ๊ฒฝ๋ ํ์ ๊ฐฏ์๋ง ๋ฆฌํดํ๋ค.
๋ฐ๋ผ์ ์ฟผ๋ฆฌ ํ ์์ฑ๋ ๊ฐ์ ๋ํ ํค ๊ฐ์ ๊ตฌํ๋ ๋ฐฉ๋ฒ์ผ๋ก keyHolder
๋ฅผ ์ด์ฉํ ์ ์๋ค.
Keyholder
๊ตฌํ ์์
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
public void insert(Member member) {
KeyHolder keyHolder = new GeneratedKeyHolder(); //์๋ ์์ฑ๋ ํค ๊ฐ ๊ตฌํด์ฃผ๋ KeyHolder ๊ตฌํ ํด๋์ค
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE) " +
"values (?, ?, ?, ?)",
new String[] { "ID" }); // preparedStatement์ ๋๋ฒ์งธ ์ธ์๋ก ๊ตฌํ ํค ๊ฐ์ ์ด๋ฆ ๋ช
์
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4,
Timestamp.valueOf(member.getRegisterDateTime()));
// ์์ฑํ PreparedStatement ๊ฐ์ฒด ๋ฆฌํด
return pstmt;
}
}, keyHolder);
Number keyValue = keyHolder.getKey(); // ๊ตฌํ ํค๊ฐ์ ์๋ ค์ฃผ๋ ๋ฉ์๋
member.setId(keyValue.longValue()); // ์ ์ ํ ํ์
์ผ๋ก ์บ์คํ
}
SimpleJdbcInsert
๊ฐ์ฒด๋ฅผ ์ด์ฉํ ๋ ์ฌ์ด ์ฝ์
JdbcTemplate
๊ฐ์ฒด์ wrapper
๊ฐ์ฒด์ธ SimpleJdbcInsert
๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ฉด ๋ ๊ฐ๋จํจ
SimpleJdbcInsert
์ฌ์ฉ๋ก
jdbcTemplate
๊ฐ์ฒด๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ์ง ์๊ณ SimpleJdbcInsert
๊ฐ์ฒด๋ก ๊ฐ์ธ ์ฌ์ฉํ๋ค.
package tacos.data;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import com.fasterxml.jackson.databind.ObjectMapper;
import tacos.Taco;
import tacos.Order;
@Repository
public class JdbcOrderRepository implements OrderRepository {
private SimpleJdbcInsert orderInserter;
private SimpleJdbcInsert orderTacoInserter;
private ObjectMapper objectMapper;
@Autowired
public JdbcOrderRepository(JdbcTemplate jdbc) {
this.orderInserter = new SimpleJdbcInsert(jdbc)
.withTableName("Taco_Order") // ๋์ ํ
์ด๋ธ ๋ช
.usingGeneratedKeyColumns("id"); // DB ์๋์์ฑ ํค ์ฌ์ฉ
this.orderTacoInserter = new SimpleJdbcInsert(jdbc)
.withTableName("Taco_Order_Tacos");
this.objectMapper = new ObjectMapper();
// ๋์ค์ ์ปค๋งจ๋ ๊ฐ์ฒด๋ฅผ Map ๊ฐ์ฒด๋ก ๋ฐ๊พธ๋๋ฐ ์ฌ์ฉ
}
@Override
public Order save(Order order) {
order.setPlacedAt(new Date());
long orderId = saveOrderDetails(order);
order.setId(orderId);
List<Taco> tacos = order.getTacos();
for (Taco taco : tacos) {
saveTacoToOrder(taco, orderId);
}
return order;
}
private long saveOrderDetails(Order order) {
@SuppressWarnings("unchecked")
Map<String, Object> values =
objectMapper.convertValue(order, Map.class); // Map ๊ฐ์ฒด๋ก ๋ฐ๊พธ๊ธฐ
values.put("placedAt", order.getPlacedAt());
long orderId =
orderInserter
.executeAndReturnKey(values) // ์คํ ๋ค id ๋๋ ค๋ฐ๊ธฐ
.longValue(); // long์ผ๋ก ๋ณํ
return orderId;
}
private void saveTacoToOrder(Taco taco, long orderId) {
Map<String, Object> values = new HashMap<>(); // execute๋ Map<String, Object> ํ์
์ ์ธ์๋ก ๋ฐ๋๋ค.
values.put("tacoOrder", orderId);
values.put("taco", taco.getId()); // column ์ค์
orderTacoInserter.execute(values); // ์ฝ์
์คํ
}
}
๋จ์ํ ๋ฉ์๋๋ฅผ ์ด์ฉํ๋ฉด ๋๋ฏ๋ก ๋์ฑ ๋น ๋ฅด๋ค.
์คํ๋ง ์ต์ ์ ๋ณํ ์ฒ๋ฆฌ
DB๋ฅผ ๋ค๋ฃจ๋ ๊ฒ์ ๋ฏผ๊ฐํ๊ณ ์ค์ํ๋ฉฐ ๋ณต์กํ ์ฌ์์ด๋ฏ๋ก ์๋นํ ๋ง์ ์ค๋ฅ๋ฅผ ๋ณด๊ฒ๋ ๊ฒ์ด๋ค.
- ์ธ์ฆ/์ธ๊ฐ ๊ด๋ จ ์ค๋ฅ (ex)
CannotGetJdbcConnectionException
) - SQL ๊ตฌ๋ฌธ ์๋ฌ (ex)
BadSqlGrammarException
) - db ์ค์ ๊ด๋ จ ์๋ฌ (ex)
CannotGetJdbcConnectionException
)
์ด๋ฅผ ์ฐจ๋ถํ ์ฝ๊ณ ๋ฌธ์ ๋ฐ์ ์์ธ์ ์ฐพ์ ์ ์๋ค.
์คํ๋ง์ ๋ค์๊ณผ ๊ฐ์ด ์ต์ ์ ์ ์ฒ๋ฆฌํ๋ค.
JDBC, Hibernate, JPA
๋ฑ ์ฌ๋ฌ ์ฐ๋ ๊ธฐ์ ๋ค์ ๊ฐ๊ธฐ ๋ค๋ฅธ ์ต์ ์ ๋ฐ์- -> ์คํ๋ง์ ๊ฐ๊ฐ์ ์ต์
์
์ ๋์ผํ
DataAccessException
์ผ๋ก ๋ณํ- ์ด๋ฅผ ํตํด ์ฐ๋ ๊ธฐ์ ์ ๊ด๊ณ์์ด ๋์ผํ๊ฒ ์ต์ ์ ์ฒ๋ฆฌ ๊ฐ๋ฅ
- ->
DataAccessException
๋ฅผ ์์๋ฐ์ ๊ตฌ์ฒด์ ์ธ ํ์ ํ์ ์ผ๋ก ๋ณํ (ex)DuplicateKeyExcetion
,QueryTimeoutException
)- ์ด๋ฅผ ํตํด ์ด๋ฆ๋ง์ผ๋ก ๋ฌธ์ ์์ธ ์ ์ถ ๊ฐ๋ฅ
DataAccessException
์ RuntimeException
์ ์์๋ฐ๊ณ ์์ผ๋ฏ๋ก, ์คํ๋ง์์ ์๋์ผ๋ก ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ํด์ค๋ค.
๋ฐ๋ผ์ ๊ธฐ์กด์ JDBC์์๋ ๋ฐ๋์ try~catch
๋ฅผ ์ด์ฉํ ์ต์
์
์ฒ๋ฆฌ๋ฅผ ํฌํจํด์ผ ๊ตฌํ ๊ฐ๋ฅํ์ง๋ง, ์คํ๋ง์์๋ ํ์ํ ๊ฒฝ์ฐ์๋ง ์ต์
์
์ฒ๋ฆฌ๋ฅผ ๊ตฌํํด์ค ์ ์๋ค.
ํธ๋์ญ์ ์ฒ๋ฆฌ : @Transactional
JDBC์์๋ ํธ๋์ญ์ ์ ํ๋์ ์์ ๋จ์๋ก ์ปค๋ฐํ๊ฑฐ๋ ๋กค๋ฐฑํ๋ ค๋ฉด ์๋์ ๊ฐ์ด ์ง์ commit๊ณผ rollback ํ์ฌ ์งํํ๋ค.
- ์ฝ๋ ๋๋ฝ, ๋ฐ๋ณต, ๊ด๋ฆฌ ํ๋ฌ ๋ฑ์ ๋ฌธ์ ๊ฐ ์์
Connection conn = null;
try {
conn = DriverManager.getConnection(jdbcUrl, user, pw);
conn.setAutoCommit(false); // ํธ๋์ญ์
๋ฒ์ ์์
//... ๊ธฐํ ์ฟผ๋ฆฌ
conn.commit(); // ํธ๋์ญ์
๋ฒ์ ์ข
๋ฃ ๋ฐ ์ปค๋ฐ
} catch(SQLException ex) {
if (conn != null)
// ํธ๋์ญ์
๋ฒ์ ์ข
๋ฃ : ๋กค๋ฐฑ
try { conn.rollabck(); } catch (SQLException e) {}
} finally {
if (conn != null)
try { conn.close(); } catch (SQLException e) {}
}
์คํ๋ง์ @Transactional
์ด๋
ธํ
์ด์
์ ์ด์ฉํ๋ฉด ์์ฝ๊ฒ ์์ ๊ฐ์ ์ฝ๋๋ฅผ ์๋์ฒ๋ผ ์ค์ผ ์ ์๋ค.
@Transactional
์ ์ด์ฉํ ํธ๋์ญ์
์ฒ๋ฆฌ
import org.springframework.transaction.annotation.Transactional;
@Transactional
public void changePassword(String email, String oldPwd, String newPwd) {
Member member = memberDao.selectByEmail(eamil);
if (member == null)
throw new MemberNotFoundException();
member.changePassword(oldPwd, newPwd);
memberDao.update(member);
}
@Transactional
์ด๋
ธํ
์ด์
์ด ๋ถ์ ๋ฉ์๋๋ ๋์ผํ ํธ๋์ญ์
๋ฒ์ ๋ด์ ์คํ๋๋ฉฐ, ์คํจ์ ์ ๋ถ ๋กค๋ฐฑ ๋๋ค.
@Transactional
๋ฉ์๋ ๋ด๋ถ์ ๋ค๋ฅธ ์ผ๋ฐ ๋ฉ์๋ ๊ณผ์ ๋ด์ ์ค๋ฅ์๋ ๋กค๋ฐฑ๋๋ฉฐ, ์ผ๋ฐ ๋ฉ์๋ ๋ด์ DB ๋ณํ๋ ๋กค๋ฐฑ๋๋ค.
@Transactional์ ์คํ๋ง ์ค์ ๋ฒ
@Transactional
๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ผ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ ๊ฐ์ ์ค์ ์ด ํ์ํ๋ค.
- ๊ตฌ์ฑ ํด๋์ค์ ํ๋ซํผ ํธ๋์ญ์ ๋งค๋์ (PlatformTransactionManager) ๋น ์ค์
@Transactional
์ด๋ ธํ ์ด์ ํ์ฑํ ์ค์
ํ๋ซํผ ํธ๋์ญ์
๋งค๋์ (PlatformTransactionManager) ๋น ์ค์
์คํ๋ง ๊ตฌ์ฑ ํด๋์ค์ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ ์ถ๊ฐํด์ผ ํ๋ค.
@EnableTransactionManagement
:@Transactional
์ด๋ ธํ ์ด์ ์ด ๋ถ์ ๋ฉ์๋๋ฅผ ํธ๋์ญ์ ๋ฒ์์์ ์คํ, ๋น์ด ์ง์ ํธ๋์ญ์ ์ ์ฉ- ํ๋ก์ ๊ธฐ๋ฐ์ด๋ฏ๋ก AOP์์ ๋ฐฐ์ ๋
order, proxyTargetClass
์์ฑ์ ์ค์ ๊ฐ๋ฅํ๋ค.
- ํ๋ก์ ๊ธฐ๋ฐ์ด๋ฏ๋ก AOP์์ ๋ฐฐ์ ๋
PlatformTransactionManager
: ๊ตฌํ ๊ธฐ์ ์ ๊ด๊ณ์์ด ๋์ผํ ๋ฐฉ์์ผ๋ก ํธ๋์ญ์ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ธํฐํ์ด์ค, ์ด์ฉํdatasource
๋ฅผ ์ง์ ํด์ผ ํจ.
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement // ํ๋ซํผ ํธ๋์ญ์
๋งค๋์ ์ฌ์ฉํจ์ ์๋ฏธ
public class AppCtx {
@Bean
public PlatformTransactionManager transactionManager() { // ํธ๋์ญ์
๋งค๋์ ๋ฑ๋ก
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(dataSource());
return tm;
}
@Bean
public ChangePasswordService changePwdSvc() { // @Transactional์ด ์ ์ฉ๋ ํธ๋์ญ์
๋ฉ์๋๊ฐ ์กด์ฌํ๋ ์๋น์ค ๊ฐ์ฒด ๋ฑ๋ก
ChangePasswordService pwdSvc = new ChangePasswordService();
pwdSvc.setMemberDao(memberDao());
return pwdSvc;
}
}
@Transactional
์ด๋
ธํ
์ด์
ํ์ฑํ ์ค์
ChangePasswordService
๊ฐ์ฒด ์์
package spring;
import org.springframework.transaction.annotation.Transactional;
public class ChangePasswordService {
private MemberDao memberDao;
@Transactional // ์ด์ changePassword ๋ฉ์๋๋ ํธ๋์ญ์
๋ฒ์ ๋ด์์ ์คํ๋จ
public void changePassword(String email, String oldPwd, String newPwd) {
Member member = memberDao.selectByEmail(email);
if (member == null)
throw new MemberNotFoundException();
member.changePassword(oldPwd, newPwd);
memberDao.update(member);
}
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
}
์ด์ ์ํ ๋, ์์ ์๋น์ค๋ฅผ ๋ถ๋ฌ์ ํธ๋์ญ์ ์ ์ฌ์ฉํ ์ ์๋ค.
ํ๋ก์๋ฅผ ์ด์ฉํ ์ปค๋ฐ, ๋กค๋ฐฑ ์ฒ๋ฆฌ
์คํ๋ง์ @Transactional
์ด๋
ธํ
์ด์
์ ์์ ๋ฐฐ์ ๋
AOP
๋ฅผ ์ด์ฉํด ํธ๋์ญ์
์ ์ฒ๋ฆฌํ๋ค. ์ฆ ํ๋ก์๋ฅผ ์ด์ฉํ๋ค.
- ์คํ๋ง์ด ๊ตฌ์ฑ ํด๋์ค์์
@EnableTransactionManagement
ํ๊ทธ๋ฅผ ๊ฐ์ง @Transactional
์ด๋ ธํ ์ด์ ์ด ์ ์ฉ๋ ๋น ๊ฐ์ฒด๋ฅผ ์ฐพ์- ์ ์ ํ ํ๋ก์ ๊ฐ์ฒด ์์ฑ ๋ค ๋ฉ์๋ ์ด์ฉ ์ ์๋์ ๊ฐ์ด ํ๋ก์๋ฅผ ํตํด ํธ๋์ญ์ ์ ์ฉ
-
ํธ๋์ญ์ ์ฑ๊ณต ์ ํ๋ก์ ์ปค๋ฐ ๋์
-
ํธ๋์ญ์ ์คํจ ์ ํ๋ก์ ๋กค๋ฐฑ ๋์
๋กค๋ฐฑ์ RuntimeException
์ด ์ผ์ด๋ ๋๋ง ์์ฑ๋๋ฏ๋ก SQLException
๊ฐ์ด ๋ค๋ฅธ ํ์
์ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ๋ฉด ๋๋ค.
@Transactional
์ ์๋ฌ ์ฒ๋ฆฌ ์ถ๊ฐ ์์
- ๋ฐ๋๋ก
noRollbackFor
์์ฑ์ ์ง์ ํด์ฃผ๋ฉด ํด๋นํ๋ ์ต์ ์ ์ ๋กค๋ฐฑํ์ง ์๋๋ค.
@Transactional(rollbackFor = {SQLException.class, IOException.class})
public void someMethod(){
//...
}
@Transactional์ ์ฃผ์ ์์ฑ๊ณผ ์ ํ
- value: ํธ๋์ญ์
๊ด๋ฆฌํ
PlatformTransactionManager
๋น์ ์ด๋ฆ ์ง์ , ์์ผ๋ฉด ํด๋น ํ์ ์ ๋น์ ์๋์ผ๋ก ์ฐพ์ - isolation: ํธ๋์ญ์
๊ฒฉ๋ฆฌ ๋ ๋ฒจ, READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE ๋ฑ์ด ์กด์ฌ, ๊ธฐ๋ณธ๊ฐ์
Isolation.DEFAULT
- timeout: ํธ๋์ญ์ ์ ํ ์๊ฐ ์ง์ , ์ด๋จ์์ด๋ฉฐ ๊ธฐ๋ณธ๊ฐ -1์ธ ๊ฒฝ์ฐ DB์ ์ค์ ์ ๋ฐ๋ฆ
- propagation: ํธ๋์ญ์
์ ํ ํ์
์ง์ , ๊ธฐ๋ณธ๊ฐ์
Propagation.REQUIRED
- ๊ธฐ๋ณธ ๊ฐ์ด๋ฉด ๋ฉ์๋ ์ํ ์ ์ด๋ฏธ ์งํ์ค์ธ ํธ๋์ญ์ ์ด ์กด์ฌํ๋ฉด ํด๋น ํธ๋์ญ์ ์ ๋ณํฉ
@Transactional
์ด ์ ์ฉ๋ ๋ฉ์๋ ๋ด๋ถ์ ๋ ๋ค๋ฅธ @Transactional
๋ฉ์๋๊ฐ ์กด์ฌํ๋ฉด, ํธ๋์ญ์
์ ํ(transaction propagation) ์์ฑ์ ๋ฐ๋ผ ๋ค๋ฅธ ๋์์ ํ๋ค.
์๋ฅผ ๋ค์ด Propagation.MANDATORY
๋ ์ด๋ฏธ ์งํ์ค์ธ ํธ๋์ญ์
์ด ์๋ค๋ฉด ์๋ฌ๋ฅผ ์ผ์ผํค๋ฉฐ, Propagation.REQUIRES_NEW
์ ํญ์ ์๋ก์ด ํธ๋์ญ์
์ผ๋ก ๋ฎ์ด์์ด๋ค.
- ์ด์ธ์๋
SUPPORTS
,NOT_SUPPORTED
,NEVER
,NESTED
๋ฑ์ด ์กด์ฌ
๋ก๊น ์ฒ๋ฆฌ
ํธ๋์ญ์
์ฒ๋ฆฌ, ์ค๋ฅ, ๊ด๋ จ ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ๋ณด๊ธฐ ์ํด์ ์ผ์ผ์ด ํ๋ฆฐํธํ๊ธฐ ๋ณด๋จ Log4j2
๋ Logback
๋ฑ์ ์ธ๋ถ ํจํค์ง๋ฅผ ์ด์ฉํ ์ ์๋ค.
- ์๋ฐํ ๋งํ๋ฉด ์คํ๋ง ์์ฒด ๋ก๊น
๋ชจ๋์ธ
spring-jcl
์ด ์์ ๋ก๊น ๋ชจ๋์ ์ธ์ํ์ฌ ์ฌ์ฉํ๋ค.
๋ฉ์ด๋ธ์ด๋ ๊ทธ๋๋ค์ ์ด์ฉํด Logback์ ์์กด ๋ชจ๋๋ก ์ถ๊ฐํ๊ณ , ํด๋์ค ํจ์ค์ ์ค์ ํ์ผ์ ์์น์ํค๊ธฐ ์ํด src/main/resources
ํด๋๋ฅผ ๋ง๋ค์ด ์ค์ xml
์ ์ถ๊ฐํ๊ณ , ๋ฉ์ด๋ธ, ๊ทธ๋๋ค๋ก ํ๋ก์ ํธ๋ฅผ ์
๋ฐ์ดํธํ๋ฉด ๋๋ค.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %c{2} - %m%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout" />
</root>
<logger name="org.springframework.jdbc" level="DEBUG" /> <!--๋ก๊ทธ ๋ฉ์์ง ์์ธ ๋ณด๊ธฐ ์ค์ -->
</configuration>
์ดํ ํฐ๋ฏธ๋์์ ๋ค์ ์์ ๊ฐ์ ํต์ฌ ๋ก๊ทธ๋ฅผ ํ์ธํ ์ ์๋ค.
๋ ์ง ์๊ฐ DEBUG o.s.j.d DataSourceTransactionManager - Initiating transaction commit
๋ ์ง ์๊ฐ DEBUG o.s.j.d DataSourceTransactionManager - Committing JDBC transaction on Connection[ProxyConnection...]
๋ ์ง ์๊ฐ DEBUG o.s.j.d DataSourceTransactionManager - Initiating transaction rollback
๋ ์ง ์๊ฐ DEBUG o.s.j.d DataSourceTransactionManager - Rolling back JDBC transaction on Connection[ProxyConnection...]
_articles/web/backend/Spring/Spring5 ์ ๋ฌธ-DB ์ฐ๋.md