오늘은 Spring 초창기부터 있었던 JdbcTemplate대해서 간단히 알아보도록 하자.
JdbcTemplate은 엄청나게 많은 메서드들을 가지고 있다. 하나씩 다 살펴볼 수는 없지만 주로 사용하는 것들 위주로 살펴보도록 하자.

필자는 대부분 Data Access를 할 경우에는 JPA를 이용하고 어쩌다 mybatisJdbcTemplate을 이용한다. 그래도 굳이 따지자면 JdbcTemplate을 더 많이 사용하고 있다. 그래도 자주 사용하지 않다보니 잘 기억이 안난다. 사용할 때만 구글링을 해서 찾아봐서 자주 사용하는 것들을 포스팅을 해보자.

execute

execute 메서드는 매우 간단하다. 주로 DDL을 실행 시킬때 사용한다. 기본적은 메서드는 딱히 리턴타입도 없다.

void execute(String sql) throws DataAccessException;

물론 이외에도 몇가지 메서드가 있는데 자주 사용하지 않을 듯하다.
한번 사용해보자.

@Service
public class DdlJdbc {

  private final JdbcTemplate jdbcTemplate;

  public DdlJdbc(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
  }

  public void execute (String sql) {
    this.jdbcTemplate.execute(sql);
  }
}

ddlJdbc.execute("drop table persons if exists");
ddlJdbc.execute("create table persons("id serial, name varchar(255))");

딱히 어려운부분은 없다. 기본적은 데이터베이스의 DDL 문법이다. 솔직히 이것도 그렇게 많이 쓰이지 않을 듯 싶다.

update

이번엔 update 메서드이다. Read를 제외한 나머지 insert, update, delete 는 모두 이 메서드를 사용하면 된다. 필자는 맨처음엔 update 메서드밖에 없는 줄 알았다.

주로 사용하는 메서드는 아래와 같다.

int update(String sql, @Nullable Object... args) throws DataAccessException;

sql과 그에 따른 파라미터들이다. 물론 이것도 보다 많은 메서드들이 있으니 참고 하면 되겠다.

@Service
public class UpdateJdbc {

  private final JdbcTemplate jdbcTemplate;

  public UpdateJdbc(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
  }

  public int insert(String name) {
    return jdbcTemplate.update("insert into persons (name) values (?)", name);
  }
}

위와 같이 insert문 과 그에 따른 name을 파라미터로 넘기면 끝난다.

updateJdbc.insert("wonwoo");
updateJdbc.insert("kevin");

아주 간단하다. 또한 batchUpdate 또한 가능하다. 주로 사용될 만한 메서드는 아래와 같다.

int[] batchUpdate(String sql, List<Object[]> batchArgs) throws DataAccessException;

여러개를 넣기 때문에 파라미터가 List<Object[]> 형태로 되어있다.

public void batchUpdate(List<String> name) {
  List<Object[]> ts = name.stream().map(i -> new Object[]{i}).collect(Collectors.toList());
  this.jdbcTemplate.batchUpdate("insert into persons (name) values (?)", ts);
}

updateJdbc.batchUpdate(Arrays.asList("hello", "world"));

아주 간단하다. 각자가 원하는 메서드를 사용하면 되겠다. 여기까지는 너무 쉽다. 뭐든 Read가 문제다. read 메서드들은 너무 많다. 한번 살펴보도록 하자.

queryForObject

queryForObject는 하나의 도메인 객체를 리턴받거나 하나의 컬럼을 리턴 받을 때 사용한다. 필자가 조금 헷갈린게 있는데 마치 도메인으로 바로 바꿔줄 것 같지만 그렇지 않다.

<T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException;

<T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;

자주 사용될만한 메서드는 위와 같다. 한번 사용해보자.

@Service
public class SelectJdbc {

  private final JdbcTemplate jdbcTemplate;

  public SelectJdbc(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
  }

  public Long findId(Long id) {
    return this.jdbcTemplate.queryForObject("SELECT id FROM persons where id = ?", Long.class, id);
  }

  public Person findOne(Long id) {
    return this.jdbcTemplate.queryForObject("SELECT id, name FROM persons where id = ?",
        (rs, rowNum) -> new Person(rs.getLong("id"), rs.getString("name")),
        id);
  }
}

위와 같이 하나의 컬럼을 갖고 오거나 하나의 도메인으로 가지고 올때 유용하다. 하지만 도메인으로 변경을 할때는 위와 같이 콜백인터페이스를 사용해서 변환해야 한다.

this.jdbcTemplate.queryForObject("SELECT id FROM persons where id = ?", Person.class, id);

위 같이는 작동하지 않는다.

또한 예전에는 queryForLong, queryForInt 메서드가 존재했으니 최신 버전에는 삭제되었다. 보통 카운트를 가져올떄 자주 이용했으나 이제는 queryForObject 메서드를 이용해서 가져와야 한다.

public int count() {
  return this.jdbcTemplate.queryForObject("SELECT count(id) FROM persons", int.class);
}

Spring 4.2부터 삭제된 것으로 보인다. 왜냐하면 4.2 이전 문서에만 queryForInt 가 있고 4.2 버전에서는 문서에서 삭제 되었으니 아마 그때 삭제 되었을 것이라 판단된다.

queryForList, queryForMap

queryForList, queryForMap 메서드 또한 queryForObject 과 동일하다. 단지 List냐 Map냐의 차이 일뿐이지 하는 행위는 동일하다.

단지 컬럼 하나를 리스트로 가져오거나 컬럼 하나를 Map으로 가져오는 메서드이다. 이 중에서도 그렇게 많이 쓰일 것 같지는 않다.

자주 사용될 만한 메서드는 아래와 같다.

<T> List<T> queryForList(String sql, Class<T> elementType) throws DataAccessException;
<T> List<T> queryForList(String sql, Object[] args, Class<T> elementType) throws DataAccessException;

Map<String, Object> queryForMap(String sql, @Nullable Object... args) throws DataAccessException;

한번 사용해보도록 하자.

  public List<Long> findAllId() {
    return this.jdbcTemplate.queryForList("SELECT id FROM persons", Long.class);
  }

  public Map<String, Object> findAllMap(Long id) {
    return this.jdbcTemplate.queryForMap("SELECT id FROM persons where id = ?", id);
  }

여기서 주의할점은 queryForList에도 마치 도메인으로 바꿀 수 있을 것처럼 보이지만 그럴수 없다. queryForObject처럼 콜백으로 받을 수 있는 파라미터가 없다. 만약 그러고 싶다면 아래 설명할 query 메서드를 이용해야 한다. 굳이 없어도 될 것 같다.

query

아마 가장 많은 메서드를 갖고 있고 위의 설명한 것을 제외하면 모두 이 메서드를 이용하면 된다. 이것 역시 메서드들이 너무 많으니 이 역시 자주 이용될 만한 것들만 살펴보자.

자주 사용될만한 메서드들로는 아래와 같다.

<T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException;
<T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;

RowMapper<T> 인터페이스를 해당 도메인에 맞게 구현하면 된다.

  public List<Person> findAll() {
    return this.jdbcTemplate.query("SELECT id, name FROM persons",
        (rs, rowNum) -> new Person(rs.getLong("id"), rs.getString("name")));
  }

딱히 어려운부분은 없는 것같다. 그리고 다양한 메서드들이 있으니 참고하면 되겠다. 필자의 경우에는 특별한 경우가 아니라면 그 외의 메서드들은 사용한적이 없다. 하지만 또 언젠가는 사용할 일이 있을테니 있다는 것만 알자.

참고로 설명한 query* 메서드들은 모두 (…) 가변인자로 설명했으니 Object[]로 받는 파라미터들도 존재한다.

예를들어 다음과 같다.

<T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException;

아마도 1.5 이전에는 가변인지가 없어서 위와 같이 사용했을 것으로 판단된다.

오늘은 이렇게 Spring에서 제공해주는 JdbcTemplate 에 대해서 알아봤다. 엄청 많은 메서드들이 존재하니 사용할 일이 있다면 문서를 좀 더 참고하면 되겠다.

그럼 오늘은 이만!