1. JDBC, JDBC 템플릿이란. ?
JDBC는 데이터베이스의 종류에 상관없이 API를 이용해서 처리할 수 있게 도와주는 것이다.
하지만 JDBC API를 사용하려면 DB 연동을 위해 Connection 객체를 생성해주어야 하며, 쿼리 작성 후 쿼리 실행을 위한PreparedStatement 객체를 생성해주어야 했다. 또한 쿼리를 실행한 이후에는 ResultSet을 이용하여 결과를 처리하고 close() 메서드로 닫아주는 과정이 필요하다.
List<Review> list = new ArrayList<>();
String sql = "SELECT * from REVIEW";
Statement stat = conn.createStatement();
ResultSet rs = stat.excuteQuery(sql);
while(rs.next()) {
review = new Review();
review.setId(rs.getInt(1));
review.setName(rs.getString(2));
review.setDescription(rs.getString(3));
list.add(review);
}
rs.close();
stat.close();
conn.close();
이러한 코드들은 데이터 처리와는 관계 없는 코드지만 JDBC 프로그래밍을 할 때 반복적으로 사용되므로 이러한 중복을 제거하기 위해 스프링은 JdbcTemplate 클래스를 제공한다고 한다.
2. JDBC 템플릿 생성
JDBC 템플릿을 생성하기 위해서는 DataSource를 JDBC 생성자에 전달하면 된다.
@Repository
public class Dao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource){
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
...
}
2. query()
위의 사진은 리뷰를 별점별로 가져오는 메서드였다. (내가 짰던 코드 중 query()를 사용한 메서드를 임의로 캡쳐해왔다.)
return this.jdbcTemplate.query(getReviewsByStarQuery,
(rs, rowNum) -> new GetReviewRes(
rs.getInt("reviewIdx"),
rs.getString("userName"),
rs.getString("profileImage"),
// ... 생략
params);
}
query() 메서드는 sql 파라미터로 전달받은 쿼리(=getReviewsByStarQuery)를 실행하고 RowMapper를 이용해서 ResultSet의 결과를 자바 객체로 변환한다.
query() 메서드의 3번째 매개변수인 @Nullable Object .. args의 경우 쿼리의 조건문에 필요한 값들을 위해 넣어주어야 했다. where productIdx = ? and reviewIdx =? 라고 쿼리를 작성한 경우 Object[] params = new Object[] {productIdx, reviewIdx}; 이런 식으로 전달해주면 된다.
쿼리 실행 결과를 자바 객체로 변환할 때 사용하는 RowMapper 인터페이스 안에는 mapRow()가 존재하는데, 이 메서드는 SQL 실행 결과로 구한 ResultSet에서 한 행의 데이터를 읽어와 자바 객체로 변환하는 매퍼 기능을 구현한다.
T mapRow(ResultSet rs, int count);
mapRow()는 ResultSet에 값을 담아와서 객체에 저장하고 그것을 count만큼 반복하게 된다.
여기서 잠깐! 우리가 순수 JDBC를 사용할 때는 반환 타입으로 데이터 형만 (Long, Integer, String 등) 가능했었다.
Object jdbcTemplate.queryForObject(SQL구문, 반환 타입, 인자);
하지만 ,,, 우리는 User, Review, Product 등등 원하는 형태의 객체 자체에 대한 값을 받고 싶은 경우가 대부분이다.
그래서 아래와 같이 ResultSet으로 먼저 값을 받은 다음 객체에 담아 반환했었기 때문에 결과값을 고민없이 받을 수 있었다.
ResultSet rs = stat.excuteQuery("SELECT * FROM USER");
while(rs.next()) {
user = new User();
user.setId(rs.getInt(1));
user.setName(rs.getString(2));
user.setDescription(rs.getString(3));
userList.add(user);
}
하지만 RowMapper를 사용하게 되면 SQL 실행 결과로 구한 ResultSet에서 한 행의 데이터를 읽어와 사용자가 원하는 자바 객체로 변환할 수 있게 된다.
2. queryForObject()
queryForObject()의 경우 query()와 달리 쿼리 결과가 1개일 때 사용한다.
아래 사진을 잘보면 query() 와 다르게 반환값이 List<>가 아니다!
위의 코드는 파라미터로 전달될 리뷰 식별자로 리뷰 단건을 조회하는 메서드이다.
@Override
@Nullable
public <T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper)
throws DataAccessException {
List<T> results = query(sql, args, argTypes, new RowMapperResultSetExtractor<>(rowMapper, 1));
return DataAccessUtils.nullableSingleResult(results);
}
query 와동일하게 쿼리문, rowMapper, 파라미터를 전달하면 된다.
다른 예시를 하나 더 보자!
위의 코드는 해당 userIdx를 가지는 유저가 존재하는 지를 확인하는 쿼리이다. (당연히 반환 값은 한개)
@Override
public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException {
return queryForObject(sql, args, getSingleColumnRowMapper(requiredType));
}
queryForObject()는 query와 동일하게 sql 구문과 쿼리의 조건문을 위해 필요한 값을 넣어주면 된다. 다만 두번째 매개변수에는 반환할 값의 타입을 지정하면 된다. (int 로 반환하고 싶다면 int.class를 넣어주면 된다.)
3. update()
update() 메서드의 경우 INSERT, UPDATE, DELETE 쿼리일 때 사용한다.
update() 메서드는 쿼리 실행 결과로 변경된 행의 개수를 리턴하게 된다. 나의 경우 MySQL을 사용하였는데, MySql의 LAST_INSERT_ID()라는 함수를 사용해서 가장 최근에 성공적으로 수행된 INSERT 구문의 첫번째 AUTO_INCREMENT column의 값을 반환하게 하여 이를 리턴하게 해주었다
참고자료
'Spring' 카테고리의 다른 글
Mockito 공식문서 읽고 정리하기 (1) (0) | 2023.04.07 |
---|---|
[Spring Boot] 소셜로그인 구현하기 - 카카오편 (카카오, 구글, JWT ...) (0) | 2022.12.09 |
댓글