[스프링 DB 2편] - 스프링 JdbcTemplate

Chooooo·2023년 1월 28일
0

스프링 DB 2편

목록 보기
3/8
post-thumbnail

이 글은 강의 : 김영한님의 - "[스프링 DB 2편 - 데이터 접근 활용 기술]"을 듣고 정리한 내용입니다. 😁😁


😀 JdbcTemplate 소개와 설정

🎈 SQL을 직접 사용하는 경우에 스프링이 제공하는 JdbcTemplate은 아주 좋은 선택지다. JdbcTemplate 은 JDBC를 매우 편리하게 사용할 수 있게 도와준다.

JDBC 장점

🎈 설정이 간편하다.

  • 별도의 복잡한 설정 없이 바로 사용할 수 있다.

🎈 반복문제 해결

  • JdbcTemplate은 템플릿 콜백 패턴을 사용해서, JDBC를 직접 사용할 때 발생하는 대부분의 반복 작업을 처리해준다.

  • 개발자는 SQL을 작성하고, 전달할 파라미터 정의 + 응답 값 매핑만 하면 된다.

  • 아래 반복 작업을 대신 해준다.

    커넥션 획득
    statement를 준비하고 실행
    결과를 반복하도록 루프를 실행
    Connetion, Statement, ResultSet 종료
    트랜잭션을 위한 Connection 동기화
    예외 발생 시, 스프링 예외 변환기 실행

JDBC 단점

🎈 동적 SQL을 해결하기 어렵다.

🎈 동적 SQL은 MyBatis / Query DSL을 이용해서 처리할 수 있다.

JDBC 설정

implementation 'org.springframework.boot:spring-boot-starter-jdbc'

  • 사용하기 위해서는 위 코드를 build.gradle에 추가하면 된다. 자동으로 spring-jdbc가 라이브러리에 포함된다.

build.gradle

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
//JdbcTemplate 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
//H2 데이터베이스 추가
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//테스트에서 lombok 사용
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
}

JDBC 주요 쿼리

🎈 query() : 실행 결과가 List 형태로 반환됨. QueryDSL의 Fetch와 동일
🎈 queryForObject : 실행 결과는 하나의 객체만 반환됨. QueryDSL의 FetchOne과 동일

JDBC 적용 - JdbcTemplateRepositoryV1 개발

🧨 ItemRepository 인터페이스를 구현한 JdbcTemplateRepositoryV1를 개발한다.

  • ItemRepository 인터페이스가 있으니 이 인터페이스를 기반으로 JdbcTemplate을 사용하는 새로운 구현체를 개발하자.

JdbcTemplate 필드 변수 선언 (DataSource 필요)

private final JdbcTemplate template;

// JdbcTemplate은 Connection이 필요하기 때문에 DataSource 주입이 필요함.
public JdbcTemplateItemRepositoryV1(DataSource dataSource) {
    this.template = new JdbcTemplate(dataSource);
}

🎃 기본

  • JdbcTemplateItemRepositoryV1 은 ItemRepository 인터페이스를 구현했다.
  • this.template = new JdbcTemplate(dataSource)
  • JdbcTemplate 은 데이터소스(dataSource)가 필요하다.
  • JdbcTemplateItemRepositoryV1() 생성자를 보면 dataSource 를 의존 관계 주입 받고 생성자 내부에서 JdbcTemplate 을 생성한다. 스프링에서는 JdbcTemplate 을 사용할 때 관례상 이 방법을 많이 사용한다.
  • 물론 JdbcTemplate 을 스프링 빈으로 직접 등록하고 주입받아도 된다.

🎃 JdbcTemplate을 한번만 생성해서 주입 받은 후, 사용한다.

  • DataSource를 클래스에서 주입 받아서 JdbcTemplate을 생성해서 사용
  • Config 클래스에서 JdbcTemplate Bean 생성 후, 주입 받아서 사용

JdbcTemplate을 이용한 save() 개발

@Override
public Item save(Item item) {

    String sql = "insert into item(item_name, price, quantity) values (?,?,?)";

    // DB에서 생성해준 ID값을 가져오는 방법
    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);

    // DB에 들어간 Key 값을 KeyHolder가 가지고 있고, 이 값을 KeyHolder가 Return 해준다.
    long key = keyHolder.getKey().longValue();
    item.setId(key);
    return item;
}

🎃 현재 PK값 관리 전략은 Identity - 개발자가 ID값을 직접 지정하는 것이 아니라 비워두고 저장해야한다. 그러면 데이터베이스가 PK인 ID를 대신 생성해준다.

  • DB에 데이터가 저장되는 순간, DB에서 PK값을 설정해준다. 따라서 Query에는 Id값을 넣지 않는다.
  • KeyHolder는 DB에서 생성된 ID값을 가지고 있는다.

🎃 Identity 전략을 사용할 때는 위 형식으로 JdbcTemplate을 사용한다.

  • 익명함수로 Connection을 처리
  • PreparedStatemnet에 sql과 "id"값을 넘겨준 후, 파라미터 바인딩 처리한다.

🎈 위의 형식으로 처리하게 되면 KeyHolder는 생성된 PK값을 가지고 있게 된다. 따라서 생성된 PK 값을 기존 객체에다가 처리해주면, DB와 Application 상의 상태가 동기화 된다.

JdbcTemplate을 이용한 update() 개발

@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
    String sql = "update item set item_name=?, price=?, quantity=? where id=?";
    template.update(sql,
            updateParam.getItemName(),
            updateParam.getPrice(),
            updateParam.getQuantity(),
            itemId);
}

🎃 update는 update()를 이용해서 처리함.

  • Parameter Binding을 나열하는 방식으로 처리할 수 있음.
  • 물론, PreparedSteatement를 직접 만들어서 처리할 수도 있음.
template.update(sql, ps -> {
    ps.setString(1,updateParam.getItemName());
    ps.setInt(2, updateParam.getPrice());
    ps.setInt(3, updateParam.getQuantity());
});

JdbcTemplate을 이용한 findById() 개발

@Override
public Optional<Item> findById(Long id) {
    String sql = "select id, item_name, price, quantity from item where id = ?";
    try {
        Item item = template.queryForObject(sql, itemRowMapper(), id);
        return Optional.of(item);
    } catch (EmptyResultDataAccessException e) {
        return Optional.empty(); 
    }
}

🎃 QueryForObject()

  • 검색 결과를 하나의 객체로 바인딩해서 가져온다.
  • 검색 결과가 없는 경우 EmptyResultDataAcessException이 발생함.

🎃 QueryForObject를 사용하는 경우, 위의 Exception을 고려해서 Try ~ Catch 문을 사용해야한다.

🎃 QueryForObject 사용 시에는 객체로 Mapping 해줄 RowMapper가 필요

private RowMapper<Item> itemRowMapper() {
    return (rs, rowNum) -> {
        Item item = new Item();
        item.setId(rs.getLong("id"));
        item.setItemName(rs.getString("item_name"));
        item.setPrice(rs.getInt("price"));
        item.setQuantity(rs.getInt("quantity"));
        return item;
    };
}

🎃 QueryForObject에서 사용하는 RowMapper를 생성함.

  • Query 결과로 받아온 ResultSet + RowNum을 바탕으로 객체를 만들어주고, Return 해주는 역할을 한다.

  • 순수 Jdbc를 사용할 경우, ResultSet의 Cursor를 직접 옮겼어야 했는데, RowMapper는 이 Loop 부분을 해결해준다.

JdbcTemplate을 이용한 FindAll() 개발

@Override
public List<Item> findAll(ItemSearchCond cond) {
    String itemName = cond.getItemName();
    Integer maxPrice = cond.getMaxPrice();

    String sql = "select id, item_name, price, quantity from item";

    // 동적 쿼리ㅏ
    if (StringUtils.hasText(itemName) || maxPrice != null) {
        sql += " where";
    }

    boolean andFlag = false;
    List<Object> param = new ArrayList<>();
    if (StringUtils.hasText(itemName)) {
        sql += " item_name like concat('%',?,'%')";
        param.add(itemName);
        andFlag = true;
    }

    if (maxPrice != null) {
        if (andFlag) {
            sql += " and";
        }
        sql += " price <= ?";
        param.add(maxPrice);
    }

    log.info("sql = {}", sql);
    // Query는 List 가져올 때 사용. QueryForObject는 전체를 가져올 때
    return template.query(sql, itemRowMapper());
}

🎈 FindAll() 메서드는 ItemSearchCond가 전달됨.

  • ItemSearchCond는 검색 조건을 담은 DTO
  • 검색 조건이 모두 null일 경우, 실제 SQL에서는 모든 데이터가 전체 검색되기 때문에 데이터 유/무에 따라서 동적 쿼리를 작성해야한다.

🎈 JdbcTemplate을 사용할 경우, String 기반으로 쿼리를 작성한다. 따라서 위와 같이 복잡한 형태로 동적 쿼리를 작성해야함.

JDBC 파라미터 바인딩 - JdbcTempalteItemRepositoryV2

  • 동적쿼리 문제
    🎃 개발을 할 때는 코드를 몇줄 줄이는 편리함도 중요하지만, 모호함을 제거해서 코드를 명확하게 만드는 것이 유지보수 관점에서 매우 중요하다.
  • JdbcTemplate을 기본으로 사용하면 파라미터는 순서대로 바인딩된다.

JdbcTemplate은 String으로 파라미터 바인딩이 되기 때문에 협업하는 입장에서 누군가 파라미터의 순서를 바꾸게 될 경우, 잘못된 파라미터끼리 바인딩 될 수 있다. 그리고 그 상황을 개발자들이 인지하지 못할 수 있다. 위처럼 파라미터를 순서대로 바인딩 하는 것은 편리하기는 하지만 순서가 맞지 않아서 버그가 발생할 수 있으므로 주의해서 사용해야 한다.

🎃 위의 SQL을 실행하게 되면 다음과 같이 파라미터 바인딩이 진행된다. 가격와 양이 서로 다른 값에 매칭되는 것이다.(심각한 문제) 이런 문제는 DB까지 다시 복원을 해야되기 때문에 매우 중대한 타격을 준다. 따라서 이런 부분을 해결하기 위해 NamedParameterJdbcTemplate을 사용한다.

  • 버그 중에서 가장 고치기 힘든 버그는 데이터베이스에 데이터가 잘못 들어가는 버그다. 이것은 코드만 고치는 수준이 아니라 데이터베이스의 데이터를 복구해야 하기 때문에 버그를 해결하는데 들어가는 리소스가 어마어마하다.

이름 지정 파라미터 1

JdbcTemplate은 이런 문제를 보완하기 위해 NamedParameterJdbcTemplate 라는 이름을 지정해서 파라미터를 바인딩 하는 기능을 제공한다.

NamedParameterJdbcTemplate

private final NamedParameterJdbcTemplate template;

public JdbcTemplateItemRepositoryV2(DataSource dataSource) {
    this.template = new NamedParameterJdbcTemplate(dataSource);
}

🎃 앞서 발생한 파라미터 바인딩 문제를 해결하기 위해 NamedParameterJdbcTemplate을 사용한다.
🎃 NamedParamaterJdbcTemplate은 JdbcTemplate과 동일한 형태로 생성해서 사용한다. 내부에 dataSource 필요.

NamedParameterJdbcTemplate의 사용방법

// JdbcTemplate
String sql = "insert into item(item_name, price, quantity) values (?,?,?)";
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);

// NamedParameterJdbcTemplate
String sql = "insert into item(item_name, price, quantity) values (:itemName,:price,:quantity)";
BeanPropertySqlParameterSource param = new BeanPropertySqlParameterSource(item);
template.update(sql, param, keyHolder);
  • JdbcTemplate은 Parameter 바인딩을 위해 "?"를 사용하고, PreparedStatement에 값을 직접 셋팅했었다.
  • NamedParameterJdbcTemplate은 PreparedStatemnet를 사용하는 것 대신, Parameter Map을 넘겨주는 방식으로 처리가 된다.
  • 이 때 Parameter Map에는 객체의 Parameter와 DB에 들어갈 값을 Binding 하는데, 이 때 String 기반에서 많이 벗어나게 된다.

Save() 메서드 : BeanPropertySqlParameterSource

@Override
public Item save(Item item) {

    String sql = "insert into item(item_name, price, quantity) " +
            "values (:itemName,:price,:quantity)";

    BeanPropertySqlParameterSource param = new BeanPropertySqlParameterSource(item);
    KeyHolder keyHolder = new GeneratedKeyHolder();
    template.update(sql, param, keyHolder);

    long key = keyHolder.getKey().longValue();
    item.setId(key);
    return item;
}
  • NamedParameterJdbcTemplate을 사용하기 때문에 "?"가 아닌 ":parameter" 형식으로 SQL을 작성해야 해.
  • NamedParameterJdbcTemplate을 사용할 때는 반드시 Parameter Map을 넘겨줘야한다.
  • BeanPropertySqlParameterSource에 DB에 넣을 객체(item)을 전달해주면, item은 "필드 명 : 값" 형식으로 Parameter Map에 저장된다.

Update() 메서드 : MapSqlParameterSource로 구현

@Override
public void update(Long itemId, ItemUpdateDto updateParam) {

    String sql = "update item set item_name=:itemName, price=:price, quantity=:quantity where id=:id";

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

    template.update(sql, param);
}

🎈 MapSqlParameterSource를 사용할 수 있다.
🎈 MapSqlParameterSource에는 값을 하나씩 "필드명 / 값" 형식으로 추가해서 만들고, SQL 실행 시 전달할 수 있다.

FindById() : Map으로 구현

@Override
public Optional<Item> findById(Long id) {
    String sql = "select id, item_name, price, quantity from item where id = :id";

    try {
        Map<String, Long> param = Map.of("id", id);
        Item item = template.queryForObject(sql, param,itemRowMapper());
        return Optional.of(item);
    } catch (EmptyResultDataAccessException e) {
        return Optional.empty(); // 이 문제가 발생했다는 것은 데이터가 없다는 것이기 때문에 Empty를 반환해준다.
    }
}

🎈 앞선 클래스를 사용하지 않더라도, 직접 Map에 값을 넣어서 SQL 실행 시 전달하는 방법도 있다.

FindAll() : BeanPropertySqlParameterSource

@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());
}
  • 동적 쿼리에도 Parameter Map을 전달할 수 있음.
    - SQL 작성 시, 내부적으로 "?" 대신 ":parameterName"으로 바꿔주기만 하면 됨.
  • SQL 실행할 때, parameter Map을 전달해줘야함.

RowMapper 생성

private RowMapper<Item> itemRowMapper() {
    // Spring이 제공하는 RowMapper를 이용해서 넣어주면 됨.
    return BeanPropertyRowMapper.newInstance(Item.class); // Camel 변환 지원함.
}
  • 기존에는 RowMapper 인터페이스를 익명 클래스로 직접 구현해서 사용했었음.
  • BeanPropertyRowmapper.newInstance()에 맵핑해야할 클래스를 넘겨주면, 자동으로 Mapper가 생성됨.
  • 예를 들어 DB에서 조회한 결과가 'select id, price'라고 한다면 다음 코드를 작성해준다. (실제로는 리플렉션 같은 기능 사용)
Item item = new Item();
item.setId(rs.getLong("id"));
item.setPrice(rs.getInt("price"));
  • DB에서 조회한 결과 이름을 기반으로 setId(), setPrice()처럼 자바 빈 프로퍼티 규약에 맞춘 메서드를 호출함.

RowMapper의 문제점 → 별칭(as) 사용

RowMapper는 자바 빈 프로퍼티 규약으로 값을 Mapping 해준다.

  • 객체에는 itemName, DB에는 item_name으로 저장됨
  • 객체에는 userName, DB에는 member_name으로 저장됨

그런데 위와 같은 두 가지 문제가 존재할 수 있다. 첫번째 경우처럼 itemName(카멜 표기법)과 item_name(스네이크 표기법)의 차이는 rowMapper가 자동으로 처리를 해준다. 그렇지만 두번째 경우는 자동으로 처리해주지 않는다.

select member_name as userName

🎃 이럴 때는 별칭(as)를 사용해서 SQL 조회 결과의 이름을 변경하는 방법이 있다. 주로 데이터베이스 컬럼 이름과 객체 이름이 완전히 다를 때 문제를 해결할 수 있다. 예를 들어 member_name으로 DB에 저장된 값을 username에 저장할 수 있다.

JdbcTemplate - 이름 지정 파라미터2

🎈 key : ":파라미터 이름"으로 지정한 파라미터 이름
🎈 value : 해당 파라미터의 값

  • 파라미터를 전달하려면 Map 형식의 Key/Value 데이터 구조를 만들어서 전달해야한다. key는 파라미터 이름이고, value는 해당 파라미터의 값이 된다.

이름 지정 바인딩에서 자주 사용하는 파라미터의 종류는 크게 3가지가 있다.
🎈 Map
🎈 SqlParameterSource

  • MapSqlParameterSource
  • BeanPropertySqlParameterSource

Map

Map<String, Long> param = Map.of("id", id);
Item item = template.queryForObject(sql, param,itemRowMapper());
return Optional.of(item);
  • 단순히 Map을 사용하면 된다.

MapSqlParmeterSource

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

template.update(sql, param);
  • SQL 타입을 지정할 수 있는 등 SQL에 특화된 기능을 제공한다.
  • MapSqlParameterSource는 메서드 체인을 통해 편리한 사용법도 제공한다.

BeanPropertySqlParameterSource (Best)

@Override
public List<Item> findAll(ItemSearchCond cond) {
    ...
    
    SqlParameterSource param = new BeanPropertySqlParameterSource(cond);
    
    ...
    
}

🎈 BeanPropertySqlParameterSource는 객체를 넘겨주면 자동으로 객체의 클래스 정보를 바탕으로 필요한 형태의 값을 만들어 줌.
🎈 자바 빈 프로퍼티 규약을 통해서 자동으로 파라미터 객체를 생성한다. (getItemName() → itemName)

  • 예를 들어서 getItemName()이 있으면, key는 "itemName"이 된다.
String sql = "update item set item_name=:itemName, price=:price, quantity=:quantity where id=:id";

//NamedParameterJdbcTemplate을 사용하면서 다음과 같이 해결함.
MapSqlParameterSource param = new MapSqlParameterSource()
        .addValue("itemName", updateParam.getItemName())
        .addValue("price", updateParam.getPrice())
        .addValue("quantity", updateParam.getQuantity())
        .addValue("id", itemId);

🎈 BeanPropertySqlParameterSource를 항상 사용할 수 있는 것은 아님.

  • update()에서는 SQL에 :id를 바인딩 해야하는데, update()에서 사용하는 ItemUpdateDto에는 itemId가 없다. 따라서 BeanPropertySqlParameterSource를 사용할 수 없음.

  • 이럴 때는 MapSqlParameterSource를 사용해야함.

JdbcTemplate - SimpleJdbcInsert (JdbcTemplateRepositoryV3)

JdbcTemplate은 INSERT SQL을 직접 작성하지 않아도 되도록 'SimpleJdbcInsert'라는 기능을 제공한다.

  • 옵션이긴 한데 편리해 ! 그냥 Insert에 있어서만 도움 받는거야 !!

준비

public JdbcTemplateItemRepositoryV3(DataSource dataSource) {
    this.template = new NamedParameterJdbcTemplate(dataSource);

    // Item 테이블을 안다 → DB에서 메타 데이터 읽어서 자동 파싱을 한다.
    this.jdbcInsert = new SimpleJdbcInsert(dataSource)
            .withTableName("item")
            .usingGeneratedKeyColumns("id") // 자동으로 키값 생성되는 것
            .usingColumns("item_name", "price", "quantity"); // 이 부분 생략 가능
}

🎈 생성해서 클래스가 가지고 있어야 함.
🎈 의존관계 주입은 DataSource가 필요함.
🎈 문법

  • withTableName : 데이터를 저장할 테이블 명을 지정함.
  • usingGeneratedKeyColumns : Key를 생성하는 PK 컬럼 명을 지정한다.
  • usingColumns : INSERT SQL에 사용할 컬럼을 지정함. 특정 값만 저장하고 싶을 때 사용한다. 생략 가능함

🎃 SimpleJdbcInsert는 생성 시점에 DB 테이블의 메타 데이터를 조회한다. 따라서 어떤 컬럼이 있는지 확인할 수 있음으로 usingColumns를 생략할 수 있다. 특정 컬럼만 지정하고 싶을 때 사용하면 된다.

  • 애플리케이션을 실행해보면 SimpleJdbcInsert가 어떤 INSERT SQL을 만들어서 사용하는지 로그로 확인할 수 있다.

JdbcTemplate 기능 정리

주요 기능

JdbcTemplate

  • 순서 기반 파라미터 바인딩을 지원한다.

NamedParameterJdbcTemplate

  • 이름 기반 파라미터 바인딩을 지원한다. (권장)

SimpleJdbcInsert

  • INSERT SQL을 편리하게 사용할 수 있다.
    ❤ SimpleJdbcCall
  • Stored Proceduer를 편리하게 호출할 수 있음.

JdbcTemplate 사용법 정리

JdbcTemplate에 대한 사용법은 스프링 공식 메뉴얼에 자세히 소개되어 있다. 기능 간단히 정리 !!

❤각종 메뉴얼

  • Stored Procedure
    - Data Access (spring.io)
  • JdbcTemplate 사용 방법
    - Data Access (spring.io)

주요 메서드

  • queryForObject : 단건 조회
  • query : 여러 건 조회

조회

단건 조회 → 숫자 조회

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);
  • 객체를 Mapping하기 위해 RowMapper를 넘겨줘야 함.

목록 조회 → 객체

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 / UPDATE / DELETE)

데이터를 변경할 때는 jdbcTempalte.update()를 사용하면 된다. 반환값은 int이고, SQL 실행 결과에 영향을 받은 Row 수를 반환함.

등록

template.update(
        "insert into t_actor (first_name, last_name) values (?,?)",
        "Leonor", "watling");

수정

template.update(
        "update t_actor set last_name = ? where id = ? ",
        "Banjo", 5276L);

삭제

template.update(
        "delete from t_actor where id = ?",
        Long.valueOf(actorId));

기타 기능

  • 임의의 SQL을 실행할 때는 execute()를 사용하면 된다. 테이블을 생성하는 DDL등에 사용할 수 있음.
template.execute(
        "create table mytable (id integer, name varchar(100))");

JdbcTemplate 정리

실무에서 가장 간단하고 실용적인 방법으로 SQL을 사용하려면 JdbcTemplate을 사용하면 된다.
JPA와 같은 ORM 기술을 사용하면서 동시에 SQL을 직접 작성해야 할 때가 있는데, 그때도 JdbcTemplate을 함께 사용하면 된다.
그런데 JdbcTemplate의 최대 단점이 있는데, 바로 동적 쿼리 문제를 해결하지 못한다는 점이다. 그리고 SQL을 자바 코드로 작성하기 때문에 SQL 라인이 코드를 넘어갈 때 마다 문자 더하기를 해주어야 하는 단점도 있다.

🎈 동적 쿼리 문제를 해결하면서 동시에 SQL도 편리하게 작성할 수 있게 도와주는 기술이 바로 MyBatis이다

profile
back-end, 지속 성장 가능한 개발자를 향하여

0개의 댓글