1.Spring JDBC 개념
Spring JDBC: JDBC의 장점과 단순성을 그대로 유지하며 기존 JDBC의 단점을 극복하고 간결한 형태의 API 사용법을 제공하여 JDBC API에서 지원하지 않는 편리한 기능을 제공한다.
- Spring JDBC는 JDBC에서 반복적으로 해야 하는 많은 작업을 대신한다.
- Spring JDBC를 사용할 때는 실행할 SQL과 바인딩 할 파라미터를 넘겨주거나, 쿼리의 실행결과를 어떤 객체에 넘겨 받을지 지정한다.
- Spring JDBC를 사용하기 위해 DB 컨넥션을 가져오는 DataSource를 Bean으로 등록한다.
2.Spring JDBC의 역할
2.1 Connection 열기와 닫기
- Connection과 관련된 모든 작업을 Spring JDBC가 필요한 시점에 알아서 진행한다.(열기)
- 진행 중에 예외가 발생했을 때도 열린 모든 Connection 객체를 닫는다.(닫기)
2.2 Statement 준비와 닫기
- SQL 정보가 담긴 Statement 또는 PreparedStatement를 생성하고 필요한 준비 작업을 Spring JDBC가 한다.
- Statement도 Connection과 마찬가지로 작업이 끝나면 Spring JDBC가 알아서 객체를 닫아준다.
*Statement와 PrepareStatement
비교항목 | Statement | PreparedStatement |
실행속도 | 질의할 떄마다 SQL 문을 컴파일 한다 | SQL 문을 미리 준비하여 컴파일해 둔다. 입력 매개변수 값만 추가하여 서버에 전송한다. 특히 여러 번 반복 하여 질의하는 경우, 실행속도가 빠르다 |
바이너리 데이터 전송 | 불가능 | 가능 |
프로그래밍 편의성 | SQL 문 안에 입력 매개변수 값이 포함되어 있어서 SQL문이 복잡하고 매개변수가 여러 개인 경우 코드 관리가 힘들다 | SQL 문과 입력 매개변수가 분리되어 있어서 코드 작성이 편리하다. |
2.3 Statement 실행
- Statement실행을 Spring JDBC가 한다.
- Statement의 실행결과는 다양한 형태로 나타난다.
2.4 ResultSet Loop 처리
- ResultSet에 담긴 쿼리 실행 결과가 한 건 이상이면 Spring JDBC는 ResultSet 루프를 만들어 반복한다.
2.5 Exception 처리와 반환
- JDBC 작업 중 발생하는 모든 예외는 Spring JDBC 예외 변환기가 처리한다.
- Checked Exception인 SQLException을 Runtime Exception인 DataAccessException 타입으로 변환한다. 즉 try~catch문을 쓰지 않아도 된다.
2.6 Transaction 처리
- transaction관련 작업(commit, rollback)은 Spring JDBC에서 모두 처리한다.
2.7 Spring JDBC 핵심클래스
- JdbcTemplate(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html)
- NamedParameterJdbcTemplate(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.html)
- SQLExceptionTranslator(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/support/SQLExceptionTranslator.html)
- RowMapper(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/RowMapper.html)
3.JdbcTemplate 클래스
3.1 JdbcTemplate 클래스
정의: 데이터 조회,삽입,수정,삭제 SQL 쿼리를 실행할 수 있는 클래스
3.3 JdbcTemplate 클래스 생성
- DataSource는 보통 Bean으로 등록해서 사용하므로 JdbcTamplate이 필요한 DAO 클래스에서 DataSource Bean을 DI 받아서 JdbcTemplate을 생성할 때 인자로 넘긴다.
3.4 JdbcTemplate 클래스의 update() 메서드
- SQL파라미터는 Object 타입 가변인자를 사용할 수 있다.
- update()는 insert, update, delete SQL문을 실행할 때 사용한다.
- update() 메서드는 레코드의 개수를 리턴한다.
3.4.1. Insert
*JDBC비교
3.4.2 Delete
3.4.3 select
*JDBC 비교
(1)RowMapper 생짜로 쓰기
①RowMapper 생성
②Select() RowMapper 사용
(2) RowMapper 람다식 활용
*JDBC 비교
3.4.3. SELECT ALL
3.5 JdbcTemplate 클래스의 queryForObject() 메서드
- SELCT SQL을 실행하여 하나의 Row를 가져올 때는 queryForObject() 메서드를 사용한다.
- SQL 실행 결과로 돌아온 여러 개의 column을 가진 한 개의 Row를 RowMapper 콜백을 이용해 DTO 객체로 매핑한다.
3.6 JdbcTemplate 클래스의 query() 메서드
- SELECT SQL을 실행하여 여러개의 Row를 가져올 때 query()를 사용한다.
- SQL 실행 결과로 돌아온 Row들을 RowMapper 콜백을 이용해 DTO객체로 매핑한다.
- 결과 값은 List 형태로 받는다. List의 각 요소가 하나의 Row에 해당한다.
3.7 query()와 queryForObject()의 매개변수
- List<T> query(String sql, RowMapper<T> rowMapper)
- List<T> query(String sql, Object[] args, RowMapper<T> rowMapper)
- List<T> query(String sql, Object[] args, int[] args Types, RowMapper<T> rowMapper)
- sql:SQL 쿼리 위치 기반 파라미터(물음표)를 이용하는 PreparedStatement용 쿼리를 사용할 수 있다.
- RowMapper : 조회 결과 ResultSet으로부터 데이터를 읽어와 객체(DTO)를 생성해주는 매퍼
- args, argTypes : args는 PreparedStatement를 실행할 때 사용할 파라미터 비인딩 목록을, argTypes는 파라미터를 바인딩 할 때 사용할 SQL 타입 목록이다. argTypes에 사용되는 값은 java.sql.Types 클래스에 정의된 값을 사용한다.
3.7 전체 사용예시
package com.example.demo.repository;
import com.example.demo.domain.Role;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.core.simple.SimpleJdbcInsertOperations;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class RoleDao {
private final JdbcTemplate jdbcTemplate;
// 생성자주입
public RoleDao(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource); // DataSource를 넣는다.
}
//INSERT
public boolean addRole(Role role) {
String sql = "INSERT INTO role(role_id, name) VALUE(?, ?))";
int result = jdbcTemplate.update(sql, role.getRoleId(), role.getName()); //update메소드는 inset, update, delete SQL문을 실행할 떄 사용한다.
return result ==1;
}
//DELETE
public boolean deleteRole(int roleId) {
String sql = "DELETE FROM role WHERE role_id = ?";
int result = jdbcTemplate.update(sql, roleId);
return result == 1;
}
//SELECT
// 한 건의 데이터르 읽어옴
public Role getRole(int roleId) {
String sql = "SELECT role_id, name FROM role WHERE role_id = ?";
// queryForObject는 1건 또는 0건을 읽어오는 메소드
// queryForObject(String sql, RowMapper<T> rowMapper(한 건의 데이터를 객체에 담아서 리턴, 매핑/바인딩), @Nullable Object... args)
try {
return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
Role role = new Role(); //DTO
role.setRoleId(rs.getInt("role_id"));
role.setName(rs.getString("name"));
return role;
}, roleId);
} catch (Exception e) {
e.printStackTrace();
}
}
//SELECT ALL
// 여러건의 데이터를 불러옴
public List<Role> getRoles() {
String sql = "SELECT role_id, name FROM role ODER BY role_id DESC";
// query메소드는 여러건의 결과를 구할 떄 사용하는 메소드
return jdbcTemplate.query(sql, (rs, rowNum) -> {
Role role = new Role();
role.setRoleId(rs.getInt("role_id"));
role.setName(rs.getString("name"));
return role;
}
);
}
}
//데이터를 한 건 읽어오는 것을 성공한 것을 가정하고, 한 건의 데이터를 Roll객체에 담아서 리턴.
//class RoleRowMapper implements RowMapper<Role> {
// @Override
// public Role mapRow(ResultSet rs, int rowNum) throws SQLException {
// Role role = new Role(); //DTO
// role.setRoleId(rs.getInt("role_id"));
// role.setName(rs.getString("name"));
// return role;
// }
//}
4.SimpleJdbcInsert
4.1. 정의
:쿼리를 사용하지 않고 데이터를 삽입할 수 있도록 해주는 클래스. Insert쿼리를 사용할 때 컬럼값이 많아지면, 복잡해진다. 쿼리문의 복잡성을 막기위해 사용한다.
4.2. 설정
①SimpleJdbcInsert의 인터페이스 SimpleJdbcInsertOperation을 변수로 지한다.
②SimpleJdbcInsert를 사용하기 위해 메서드에 dataSource를 넣어주고 insert문이 실행될 테이블을 적어준다.
4.3.SimpleInsertJdbc 활용 코드
①객체 BeanPropertySqlParameterSource는 SqlParameterSource를 구현하고 있는 객체.
②BeanPropertySqlParameterSource객체는 '()'안에 들어간 객체의 프로퍼티(변수, roleId,name)를 읽어들인다.
③변수 insertAction는 INSERT INTO role(role_id, name) VALUES(:roleId, :name)를 SimpleJdbcInsert을 통해 내부에 쿼리를 만든다. SqlParameterSource에서 뽑아낸 파라미터 값(roleId, name)을 insertAction.execute매개변수로 넣으면 '?'이 매핑됨과 동시에 SQL문이 실행된다.
*Role클래스의 프로퍼티이름과 컬럼명이 맞아야한다. ex) role_id = roleId 프로퍼티
5.NamedParameterJdbcTemplate
5.1.정의
:NamedParameterJdbcTemplate은 JdbcTemplate과 동일한 기능을 제공한다. 하지만 인덱스 기반의 파라미터로 설정하는 JdbcTemplate과 달리 NamedParameterJdbcTemplate은 이름 기반의 파라미터를 설정한다.
5.2. 설정
5.3. NamedParameterJdbcTemplate 활용코드
RowMapper의 구현체 BeanPropertyRowMapper.newInstance(Role.class)가 RowMapper의 역할을 대신한다. 즉, 기존에 사용했던 람다표현식을 대체한다.
6.SqlParameterSource
6.1. 정의
:SqlParameterSource는 파라미터 값을 설정하는 클래스.
6.2.메서드 목록
6.3.SqlParameterSource 구현클래스
- BeanPropertySqlParameterSource : 동일한 이름을 갖는 자바 객체의 프로퍼티 값을 이용해서 파라미터 값을 설정한다.
- MapSqlParameterSource : Map과 비슷하게 <이름, 값> 쌍을 이용해서 파라미터 값을 설정한다.
6.3.1. BeanPropertySqlParameterSource 코드 예시
:위 코드에 포함된 name, message, createTIme 파라미터는 각각 message 객체의 name 프로퍼티, message 프로퍼티 그리고 createTime 프로퍼티 값을 이용해 설정된다.
6.3.2. MapSqlParameterSource 코드 예시
: MapSqlParameterSource 객체를 생성한 뒤, addValue() 메서드를 이용해서 파라미터 이름과 값을 설정해주면 된다.
출처 및 참고:
-Spring Framework Basic 11강 Spring JDBC 개요 | T아카데미
-스프링 퀵 스타트(저자:채규태 / 출판사: 루비페이퍼)
-자바 웹 개발 워크북(저자:엄진영 / 출판사: 프리렉)
-즐거운 스프링 부트 03 - SQL부터 Spring Data JPA까지(https://www.youtube.com/watch?v=MY3t6VOxsuU&ab_channel=%EB%B6%80%EB%B6%80%EA%B0%9C%EB%B0%9C%EB%8B%A8-%EC%A6%90%EA%B2%81%EA%B2%8C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EB%B0%B0%EC%9A%B0%EA%B8%B0)