[21강]

2023. 2. 5. 17:44Spring 강의/section6

스프링 JdbcTemplate

순수 Jdbc와 동일한 환경설정을 하면 된다.

   -> build.gradle에서 넣은 코드

implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'

스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서의 반복 코드를 대부분 제거해준다.

하지만 sql은 직접 작성해야 한다.

 

먼저 repository - JdbcTemplateMemberRepository 클래스를 만든다.

 

순수 jdbc로 만들었을 때 이 길었던 코드를

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
public class JdbcMemberRepository implements MemberRepository {
    //db에 붙으려면 DataSource가 필요하다.
    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);
            //id값 세팅 없이 저장할 때 자동으로 id값 생성시켜주는 옵션
 
            pstmt.setString(1, member.getName());
 
            pstmt.executeUpdate(); //db에 쿼리가 날아간다.
            rs = pstmt.getGeneratedKeys(); //RETURN_GENERATED_KEYS와 매칭해서 사용할 수 있다.
                                           // db가 생성해준 키를 반환받을 수 있다.
            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);
        }
    }
    @Override
    public Optional<Member> findById(Long id) {
        String sql = "select * from member where id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setLong(1, id);
 
            rs = pstmt.executeQuery(); //쿼리 조회하기
 
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            } else {
                return Optional.empty();
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public List<Member> findAll() {
        String sql = "select * from member";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            List<Member> members = new ArrayList<>();
            while(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                members.add(member);
            }
            return members;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public Optional<Member> findByName(String name) {
        String sql = "select * from member where name = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            }
            return Optional.empty();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    private Connection getConnection() {
        return DataSourceUtils.getConnection(dataSource);
    }
    private void close(Connection conn, PreparedStatement pstmt, ResultSet rs)
    {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (pstmt != null) {
                pstmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                close(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    private void close(Connection conn) throws SQLException {
        DataSourceUtils.releaseConnection(conn, dataSource);
    }
}
cs

 

JdbcTemplate을 이용하여 단 2줄로 구현할 수 있다.

1
2
3
4
5
    public Optional<Member> findById(Long id) {
        List<Member> result = jdbcTemplate.query("select * from member where id =?",memberRowMapper());
        //query() 메서드는 sql 파라미터로 전달받은 쿼리를 실행하고 memberRowMapper를 이용해서 ResultSet의 결과를 자바 객체로 변환한다.
        return result.stream().findAny();
    }
cs

jdbctemplate에서 쿼리 날려서 결과를 rowmapper로 매핑하고 그것을 list로 받아서 optional로 바꿔서 반환한다.

 

쿼리문을 직접 작성하지 않아도 된다.

1
2
3
4
5
6
7
8
9
10
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;
    }
cs

 

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package hello.hellospring2.repository;
 
import hello.hellospring2.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
 
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
 
public class JdbcTemplateMemberRepository implements MemberRepository{
 
    private final JdbcTemplate jdbcTemplate;
 
    @Autowired
    public JdbcTemplateMemberRepository(DataSource dataSource) {
        this.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());
        //query() 메서드는 sql 파라미터로 전달받은 쿼리를 실행하고 memberRowMapper를 이용해서 ResultSet의 결과를 자바 객체로 변환한다.
        return result.stream().findAny();
    }
 
    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = jdbcTemplate.query("select * from member where name =?",memberRowMapper());
        return result.stream().findAny();
    }
 
    @Override
    public List<Member> findAll() {
        return jdbcTemplate.query("select * from member",memberRowMapper());
    }
 
    private RowMapper<Member> memberRowMapper(){
        return new RowMapper<Member>() {
            @Override
            public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
 
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return member;
            }
        };
    }
}
 
cs

 

20번 ~ 25번 줄은 스프링에서도 권장하는 방법이다.

 

이제 SpringConfig에서 조립해주면 사용할 수 있다.

1
2
3
4
5
    @Bean
    public MemberRepository memberRepository(){
 
        return new JdbcTemplateMemberRepository(dataSource);
    }
cs

실제 웹으로 돌릴 필요없이 지난 번에 만든 통합 test에서 돌리면 된다.

그리고 오류남

 

파라미터 설정에 관한 오류이다.

findById, findByName에서 마지막에 파라미터를 각각 id, name으로 넣어주면 된다.

'Spring 강의 > section6' 카테고리의 다른 글

[23강]  (0) 2023.02.06
[22강]  (0) 2023.02.06
[20강]  (0) 2023.02.05
[19강]  (0) 2023.02.05
[18강]  (0) 2023.02.05