Post

[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(์˜์กด์„ฑ ์ฃผ์ž…)์˜ ํฐ ์žฅ์ ์ด๋‹ค.


Untitled

This post is licensed under CC BY 4.0 by the author.