스프링 공식문서를 참조해서 JdbcTemplate 사용법을 정리해보았다.
JdbcTemplate 생성
스프링 공식문서에 따르면 JdbcTemplate을 생성하는 방법은 두가지가 있다.
1. DAO 구현체가 DataSource를 통해 JdbcTemplate를 직접 인스턴스화
2. 스프링 IoC 컨테이너에서 DAO에게 빈 참조로 제공
하지만 프로젝트의 의존성에 spring-boot-starter-jdbc 모듈을 추가한다면 스프링 부트가 DataSource와 JdbcTemplate 객체를 자동으로 IoC 컨테이너에 설정해준다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
}
그러니까 우리는 IoC 컨테이너에 들어있는 DataSource 객체와 JdbcTemplate 객체를 가져다 쓰면 된다.
복잡한 것 필요 없고 다음 두가지 형태 중 하나를 골라 JdbcTemplate을 사용할 수 있다.
첫번째 방법
@Repository
public class CarDao {
private final JdbcTemplate jdbcTemplate;
public CarDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
두번째 방법
@Repository
public class CarDao {
private final JdbcTemplate jdbcTemplate;
public CarDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
겉으로 봤을 때 두 방법의 차이는 DAO 생성자의 파라미터로 DataSource가 들어가냐, JdbcTemplate가 들어가냐이다.
첫번째 방법은 DAO 구현체 내에서 DataSource 빈을 생성자 주입 받아서 JdbcTemplate 객체를 직접 만든다. 이 방법을 사용하면 JdbcTemplate 객체의 생성과 관리에 대한 처리를 구현체 내에서 직접 해주어야 한다.
두번째 방법은 DAO 구현체가 JdbcTemplate 빈을 생성자 주입 받는다. 이 방법을 쓰면 스프링에서 JdbcTemplate 객체의 생성과 관리를 자동으로 처리해준다.
두 방법의 차이를 꼽는다면, JdbcTemplate를 직접 만들어 쓸 것이냐, 스프링 부트가 잘 만들어놓은 빈을 가져다 쓸 것이냐다.
Querying (SELECT)
조회할 때는 주로 queryForObject와 query 메서드를 사용한다.
queryForObject : 쿼리문 수행 결과가 한개일 때 사용
query : 쿼리문 수행 결과가 한개 이상일 때 사용
1. 조회된 행의 숫자를 가져온다.
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
2. 특정 조건을 만족하는 행의 숫자를 가져온다.
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
3. 조회된 값을 문자로 가져온다.
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
4. 조회된 내용을 하나의 도메인 객체로 만들어 가져온다.
Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
(resultSet, rowNum) -> {
Actor newActor = new Actor();
newActor.setFirstName(resultSet.getString("first_name"));
newActor.setLastName(resultSet.getString("last_name"));
return newActor;
},
1212L);
5. 조회된 내용들을 도메인 객체 리스트로 만들어 가져온다.
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
(resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
});
위의 4번과 5번에서, 도메인 객체로 만들 때 코드의 중복이 발생한다. 이를 제거하기 위해서 RowMapper를 사용할 수 있다.
RowMapper : 데이터베이스의 반환 결과인 ResultSet을 객체로 변환해 주는 클래스
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);
}
Updating (INSERT, UPDATE, DELETE)
업데이트를 해주는 코드들은 간단하니 한번 쭉 보면 된다.
1. INSERT
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
2. UPDATE
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
3. DELETE
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
NamedParameter
이름이 붙여진 파라미터를 사용함으로써, 쿼리문의 ? 순서와 파라미터의 순서가 일치하지 않아도 된다.
1. Map 자료구조를 이용해 파라미터에 이름을 붙여주는 방법
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
2 MapSqlParameterSource를 이용해 파라미터에 이름을 붙여주는 방법
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
3. BeanPropertySqlParameterSource를 이용해 파라미터에 이름을 붙여주는 방법(자동)
자바빈 컨벤션을 따르는 자바빈을 이용해서 자동으로 파라미터 값들을 설정해준다.
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
데이터베이스에 Insert할 때 자동생성되는 키 가져오기
원래는 JdbcTemplate을 이용해 insert하고 난 후 key 값을 가져오려면 다음과 같은 거추장스러운 코드를 작성해야했다.
@Repository
public class InsertDao {
private final JdbcTemplate jdbcTemplate;
public InsertDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Customer insert(Customer customer) {
String sql = "insert into customers (first_name, last_name) values (?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, customer.getFirstName());
ps.setString(2, customer.getLastName());
return ps;
}, keyHolder);
long id = Objects.requireNonNull(keyHolder.getKey()).longValue();
return new Customer(id, customer.getFirstName(), customer.getLastName());
}
}
이럴땐 SimpleJdbcInsert를 쓰면 코드를 간단히 할 수 있다.
@Repository
public class SimpleInsertDao {
private final SimpleJdbcInsert insertCustomer;
public SimpleInsertDao(DataSource dataSource) {
this.insertCustomer = new SimpleJdbcInsert(dataSource)
.withTableName("customers")
.usingGeneratedKeyColumns("id");
}
public Customer insert(Customer customer) {
SqlParameterSource parameters = new BeanPropertySqlParameterSource(customer);
Number id = insertCustomer.executeAndReturnKey(parameters);
return new Customer(id.longValue(), customer.getFirstName(), customer.getLastName());
}
}
잘못된 내용을 댓글로 알려주세요
'프로그래밍 > JAVA Spring' 카테고리의 다른 글
[Spring 스프링] Spring MVC - ControllerAdvice(@ControllerAdvice와 @ExceptionHandler를 이용한 예외처리) (0) | 2023.04.20 |
---|---|
[Spring 스프링] Spring MVC - Annotated Controllers(@Controller와 @RestController) (0) | 2023.04.20 |
[Spring 스프링] JDBC, JDBC 드라이버, JDBCTemplate (0) | 2023.04.14 |
[Spring 스프링] @Component, @Repository, @Service, @Controller 는 뭐가 다를까 (2) | 2023.04.12 |
[Spring 스프링] consumes와 produces의 차이 (1) | 2023.04.11 |