
JdbcTemplate์ ์คํ๋ง ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ JDBC(Java Database Connectivity) ๊ธฐ๋ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ก์ธ์ค๋ฅผ ๋จ์ํํ๊ณ ํธ๋ฆฌํ๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํด๋์ค์ด๋ค. JDBC๋ ์๋ฐ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฐ๊ฒฐ์ ๊ด๋ฆฌํ๊ณ SQL ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ๋ฐ ์ฌ์ฉ๋๋ ํ์ค API์ด๋ฉฐ, JdbcTemplate์ ์ด๋ฌํ JDBC ๊ธฐ๋ฅ์ ๋ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์คํ๋ง์์ ์ ๊ณตํ๋ ๋๊ตฌ ์ค ํ๋์ด๋ค.
JdbcTemplate์ ์ฌ์ฉํ๋ฉด ์ผ๋ฐ์ ์ธ JDBC ์ฝ๋์์ ๋ฐ์ํ ์ ์๋ ๋ฐ๋ณต์ ์ด๊ณ ์ง๋ฃจํ ์์ ๋ค์ ์ค์ผ ์ ์์ผ๋ฉฐ, ์์ธ ์ฒ๋ฆฌ ๋ฐ ๋ฆฌ์์ค ๊ด๋ฆฌ ๋ฑ์ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค๋ค.
๊ฐ๊ฒฐํ ์ฝ๋
: JDBC ์ฝ๋๋ ๋ณดํต ๋ง์ ๋ถ๋ถ์ด ๋ฐ๋ณต๋๊ณ ๋ฒ๊ฑฐ๋ก์ด๋ฐ, JdbcTemplate์ ์ฌ์ฉํ๋ฉด ์ด๋ฌํ ๋ถ๋ถ์ ์ค์ผ ์ ์์ต๋๋ค. ๊ฐ๊ฒฐํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋๋ก ๋ค์ํ ํ
ํ๋ฆฟ ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
์์ธ ์ฒ๋ฆฌ ์๋ํ
: JdbcTemplate์ JDBC ์ฐ์ฐ ์ค ๋ฐ์ํ๋ ์ผ๋ฐ์ ์ธ ์์ธ๋ค์ ์ฒ๋ฆฌํด์ฃผ๋๋ฐ, ์ด๋ฅผ ํตํด ๋ช
์์ ์ธ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
์ฝ๋ฐฑ ํจํด ํ์ฉ
: JdbcTemplate์ JDBC ์ฐ์ฐ์ ์ผ๋ถ ๋๋ ์ ์ฒด๋ฅผ ์ฝ๋ฐฑ ํจํด์ ํตํด ์ฒ๋ฆฌํ ์ ์๋๋ก ์ง์ํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ฌ์ฉ์๋ ํน์ ์์
์ ์ํํ๊ธฐ ์ํด ํ์ํ ๋ก์ง์ ์ ๊ณตํ ์ ์์ต๋๋ค(RowMapper์ PreparedStatementCallback).
ํธ๋ฆฌํ ResultSet ์ฒ๋ฆฌ
: JdbcTemplate์ RowMapper ์ธํฐํ์ด์ค๋ฅผ ํตํด ResultSet์ ๋ฐ์ดํฐ๋ฅผ ์๋ฐ ๊ฐ์ฒด๋ก ๋งคํํ ์ ์๋๋ก ์ง์ํฉ๋๋ค. ์ด๋ ๋ฐ๋ณต์ ์ธ ResultSet ์ฒ๋ฆฌ๋ฅผ ํธ๋ฆฌํ๊ฒ ํด์ค๋๋ค.
ํธ๋์ญ์
๊ด๋ฆฌ
: JdbcTemplate์ ์คํ๋ง์ ํธ๋์ญ์
๋งค๋์ ์ ํจ๊ป ์ฌ์ฉ๋ ์ ์์ด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ๋์ญ์
์ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
spring-jdbc๋ Spring Framework์์ ์ ๊ณตํ๋ JDBC(Java Database Connectivity) ์ง์์ ๋ด๋นํ๋ ๋ชจ๋ ์ค ํ๋๋ก์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฐ๊ฒฐ์ ๊ด๋ฆฌํ๊ณ JDBC ์์ ์ ์ํํ๋ ๋ฐ ๋์์ ์ฃผ๋ ์ฌ๋ฌ ์ ํธ๋ฆฌํฐ ํด๋์ค์ API๋ฅผ ํฌํจํ๊ณ ์๋ค.
JDBC API๋ฅผ ๊ฐ์ ํ๋ฉด์ ์ปค๋ฅ์ ํ์ ์ ๊ณตํ๋ ํ์ฅ๋ API
"org.springframework:spring-jdbc:$spring_version"Spring Framework์์ spring-jdbc ๋ชจ๋์ ์ฌ์ฉํ ๋, ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ์ปค๋ฅ์ ํ์ ์ฌ์ฉํ ์ ์๋ค. ์ผ๋ฐ์ ์ผ๋ก Spring์ ์ธ๋ถ ์ปค๋ฅ์ ํ์ ์ฌ์ฉํ๋๋ก ๊ตฌ์ฑํ ์ ์์ผ๋ฉฐ Tomcat์ DataSource๊ฐ ๋ง์ด ์ฌ์ฉ๋๋๋ฐ, ์ด๋ Apache Tomcat์ด ๋ด์ฅํ๊ณ ์๋ ์ปค๋ฅ์ ํ ๊ตฌํ์ฒด ์ค ํ๋์ด๋ค.
- ์คํ๋ง jdbc๋ ์ปค๋ฅ์ ํ์ ์ง์ํ๋ API์ด๋ค
- API๋ ๊ตฌํ์ฒด๊ฐ ๋ฐ๋์ ํ์ํ๋ฉฐ, ์ด๋ ๋ง์ด ์ฌ์ฉํ๋ ๊ตฌํ์ฒด๋ tomcat-jdbc์ DataSource์ด๋ค.
์ฆ, spring-jdbc๋ ์ปค๋ฅ์ ํ์ ์ง์ํ๋ API์ด๊ณ , tomcat-jdbc์ DataSource๋ ์ปค๋ฅ์ ํ์ ๊ตฌํ์ฒด ์ด๋ค.
DriverManger -> ์ปค๋ฅ์
ํ ์์ : ์ผ๋ฐ์ ์ธ ์ ์implementation 'org.apache.tomcat:tomcat-jdbc:10.1.16'Tomcat์ DataSource๋ ์ปค๋ฅ์ ์ ํ์ ๋ฐํํ์ฌ ์ฌ์ฌ์ฉํ๋ฏ๋ก ๊ฐ๋ฐ์๊ฐ ์ง์ ์ปค๋ฅ์ ์ ๋ซ์ ํ์๊ฐ ์๋ค.
Tomcat์ DataSource๋ฅผ ์ฌ์ฉํ๋ฉด ์ปค๋ฅ์ ๊ด๋ฆฌ์ ๋ํ ๋ถ๋ด์ด ๊ฐ์ํ๊ณ , ์ปค๋ฅ์ ํ๋ง์ ํตํด ์ฑ๋ฅ์ด ํฅ์๋๋ค. ๊ฐ๋ฐ์๋ ์ปค๋ฅ์ ์ ์ป์ ํ์ ํ์ํ ์์ ์ ์ํํ๊ณ , ์ปค๋ฅ์ ์ ๋ซ์์ฃผ๋ ๋์ ๋ฐํ๋ง ํ๋ฉด ๋๋ค.
โจ ์ด ๋, ์ปค๋ฅ์ ํ์ ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋์ ๊ฐ์ฒด๋ฅผ ๋ฐ์ ์ฌ์ฉํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ฐ๊ฒฐ๋ ๊ฐ์ฒด๊ฐ ์ ํด์ํ์ธ์ง ์ฒดํฌํ๋ ๊ฒ์ด ์ค์ํ๋ค. ์ค๋์๊ฐ ์ฐ๊ฒฐ์ด ๋์ด์์ง ์์ผ๋ฉด DB์์ ์ฐ๊ฒฐ์ด ๋๊ธฐ๊ณ ์ฐ๊ฒฐ์ด ๋๊ธฐ๊ฒ ๋๋ฉด ์ค๋ฅ๊ฐ ์๊ธฐ๊ธฐ ๋๋ฌธ์ ์ค์ DB์ ์ฐ๊ฒฐ๋ ์ํ์ธ์ง ์ฃผ๊ธฐ์ ์ผ๋ก ์ฒดํฌํด์ผ ํ๋ค.โจ
tomcat-jdbc : ์ปค๋ฅ์ ํ์ ์ง์ํ๋ ์ฐ๊ฒฐ ๊ฐ์ฒด -> ์์์ ์๋นํจ -> ์์ ํด์ ํด์ค์ผํจ
@Configuration
public class DBConfig {
@Bean(destroyMethod = "close")
public DataSource dataSource(){
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
dataSource.setUsername("spring6");
dataSource.setPassword("aA123456");
dataSource.setInitialSize(2); // ์ปค๋ฅ์
ํ์ ์ด๊ธฐํ ํ ๋ ์์ฑํ ๊ฐฏ์ ๋ง์ผ๋ฉด ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆผ
dataSource.setTestWhileIdle(true); // db์์ ์ฐ๊ฒฐ์ด ๋์ด์ก๋ ์ง ํ์ธ
dataSource.setTimeBetweenEvictionRunsMillis(3000); // ์ ํจ์ํ๋ฅผ ์ ์งํ ์ต์ด ์๊ฐ
dataSource.setMinEvictableIdleTimeMillis(30*1000); // ์ ํจ ์ปค๋ฅ์
์ ๊ฒ์ฌํ ์ฃผ๊ธฐ ์๊ฐ
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
}
์์ํด์ ๊ฐ ํ์ํ์ง ์๊ณ ์ปดํ์ผ์์ธ๊ฐ ์๋ ๋ฐํ์์์ธ๋ก ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ SQLException๋ฑ try catch๋ก ์ก์ง ์์๋ ์์ธ๊ฐ ๋ฐ์ํ์ง ์๋๋ค. ๋๋๋ก try catch๊ฐ ํ์ํ๊ธฐ๋ ํจ
์๋ ๋งคํ์ด ๋จ ํน์ ๋ฐ์ดํฐ ๊ฐ์ฒด๋ก ๋ฐํํด์ฃผ๋ ๋ก์ง์ด ์๋ค.
๊ฐ ์ฐ๋ ๊ธฐ์ ์ ๋ฐ๋ผ ๋ฐ์ํ๋ ์์ธ๋ฅผ ์คํ๋ง์ด ์ ๊ณตํ๋ ์์ธ๋ก ๋ณํํจ์ผ๋ก์จ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ ๊ธฐ์ ์ ์๊ด์์ด ๋์ผํ ์ฝ๋๋ก ์์ธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋๋ค.
๐ JDBC API
- ์กฐํ(SELECT)
ResultSet executeQuery(..)- ๋ณ๊ฒฝ(INSERT, UPDATE, DELETE)
int executeUpdate(..) : ๋ฐ์๋ ๋ ์ฝ๋ ๊ฐ์ ๋ฐํ
<T> List<T> query(String sql, RowMapper<T> rowMapper)
/* Execute a query given static SQL, mapping each row to a result object via a RowMapper. */
<T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args)
/* Query given SQL to create a prepared statement from SQL and a list of arguments to bind to the query, mapping each row to a result object via a RowMapper. */
RowMapper<T> : ๋ณํ, ๊ฒ์ db์์ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์ํ๋ ๋ฐ์ดํฐ ๊ฐ์ฒด๋ก ๋ณํํด์ค... : ๊ฐ๋ณ์ new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
return null;
}
});
RowMapper๋ JDBC ๊ฒฐ๊ณผ์ ์์ ๊ฐ ํ(row)์ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ์ถ์ถํ ์ง ์ ์ํ ์ธํฐํ์ด์ค๋ก์ ๊ตฌํ์ ํตํด ResultSet์์ ์ฝ์ ๋ฐ์ดํฐ๋ฅผ ์๋ฐ ๊ฐ์ฒด์ setter๋ฉ์๋๋ฅผ ํตํด ๋งคํํ ์ ์๋ค.
ResultSet์ ์ปค์์ ์์น๋ฅผ ์๋ฏธํ๊ธฐ ๋๋ฌธ์ next๋ก ๋ค์ ๊ฐ์ ๋ถ๋ฌ์์ผ ํ๋๋ฐ JDBCTemplate์ query๋ฉ์๋๋ mapRow๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ํตํด ์ฝ๊ฒ ๋ถ๋ฌ์ฌ ์ ์๋ค.
@Test
@DisplayName("์กฐํํ
์คํธ")
void selectTest(){
String sql = "select * from member";
List<Member> memberList = jdbcTemplate.query(sql, this::mapper);
memberList.stream().forEach(System.out::println);
}
private Member mapper(ResultSet rs, int i) throws SQLException{
return Member.builder()
.userId(rs.getString("USER_ID"))
.userPw(rs.getString("USER_PW"))
.userName(rs.getString("USER_NAME"))
.userNo(rs.getLong("USER_NO"))
.email(rs.getString("EMAIL"))
.build();
}
int update(String sql)
/* Issue a single SQL update operation (such as an insert, update or delete statement). */
int update(String sql, Object... args)
/* Issue a single SQL update operation (such as an insert, update or delete statement) via a prepared statement, binding the given arguments. */
@ExtendWith(SpringExtension.class)
// ํ
์คํธํ๊ฒฝ์์์ ์คํ๋ง ์ปจํ
์ด๋๋ก ์ฌ์ฉํ ํด๋์ค
@ContextConfiguration(classes = AppConfig.class)
class DBConfigTest {
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
void insertTest(){
// DataAccessException -> RuntimeException = ์์ธ์ฒ๋ฆฌ x
String sql = "insert into member (user_no, user_id, user_name, user_pw, email)" +
"values (seq_member.nextval, ?, ?, ?, ?)";
int update = jdbcTemplate.update(sql, "user01", "hong01", "123456", "e@e");
System.out.println(update); // 1
}
}
- ์ ๋ณด์ ์์ ๋ฐ๋ผ ๋ก๊ทธ ๋ ๋ฒจ์ด ์์ / ์๋๋ก ๊ฐ์๋ก ์์ด ๋ง์์ง
- FATAL
- ERROR
- WARN : ๊ธฐ๋ฅ์ ๋ฌธ์ ๋ ์์ง๋ง, ํฅํ ๋ฌธ์ ๊ฐ๋ฅ์ฑ์ด ์์
(์ ๋ณด์ฑ ๋ก๊ทธ)
- INFO
- DEBUG
- TRACE
xml properties๋ก ๋ก๊ทธ๋ ๋ฒจ์ ์ค์ ํ ์ ์์ -> ํจํค์ง๋ resource : ์ ์ ์์
<?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="TRACE" />
</configuration>
ConsoleAppender : ์ฝ์ํ๋ฉด์ ๋ก๊ทธ ์ถ๋ ฅ / html๋ฑ ํ์ผ๋ก ์ถ๋ ฅํ๋ ๊ฒฝ์ฐ๋ ์์%d %5p %c{2} - %m%n : %d(๋ ์ง, ์๊ฐ) / %5p(5์ ๋ด์์ ๋ก๊ทธ ๋ ๋ฒจ ์ถ๋ ฅ) / %c{2}(ํจํค์ง๋ฅผ ํ๊ธ์๋ก ์์ฝ + ํด๋์ค๋ช
) / %m(์ถ๋ ฅ ๋ฉ์ธ์ง)INFO : ๊ธฐ๋ณธ ๋ ๋ฒจ์ ์ธํฌ๋ก ์ค์ ํ์ฌ ์ธํฌ ์ด์ ๋ชจ๋ ๋ ๋ฒจ ์ถ๋ ฅ<logger name="org.springframework.jdbc" level="TRACE" /> : ํน์ ํจํค์ง๋ ๋๋ฒ๊ทธ๋ ๋ฒจ@ExtendWith(SpringExtension.class)
// ํ
์คํธํ๊ฒฝ์์์ ์คํ๋ง ์ปจํ
์ด๋๋ก ์ฌ์ฉํ ํด๋์ค
@ContextConfiguration(classes = AppConfig.class)
class DBConfigTest {
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
@DisplayName("insertํ ์ํ์ค ๋ฒํธ ์ถ์ถํ๊ธฐ")
void insertTest2() {
KeyHolder keyHolder = new GeneratedKeyHolder();
int affectedRows = jdbcTemplate.update(con -> {
String sql = "INSERT INTO MEMBER (USER_NO, USER_ID, USER_PW, USER_NAME, EMAIL) \n" +
"VALUES (SEQ_MEMBER.nextval, ?, ?, ?, ?)";
PreparedStatement preparedStatement = con.prepareStatement(sql, new String[]{"USER_NO"});
preparedStatement.setString(1, "USER201");
preparedStatement.setString(2, "123456");
preparedStatement.setString(3, "์ฌ์ฉ์201");
preparedStatement.setString(4, "USER199@test.org");
return preparedStatement;
}
, keyHolder);
// keyHolder.getKey(); ๋ฐํ ํ์
์ด NUMBER์
Long key = keyHolder.getKey().longValue();
System.out.println(key);
}
}
@ExtendWith(SpringExtension.class)
// ํ
์คํธํ๊ฒฝ์์์ ์คํ๋ง ์ปจํ
์ด๋๋ก ์ฌ์ฉํ ํด๋์ค
@ContextConfiguration(classes = AppConfig.class)
class DBConfigTest {
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
@DisplayName("1๊ฑด ์กฐํ")
void selectTest2(){
String user_id = "USER201";
String sql = "select * from member where user_id = ?";
try {
Member member = jdbcTemplate.queryForObject(sql, this::mapper, user_id); // ์์ธ์ ๋ฏผ๊ฐํ๋ค.
System.out.println(member);
}catch (DataAccessException e){
System.out.println("์กฐํ๋ ๋ฐ์ดํฐ ์์");
}
}
@Test
@DisplayName("๋ฉค๋ฒ์ ์กฐํ")
void selectTest3(){
String sql="select count(*) from member";
Long l = jdbcTemplate.queryForObject(sql, long.class);
System.out.println(l);
}
}
SQL์ ์คํ ๋จ์๋ก commit(DB์๊ตฌ๋ฐ์)์ ๊น์ง๊ฐ ํ๋์ ํธ๋์ญ์
์ด๋ค.
ํธ๋์ญ์
์ ์๋์ผ๋ก ๊ด๋ฆฌํด์ค์ผ ํ ๋(์ฐ์๋ ์ปค๋ฆฌ๋ ์๋์ผ๋ก ๊ด๋ฆฌํด์ผํจ) ์ฌ์ฉ
/**
* PlatformTransactionManager : ์ธํฐํ์ด์ค
* DataSourceTransactionManager : ๊ตฌํ์ฒด
*/
@Bean
public PlatformTransactionManager transactionManager(){
DataSourceTransactionManager tm = new DataSourceTransactionManager(dataSource());
return tm;
}
ํธ๋์ญ์ ๊ด๋ จ๋ ๋ถ๋ถ์ ๋น์ผ๋ก ์ถ๊ฐํ๋ค. (์คํ๋ง๋ถํธ์์ ์๋)
@Transactional ์ ๋ํ
์ด์
(ํธ๋์ญ์
AOP)conn.setAutocommit(false); // ๊ณตํต ๊ธฐ๋ฅ
// ํต์ฌ ๊ธฐ๋ฅ
SQL1
SQL2
SQL3
...
conn.commit(); // ๊ณตํต ๊ธฐ๋ฅ
PlatformTransactionManager๊ฐ ์์ ๋ก์ง์ ํ๋ก์๋ก ๊ตฌํํด์ฃผ๊ธฐ ๋๋ฌธ์
@Transactional์ ๋ํ ์ด์ ์ด ๋ถ์ ํด๋์ค๋ ๋ฉ์๋๋ ์ ๋ก์ง์ด ์ ์ฉ๋๋ค.