- 순수 Jdbc와 동일한 환경설정을 하면 된다.
- 스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해준다. 하지만 SQL은 직접 작성해야 한다.
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
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){
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) {
// return Optional.empty();
// jdbcTemplate.query("select * from member where id=?", memberRowMapper());
List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(),id);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
private RowMapper<Member> memberRowMapper(){
// return new RowMapper<Member>() { => 이걸 람다식으로 바꾼게 밑에꺼
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
이후 스프링 통합 테스트를 실행해보면 오류없이 잘 작동되는것을 알 수 있다.
추가적으로...
JdbcTemplate 사용법
1. JdbcTemplate 설정
스프링에서 JdbcTemplate을 사용하기 위해서는 먼저 jdbc 라이브러리를 프로젝트에 추가해야 한다.
Gradle을 기준으로 설명하자면 다음과 같다.
//JdbcTemplate 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
build.gradle 파일에 위와 같은 코드를 작성해주면 된다.
JdbcTemplate은 spring-jdbc에 포함되어 있다.
2. DataSource 주입
private final JdbcTemplate jdbcTemplate;
public JdbcTemplateItemRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
JdbcTemplate은 DataSource를 필요로 한다. DataSource는 스프링 빈으로 등록되어 있어야 한다.
이렇게 JdbcTemplate을 사용할 때 DataSource를 의존 관계 주입받는 방법과, JdbcTemplate을 스프링 빈으로 직접 등록하고 주입받는 방법이 있지만 전자를 관례상 많이 사용한다.
3. 쿼리 작성 및 실행
스프링 JdbcTemplate 사용 방법 공식 매뉴얼이 제공하는 몇 가지 예제를 살펴보자.
1) queryForObject()
queryForObject() 인터페이스의 정의는 다음과 같다.
<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
- sql : 실행할 sql문
- rowMapper : 조회대상
- args : 파라미터들
이러한 queryForObject() 메서드를 사용하면 단건 조회를 할 수 있다. 이때 조회 대상이 객체가 아닌 단순 데이터라면 타입을 아래와 같이 지정해줄 수 있다.
int rowCount = jdbcTemplate.queryForObject("select count(*) from t_actor",Integer.class);
preparedStatement ("?")를 이용해 파라미터 바인딩도 할 수 있다.? 에 순서대로 파라미터가 들어가게 된다.
int countOfActorsNamedJoe = jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
String을 조회할 때는 아래와 같이 사용할 수도 있다.
String lastName = jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?", String.class, 1212L);
이때 단순 데이터 타입이 아닌 객체를 조회하려면 어떻게 할까?
여기서 RowMapper를 활용해 객체를 조회할 수 있다.
RowMapper란?
RowMapper는 데이터베이스의 반환 결과인 ResultSet을 객체로 변환해주는 클래스이다.
순수 JDBC를 사용할 때는 객체를 조회할 때 다음과 같이 했다.
// 쿼리 날리기
ResultSet rs = stat.excuteQuery("SELECT * FROM Item");
// 결과값 가져오기
while(rs.next()) {
// item 객체에 값 저장
item = new Item();
item.setId(rs.getInt(1));
item.setItemName(rs.getString(2));
item.setPrice(rs.getInt(3));
// 리스트에 추가
itemList.add(item);
}
ResultSet의 결과를 개발자가 직접 꺼내 객체에 담아 저장하였다.
하지만 RowMapper를 사용하면 이러한 반복 작업을 자동화해준다.
RowMapper의 mapRow()라는 메서드는 아래와 같이 정의되어 있다.
T mapRow(ResultSet rs, int rowNum) throws SQLException;
ResultSet rs에 결괏값을 담아와 사용자가 원하는 객체에 담는다. rowNum은 반복되는 루프 중 현재 행의 번호를 나타낸다.
즉 아래와 같이 동작한다.
public Item mapRow(ResultSet rs, int rowNum) throws SQLException {
// ResultSet 값을 Item 객체에 저장
Item item = new Item();
item.setId(rs.getLong("id"));
item.setItemName(rs.getString("item_name"));
item.setPrice(rs.getInt("price"));
// item 반환
return item;
};
이것을 람다식을 이용해 더 간단하게 만들고, queryForObject() 메서드에서 객체를 조회하도록 할 수 있다.
String sql = "select id, item_name, price from item where id= ?";
Item item = jdbcTemplate.queryForObject(sql, itemRowMapper(), id);
private RowMapper<Item> itemRowMapper() {
return (rs, rowNum) -> {
Item item = new Item();
item.setId(rs.getLong("id"));
item.setItemName(rs.getString("item_name"));
item.setPrice(rs.getInt("price"));
item.setQuantity(rs.getInt("quantity"));
return item;
};
}
2) query() 메서드
query() 메서드를 사용하면 여러 건을 조회할 수 있다. 이때도 역시 RowMapper를 이용할 수 있다.
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
};
public List<Actor> findAllActors() {
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
3) update() 메서드
INSERT, UPDATE, DELETE 등 데이터를 변경하고 싶을 때는 update() 메서드를 사용할 수 있다. SQL 실행 결과에 영향받은 로우 수를 int로 반환한다.
등록
jdbcTemplate.update("insert into t_actor (first_name, last_name) values (?, ?)","Leonor", "Watling");
수정
jdbcTemplate.update("update t_actor set last_name = ? where id = ?", "Banjo", 5276L);
삭제
jdbcTemplate.update("delete from t_actor where id = ?", Long.valueOf(actorId));
4) execute() 메서드
이외에 임의의 SQL을 실행할 때는 execute() 메서드를 사용할 수 있다. 테이블을 생성하는 DDL 등에 사용할 수 있다.
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
참고
'Back End > Spring' 카테고리의 다른 글
Spring - DB접근6 (스프링 데이터 JPA) (0) | 2023.07.05 |
---|---|
Spring - DB접근5 (JPA) (0) | 2023.07.04 |
Spring - DB접근3 (스프링 통합 테스트) (0) | 2023.07.04 |
Spring - DB접근2 (고전 jdbc 이용) (0) | 2023.07.04 |
Spring - DB접근1 (h2) (0) | 2023.07.04 |