스프링을 사용하면 DataSource나 Connection, Statemement, ResultSet을 직접 사용하지 않고, JdbcTemplate을 이용해서 편리하게 쿼리를 실행할 수 있습니다.
JDBCTemplate 생성하기
package spring;
import java.util.Collection;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class MemberDao {
private JdbcTemplate jdbcTemplate;
public MemberDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
...
}
JdbcTemplate객체를 생성하려면 MemberDao 생성자에 Datasource 타입을 생성자에 전달하면 됩니다. 이를 위해 DataSource를 주입받도록 MemberDao 클래스의 생성자를 구현했습니다(setter주입으로도 가능).
스프링 설정에 MemberDao 빈 설정 추가
@Configuration
public class AppCtx {
@Bean(destroyMethod = "close")
public DataSource dataSource() {
DataSource ds = new DataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
ds.setUsername("spring5");
ds.setPassword("spring5");
//...
return ds;
}
@Bean
public MemberDao memberDao() {
System.out.println("memberDao() call...");
return new MemberDao(dataSource());
}
...
}
JdbcTemplate을 이용한 조회 쿼리 실행
JdbcTemplate 클래스는 SELECT 쿼리 실행을 위한 query() 메서드를 제공합니다. 자주 사용되는 쿼리 메서드는 다음과 같습니다.
* List<T> query(String sql, RowMapper<T> rowMapper)
* List<T> qurey(String sql, Object[] args, RowMapper<T> rowMapper)
* List<T> qurey(String sql, RowMapper<T> rowMapper, Object ...args)
qurey() 메서드는 sql 파라미터로 전달받은 쿼리를 실행하고 RowMapper를 이용해서 ResultSet의 결과를 자바 객체로 변환합니다.
sql 파라미터가 인덱스 기반 파라미터를 가진 쿼리이면 args 파라미터를 이용해서 각 인덱스 파라미터의 값을 지정합니다.
select * from member where email = ?
쿼리 실행 결과를 자바 객체로 변환할 때 사용하는 RowMapper 인터페이스입니다.
package org.springframework.jdbc.core;
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
RowMapper의 mapRow() 메서드는 SQL 실행 결과로 구한 ResultSet에서 한 행의 데이터를 읽어와 자바 객체로 변환하는 매퍼 기능을 구현합니다. RowMapper 인터페이스를 구현한 클래스를 작성할 수도 있지만 임의 클래스나 람다식으로 RowMapper의 객체를 생성해서 query() 메서드에 다음 코드처럼 전달할 때도 많습니다.
public class MemberDao {
private JdbcTemplate jdbcTemplate;
public MemberDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Member selectByEmail(String email) {
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").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
}, email);
return results.isEmpty() ? null : results.get(0);
}
}
위의 코드는 qurey() 메서드를 통해 쿼리를 실행합니다. 해당 쿼리는 인덱스 파라미터(?)를 포함하고 있습니다. 인덱스 파라미터에 들어갈 값은 email입니다. query() 메서드의 세 번째 파라미터는 가변 인자로 인덱스 파라미터가 두 개 이상이면 인덱스 파라미터 설정에 사용할 각 값을 콤마로 구분해 줍니다.
List<Member> results = jdbcTemplate.qurey(
"select * from MEMBER where EMAIL = ? and NAME = ?",
new RowMapper<Member>(){
...
}, email, name); //물음표 개수만큼 해당되는 값 전달
RowMapper는 ResultSet에서 데이터를 읽어와 Member 객체로 변환해 주는 기능을 제공하므로 RowMapper의 타입 파라미터로 Member를 사용했습니다.(RowMapper <Member>)
mapRow() 메서드는 파라미터로 전달받은 ResultSet에서 데이터를 읽어와 Member 객체를 생성해 리턴하도록 구현하였습니다.
람다를 사용하면 임의 클래스를 사용하는 것보다 보다 간결합니다.
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.getSLong("ID");
return member;
}, email);
동일한 RowMapper 구현을 여러 곳에서 사용한다면 RowMapper 인터페이스를 구현한 클래스를 만들어서 코드 중복을 막을 수 있습니다.
public class MemberRowMapper implements 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;
}
//MemberRowMapper 객체 생성
List<Member> results = jdbcTemplate.qurey(
"select * from MEMBER where EMAIL = ? and NAME = ?",
new MemberRowMapper(),
email, name);
}
MemberDao의 selectByEamil() 메서드는 지정한 이메일이 해당하는 MEMBER 데이터가 존재하면 해당 Member 객체를 리턴하고 그렇지 않으면 null을 리턴하도록 구현되어 있습니다.
return results.isEmpty() ? null : results.get(0); 해당 코드처럼 result가 비어 있는 경우와 그렇지 않은 경우를 구분해서 리턴 값을 처리한 것을 볼 수 있습니다.
qurey() 메서드는 쿼리를 실행한 결과가 존재하지 않으면 길이가 0인 List를 리턴하므로 List가 비어있는지 여부로 결과가 존재하지 않는지 확인할 수 있습니다.
query() 메서드를 사용하여 MemberDao의 또 다른 메서드 selectAll() 메서드를 구현해 봅시다.
public Collection<Member> selectAll() {
List<Member> results = jdbcTemplate.query("select * from MEMBER", 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;
}
});
return results;
}
Member를 위한 RowMapper 구현 클래스를 이용하도록 두 메서드를 수정하면 RowMapper 임의 클래스나 람다 식 중복을 제거할 수 있습니다.
public Member selectByEmail(String email){
List<Member> results = jdbcTempalte.qurey("select * from MEMBER where EMAIL = ?",
new MemberRowMapper(), email);
return results.isEmpty() ? null : results.get(0);
}
public Collection<Member> selectAll(){
List<Member> results = jdbcTemplate.qurey("select * from MEMBER",
new MemberRowMapper());
return results;
}
'Spring' 카테고리의 다른 글
[Spring] Spring DB - 쿼리실행 (0) | 2024.02.21 |
---|---|
[Spring] Spring JdbcTemplate - qureyForObject 메서드 (0) | 2024.02.19 |
[Spring] Spring DB 연동 - Tomcat JDBC의 프로퍼티 (0) | 2024.02.18 |
[Spring] Spring JDBC - DB 연결해보기 (1) | 2024.02.17 |
[Spring] 스프링 DB 연동 (0) | 2024.02.16 |