영호

[Spring JDBC] SimpleJdbcInsert로 쉽게 INSERT하기 본문

Spring

[Spring JDBC] SimpleJdbcInsert로 쉽게 INSERT하기

0h0 2023. 4. 12. 21:06

SimpleJdbcInsert란

기존 JdbcTemplate를 이용한 Insert보다 손쉽게 데이터를 저장하기 위해 제공하는 구현체다.

데이터 저장 후 primaryKey를 알고 싶은 경우 SimpleJdbcInsert를 이용해 keyHolder없이 구현할 수 있다.

SimpleJdbcInsert와 JdbcTemplate제공 기능 차이

SimpleJdbcInsert

SimpleJdbcInsert는 데이터를 저장하기 위한 기능만 제공하기 위해 SimpleJdbcInsertOperations를 구현하고 있다.

  • execute()
  • executeAndReturnKey()
  • withTableName()
  • 등등...

JdbcTemplate

JdbcTemplate은 데이터 저장뿐만 아니라 조회, 삭제, 업데이트 등의 기능을 제공하고 있기 때문에 JdbcOperations를 구현하고 있다.

JdbcTemplate으로 INSERT후 primaryKey가져오기

keyHolder를 이용해 코드를 작성해야 한다.

final String sql = "insert into customers (first_name, last_name) values (?, ?)";
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
    PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"});
    ps.setString(1, customer.getFirstName());
    ps.setString(2, customer.getLastName());
    return ps;
}, keyHolder);

long key = keyHolder.getKey().longValue();
return key;

사실 코드를 처음보고 이해가 안됐다. PreparedStatement객체 생성하고, 파라미터 바인딩하고, 반환하고 예제 코드를 봤을 때 읽기 싫었다.

하지만 레벨1 체스미션에서 데이터 생성 후 primaryKey가 필요한 상황이 있었기 때문에 코드를 이해하고 넘어가고 싶었지만 쉽지 않았다.

다행히 SimpleJdbcInsert가 위와 같은 과정을 간단하게 제공해주는 기능이 있다.

SimpleJdbcInsert로 대체하기

SimpleJdbcInsert객체 생성

private SimpleJdbcInsert insertActor;

public void setDataSource(DataSource dataSource) {
    this.insertActor = new SimpleJdbcInsert(dataSource)
            .withTableName("t_actor")
            .usingGeneratedKeyColumns("id");
}

usingGeneratedKeyColumns메서드를 통해 자동생성되는 key가 있는 column을 지정해준다.

저장할 Actor코드

public class Actor {
    private long id;
    private String firstName
    private String lastName;

    public Actor(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Actor(long id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public long getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
}

Map을 통한 저장

public Actor add(Actor actor) {
    Map<String, Object> parameters = new HashMap<String, Object>(2);
    parameters.put("first_name", actor.getFirstName());
    parameters.put("last_name", actor.getLastName());
    Long newId = insertActor.executeAndReturnKey(parameters).longValue();
    
    return new Actor(newId, actor.getFirstName(), actor.getLastName());
}

JdbcTemplate보단 코드 가독성이 좋다. 그러나 Map보다 사용하기 편한 방법을 더 제공해준다.

SqlParameterSource를 이용한 저장

SqlParameterSource인터페이스를 구현한 MapSqlParameterSource, BeanPropertySqlParameterSource클래스를 제공한다.

MapSqlParameterSource

public Actor add(Actor actor) {
    SqlParameterSource parameters = new MapSqlParameterSource()
            .addValue("first_name", actor.getFirstName())
            .addValue("last_name", actor.getLastName());
    Long newId = insertActor.executeAndReturnKey(parameters).longValue();

	return new Actor(newId, actor.getFirstName(), actor.getLastName());
}

 

Map방식의 put대신 메서드 체이닝 방식을 이용해 데이터를 만든다.

(사실 Map방식과 큰 차이를 모르겠다..)

BeanPropertySqlParameterSource

public Actor add(Actor actor) {
    SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor);
    Long newId = insertActor.executeAndReturnKey(parameters).longValue();
	
    return new Actor(newId, actor.getFirstName(), actor.getLastName());
}

DB컬럼과 인스턴스 변수가 일치하는 객체(빈)이 존재한다면 이 방법을 사용할 수 있다. NOT NULL컬럼의 경우 반드시 해당 객체의 인스턴스 변수로 존재해야 한다. 만약, DB테이블에 age컬럼이 NOT NULL로 존재하는데 Actor클래스에 age변수가 없다면 에러가 발생한다.

 

위 코드가 가능한 이유는 해당 객체의 getter를 이용해 데이터를 추출해 저장한다.

 

마무리

우아한테크코스 레벨2에서 Spring을 학습하기 전 맛보기 느낌으로 알게된 개념들이라 아직 실제로 사용해보진 못했다. 그러나, 레벨1 체스미션에서 직접 Jdbc API를 사용헤보고, JdbcTemplate도 사용해보고 나서 SimpleJdbcInsert를 사용해보니 발전된 과정을 본듯한 기분이 들었다.

SimpleJdbcCall도 있는데 이건 아직 학습을 못했다. 미션 하면서 사용해봐야겠다.

 

출처

Comments