본문 바로가기
데이터 접근 기술/JdbcTemplate

NamedParameterJdbcTemplate 활용

by oncerun 2022. 10. 13.
반응형

 

JdbcTemplate을 사용해 파라미터를 바인딩하는 모습을 유심히 볼 필요가 있다. 

 

String sql = "update student set name=?, age =? where student_id = ?";

template.update(sql,
        name,
        age,
        studentId);

 

"?"에 들어갈 파라미터를 개발자를 순서에 맞게 세팅해야 한다. 이는 실수를 발생할 여지가 있다. 

 

실제로 쿼리에 들어갈 파라미터는 10개가 넘어 갈 수 도 있기 때문에 이는 큰 버그를 발생시킬 수 있다. 

 

이러한 버그로 인해 데이터가 데이터베이스에 잘못 들어갔다면, 코드 복구와 더불어 데이터베이스 복구해야 하기 때문에 매우 긴 시간이 투자된다. 

 

해결 방안으로 스프링은 NamedParameterJdbcTemplate을 지원한다. 

 

관련 기능은 Map,  SqlParameterSource 입니다.

  • SqlParameterSource
    - BeanPropertySqlParameterSource
    - MapSqlParameterSource
  • Map

 

Map

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);
}

 

 

BeanPropertySqlParameterSource

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);
}

 

 

이는 자바 빈 프로퍼티 규약을 통해 자동으로 파라미터 객체를 생성합니다.

getXXX -> XXX와 같은 규약을 통해 값을 꺼내 K, V를 지정하여 사용합니다.

 

하지만 파라미터의 Key값이 자바 빈에 존재하지 않는 경우에는 사용할 수 없습니다. 이 경우는 MapSqlParameterSource를 사용합니다.

 

MapSqlParameterSource

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);
 }
 
 
 // 다중 ex
 public void update(Long itemId, ItemUpdateDto updateParam) {
    String sql = "update item set item_name =:itemName, price = :price, quantity = :quantity" +
                " where id = :id";

    SqlParameterSource param = new MapSqlParameterSource()
            .addValue("itemName", updateParam.getItemName())
            .addValue("price", updateParam.getPrice())
            .addValue("quantity", updateParam.getQuantity())
            .addValue("id", itemId);

    template.update(sql, param);
}

 

NamedParameterJdbcTemplate을 적용시킬 때 변경되어야 하는 점은 기존 SQL문에서 사용했던?를 바인딩할 변수명으로 변경시키는 점과 queryForObject 등 변경을 가하는 메서드의 인자에 SqlParameterSource를 구현한 구현체를 넣어야 한다는 점입니다. 

 

 

추가적으로 RowMapper에 대해 더욱 간략히 할 수 있는 방법이 존재합니다. 

 

Resultset을 통해 객체를 생성 후 setter를 통해 값을 주입하는 방법을 대체하여 다음과 같이 사용할 수 있습니다.

private RowMapper<Item> itemRowMapper() {
    return BeanPropertyRowMapper.newInstance(Item.class);
}

 

이 경우도 자바 빈 프로퍼티 규약에 맞춘 메서드를 호출하는 것이다.

 

이 부분에 대해 네이밍 표기법에 대해 고민할 수 있다.

관계형 데이터베이스는 snake_case를 주로 사용하고 객체는 camleCase를 관례상 많이 사용하기 때문이다. 

 

이러한 관례가 많다 보니 BeanPropertyRowMapper는 언더 스코어 표기법을 자동으로 카멜 케이스로 변환해준다. 

다만 데이터베이스의 column명과 객체의 멤버 필드의 이름이 완전히 다른 경우에는 "as" 별칭을 통해 자바 빈 프로퍼티 규약에 맞춰주어야 한다. 

 

 

 

 

반응형

'데이터 접근 기술 > JdbcTemplate' 카테고리의 다른 글

JdbcTemplate 소개  (0) 2022.10.13

댓글