실무에서 가장 간단하고 실용적인 방법으로 SQL을 사용하려면 JdbcTemplate를 사용하면 된다.
기본 사용법
단건 조회(queryForObject)
int rowCount = jdbcTemplate.queryForObject("select count(*) from t_actor", 
Integer.class);
단건 조회 - 숫자 조회, 파라미터 바인딩
 int countOfActorsNamedJoe = jdbcTemplate.queryForObject(
 "select count(*) from t_actor where first_name = ?", Integer.class, 
"Joe");
단건 조회 - 문자 조회
 String lastName = jdbcTemplate.queryForObject(
 "select last_name from t_actor where id = ?",
 String.class, 1212L);
단건 조회 - 객체 조회
 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);
목록 조회 - 객체
List<Actor> actors = 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;
        });
목록 조회 - 객체
 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);
 }
INSERT
jdbcTemplate.update(
 "insert into t_actor (first_name, last_name) values (?, ?)",
 "Leonor", "Watling");
UPDATE
 jdbcTemplate.update(
 "update t_actor set last_name = ? where id = ?",
 "Banjo", 5276L);
DELETE
 jdbcTemplate.update(
 "delete from t_actor where id = ?",
 Long.valueOf(actorId));
이름 지정 파라미터[NamedParameterJdbcTemplate]
JdbcTemplate을 기본으로 사용하면 파라미터를 sql에 순서대로 바인딩한다.
String sql = "update item set item_name=?, price=?, quantity=? where id=?";
 template.update(sql,
        itemName,
        price,
        quantity,
        itemId);
파라미터가 적을때는 문제가 안되지만 파라미터가 많이 있을때는 문제가 발생할 수 있다.
이러한 문제를 해결하기 위해 Jdbc에서는 NamedParameterJdbcTemplate를 제공한다.
<JdbcTemplate 기본 사용 코드>
@Override
public Item save(Item item) {
    String sql = "insert into item (item_name, price, quantity) values (?, ?, ?)";
    KeyHolder keyHolder = new GeneratedKeyHolder();
    template.update(connection ->{
        PreparedStatement ps = connection.prepareStatement(sql,new String[]{"id"});
        ps.setString(1,item.getItemName());
        ps.setInt(2,item.getPrice());
        ps.setInt(3,item.getQuantity());
        return ps;
    },keyHolder);
    long key = keyHolder.getKey().longValue();
    item.setId(key);
    return item;
}
< NamedParameterJdbcTemplate 사용 코드(save)>
@Override
public Item save(Item item) {
    String sql = "insert into item (item_name, price, quantity) " +
            "values (:itemName, :price, :quantity)";
    SqlParameterSource param = new BeanPropertySqlParameterSource(item);
    KeyHolder keyHolder = new GeneratedKeyHolder();
    template.update(sql,param,keyHolder);
    long key = keyHolder.getKey().longValue();
    item.setId(key);
    return item;
}
< NamedParameterJdbcTemplate 사용 코드(update)>
@Override
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);
}
MapSqlParameterSource ==> Map과 유사하며 SQL타입을 지정할 수 있는 등 SQL에 특화된 기능을 제공
< NamedParameterJdbcTemplate 사용 코드(findById)>
@Override
public Optional<Item> findById(Long id) {
    String sql = "select id, item_name, price, quantity from item where id = :id";
   try {
       Map<String, Object> param = Map.of("id",id);
       Item item = template.queryForObject(sql, param, itemRowMapper()); //데이터베이스에서 조회한 결과를 Item 객체로 매핑.
       return Optional.of(item);
   }catch (EmptyResultDataAccessException e){
       return Optional.empty(); //item이 NULL일 경우
   }
}
private RowMapper<Item> itemRowMapper() { //데이터베이스에서 가져온 결과를 Item 클래스의 필드에 자동으로 매핑해준다.
        return BeanPropertyRowMapper.newInstance(Item.class);
    }
BeanPropertyRowMapper ==> ResultSet 의 결과를 받아서 자바빈 규약에 맞추어 데이터를 변환한다.
또한, BeanPropertyRowMapper는 camel 변환을 지원한다.
따라서 select item_name으로 조회해도 setItemName()에 문제없이 들어간다.
queryForObject==> 하나의 로우 조회
< NamedParameterJdbcTemplate 사용 코드(findAll)>
@Override
public List<Item> findAll(ItemSearchCond cond) {
    String itemName = cond.getItemName();
    Integer maxPrice = cond.getMaxPrice();
    SqlParameterSource param = new BeanPropertySqlParameterSource(cond);
    String sql =   "select id, item_name, price, quantity from item";
    //동적 쿼리
    if (StringUtils.hasText(itemName) || maxPrice != null) {
        sql += " where";
    }
    boolean andFlag = false;
    if (StringUtils.hasText(itemName)) {
        sql += " item_name like concat('%',itemName,'%')";
        andFlag = true;
    }
    if (maxPrice != null) {
        if (andFlag) {
            sql += " and";
        }
        sql += " price <= maxPrice";
    }
    log.info("sql={}", sql);
    return template.query(sql,param,itemRowMapper());
}
SimpleJdbcInsert
JdbcTemplate는 INSERT 쿼리를 직접 작성하지 않아도 되도록 SimpleJdbcInsert라는 기능을 제공한다.
< SimpleJdbcInsert 사용 코드>
@Slf4j
public class JdbcTemplateRepositoryV3 implements ItemRepository {
    private final NamedParameterJdbcTemplate template;
    private final SimpleJdbcInsert jdbcInsert;
    public JdbcTemplateRepositoryV3(DataSource dataSource){
        this.template = new NamedParameterJdbcTemplate(dataSource);
        this.jdbcInsert = new SimpleJdbcInsert(dataSource)
                .withTableName("item")
                .usingGeneratedKeyColumns("id");
      //  .usingColums("item_name","price","quantity"); 생략 가능
    }
    @Override
    public Item save(Item item) {
        SqlParameterSource param = new BeanPropertySqlParameterSource(item);
        Number key = jdbcInsert.executeAndReturnKey(param); // INSERT SQL을 실행
        item.setId(key.longValue());
        return item;
    }
    }
- withTableName : 데이터를 저장할 테이블 명 지정
 - usingGeneratedKeyColumns : 테이블의 기본키 칼럼 명을 지정
 
로그를 확인하면 INSERT 쿼리를 자동으로 생성하는 것을 확인할 수 있다.

JdbcTemplate의 장단점
장점
- 별도의 복잡한 설정 없이 사용 가능
 - JDBC를 직접 사용할때 발생하는 반복작업(커넥션 획득, statement 를 준비하고 실행, 커넥션 종료, statement, resultset종료, 예외발생시 스프링 예외 변환기 실행 ....)을 대신 처리해준다.
 
단점
- 동적 SQL을 해결하기 어렵다.
 
'BackEnd > Database' 카테고리의 다른 글
| MyBatis (0) | 2025.02.08 | 
|---|---|
| DB Test[@Transactional, 임베디드 모드 DB] (0) | 2025.02.07 | 
| JDBC를 이용한 반복문제 해결 - JdbcTemplate (0) | 2025.02.04 | 
| 스프링 예외 추상화 (0) | 2025.02.04 | 
| TransactionTemplate (0) | 2025.02.01 |