[Spring/Database] JDBC์ JDBCTemplate
โ ๐๏ธ Fairy-Tale Island ๐๏ธ ๋ฆฌํฉํ ๋ง!
Spring Database ์ ๊ทผ ๋ฐฉ์
Spring์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๋ ๋ฐฉ์๋ค์ ์์๋ณธ๋ค. ORM ์ด์ ์ ์ ๊ทผ ๋ฐฉ๋ฒ๋ค๊ณผ ๋จ์ ์ ์์๋ณด๊ณ , ์ด๋ฅผ ๋ณด์ํ๊ธฐ ์ํด ๋ฑ์ฅํ ORM์ ๊ฐ๋ ๊ณผ Spring์์ ์ฌ์ฉํ๋ Java ORM ๊ธฐ์ ์ ์์๋ณธ๋ค.
#1 [Database] ORM ์ ์, ๋ฑ์ฅ ๋ฐฐ๊ฒฝ, ์ฅ๋จ์
#2 [NOW] [Spring/Database] JDBC์ JDBCTemplate
#3 [Spring/Database] Spring JPA์ Spring Data JPA
#4 ์ ์ฒด ์ฝ๋ ์ ์ฅ์ : Various way of Spring Database Access
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ : JDBC
JDBC(Java Database Connectivity)๋ Java ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ ๋ฐ ์ ๋ฐ์ดํธํ๊ฑฐ๋, ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ Java์์ ์ฌ์ฉํ ์ ์๋๋ก ํ๋ ์๋ฐ API์ด๋ค.
Spring์์ ์์ JDBC๋ฅผ ์ด์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์.
Spring JDBC ์ฌ์ฉ ๋ฐฉ๋ฒ
build.gradle
์ ์์กด์ฑ์ ์ถ๊ฐํ๋ค.
1
2
3
4
5
6
7
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
...
}
์ด์ ์ ๊ฐ๋ฐ์ ์์ํ ๋น์์๋ ์ด๋ค ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ ์ง ์ ํด์ง์ง ์์์, ์ธํฐํ์ด์ค MemberRepository๋ฅผ ๊ตฌํํด๋์ ์ํ์๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ ํด์ง๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ด์ ์ฐ๊ฒฐ ๋ค๋ฆฌ์ธ JDBCMemberRepository๋ฅผ ๊ตฌํํ์๋ค.
JDBC๋ฅผ ์ด์ฉํ ๊ตฌํ์ ๊ฒฝ์ฐ, ์์ฑ์์์ DataSource
์ฃผ์
์ ํ์๋ก ํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class JDBCMemberRepository implements MemberRepository {
private final DataSource dataSource;
public JDBCMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Member save(Member member) {
String sql = "insert into member(name) values(?)";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, member.getName());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if (rs.next()) {
member.setId(rs.getLong(1));
} else {
throw new SQLException("id ์กฐํ ์คํจ");
}
return member;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
{ ... }
}
String sql = "insert into member(name) values(?)";
์ฒ๋ผ SQL๋ฌธ์ ์ง์ ๊ตฌํํด์ผ ํ๊ณ , try-catch
๋ฌธ๊ณผ ๊ฐ์ด ๋ชจ๋ ๋ฉ์๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๊ตฌ๋ฌธ์ ์์ฑํด์ผ ํ๋ค.
์คํค๋ง๊ฐ ํ์ฅ๋ ๋๋ง๋ค SQL๋ฌธ์ ์์ ํด์ผ ํ๋ ์ ์ ์ํํธ์จ์ด ๊ฐ๋ฐ ์์น์์ Open-Closed ์์น์๋ ์ด๊ธ๋๊ณ , ๊ฐ๋ฐ ๋น์ฉ์ ์ฆ๊ฐ์ํจ๋ค. try-catch
์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ฝ๋๊ฐ ๋ฐ๋ณต๋๋ ์ ๋ ๋ํ์ ์ธ ๋ถํธํ ์ ์ด๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ : JDBCTemplate
์์ ๊ฐ์ ์์ JDBC ์ฌ์ฉ์ ๋ฌธ์ ์ ์ ์ค์ฌ์ฃผ๋ JDBCTemplate๊ฐ ์๋ค.
Spring JDBCTemplate ์ฌ์ฉ ๋ฐฉ๋ฒ
์์ JDBC์ ๊ฐ์ ํ๊ฒฝ์ค์ ์ด๋ค. build.gradle
์ ๊ทธ๋๋ก ์ฌ์ฉํ๋ค.
JDBCMemberRepository์ ๋ง์ฐฌ๊ฐ์ง๋ก, MemberRepository์ ๊ตฌํ์ฒด์ธ JDBCTemplateMemberRepository๋ฅผ ์์ฑํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class JDBCTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
public JDBCTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", member.getName());
Number key = jdbcInsert.executeAndReturnKey(new
MapSqlParameterSource(parameters));
member.setId(key.longValue());
return member;
}
@Override
public Optional<Member> findById(Long id) {
List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
return result.stream().findAny();
}
{...}
}
์ด์ ์์ JDBC๋ฅผ ์ฌ์ฉํ ๋๋ ๋ชจ๋ ๋ฉ์๋ ๊ตฌํ๋ง๋ค try-catch
๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ์ค์ ํด์ผ ํ๋๋ฐ, JDBCTemplate์์๋ ๊ทธ๋ ์ง ์๋ค. ํ์ฐํ ์ฐ๊ฒฐ ์ค์ ์ ๊ด๋ จ๋ ์ค๋ณต ์ฝ๋๊ฐ ์ญ์ ๋์๋ค!
ํ์ง๋ง List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
๊ณผ ๊ฐ์ด, SQL๋ฌธ์ ์ง์ ์์ฑํด์ผ ํ๋ค.
Spring์ ๊ด๋ฆฌ ๋ฐฉ๋ฒ
์์ ๊ฐ์ด ์์ JDBC ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๋ค๊ฐ, JDBCTemplate๋ก ์ ๊ทผ ๋ฐฉ๋ฒ์ ๋ณ๊ฒฝํ๊ณ ์ ํ๋ค๋ฉด, ๊ด๋ จ๋ ๋ชจ๋ ์ฝ๋๋ค์ ์์ ํด์ผ ํ ๊น? ๋ต์ ์ ํ ์๋๋ค.
์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ ํด์ง์ง ์์์, MemberRepository ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ค๊ณ ํ๋ค. ์ธํฐํ์ด์ค๋ก ๋จผ์ ๊ตฌํํด์ผ ํ ๋ฉ์๋๋ค์ ์ ์๋ง ํด๋๊ณ , ๊ตฌํ์ฒด๋ก JDBCMemberRepository์ JDBCTemplateMemberRepository๊ฐ ์กด์ฌํ๋ ๊ฒ์ด๋ค. SpringConfig
์ค์ ๋ง์ผ๋ก, ๋ค๋ฅธ ์ฝ๋๋ค์ ์ ํ ์๋์ง ์๊ณ ์ค์ ํ์ผ๋ง์ผ๋ก ๊ตฌํ์ฒด๋ฅผ ๋ฐ๊ฟ ์ ์๋ค! SOLID ๊ฐ๋ฐ ์์น์ Open-Closed ์์น์ ์คํํ๋ ๊ฒ์ด๋ค! ์ด๋ป๊ฒ ํ์ฅ์๋ ์ด๋ ค ์๊ณ ์์ ์๋ ๋ซํ ์๋์ง ํ์ธํ์.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
public class SpringConfig {
private final DataSource dataSource;
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// JDBC
return new JDBCMemberRepository(dataSource);
// JDBCTemplate
return new JDBCTemplateMemberRepository(dataSource);
}
}
public MemberRepository memberRepository()
์ธํฐํ์ด์ค๋ก ๊ตฌํํ MemberRepository์ ๊ตฌํ์ฒด๋ฅผ ๋ฌด์์ผ๋ก ํ ๊ฑด์ง ๋ฐํ๋ง ๋ณ๊ฒฝํ๋ฉด, ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋ฐ๋ก ๊ตฌํ์ฒด๋ฅผ ๊ฐ์๋ผ์ธ ์ ์๋ค! ์คํ๋ง์ Dependency Injection(์์กด์ฑ ์ฃผ์
)์ ํฐ ์ฅ์ ์ด๋ค.