프로젝트 요구사항에 맞추다보면 조회 성능 등의 이유로 삽입 직후에 별도의 조회없이 곧바로 primary key를 반환받아야 하는 경우가 있다.
JdbcTemplate에서 삽입 직후 곧바로 primary key를 받아오는 기능에는 KeyHolder와 SimpleJdbcInsert가 있다.
이번 프로젝트를 진행하며 리뷰 받기 전에는 jdbcTemplate의 KeyHolder 기능밖에 몰랐으나 리뷰를 받고 찾아보며 SimpleJdbcInsert에 대해 알게 되어 추가로 정리했다.
db에 새로운 정보를 추가하고 그 때 생성된 primary key를 가져오기 위해서 KeyHolder
사용 가능
jdbcTemplate 내부에 정의된 기능으로, update 쿼리 두번째 인자에 KeyHolder를 넣으면 생성된 테이블에 대한 primary key를 곧바로 받아올 수 있다.
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(
"insert into customers (first_name, last_name) values (?,?),
new String[]{"id"});
ps.setString(1, customer.getFirstName());
ps.setString(2, customer.getLastName());
return ps;
}, keyHolder);
Long id = keyHolder.getKey().longValue();
해당 코드도 람다함수, 삽입 방식 등 가독성이 좋지 않는데 예외처리로 try-catch 구문이 추가되면 더 복잡한 코드가 된다.
JdbcTemplate에서 이러한 가독성 문제를 해결하기 위해서 SimpleJdbcInsert를 지원한다.
내가 코드에 적용한 방식은 아래와 같다.
@Autowired
public ReservationTimeRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.jdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("reservation_time")
.usingGeneratedKeyColumns("id");
}
@Override
public Long add(ReservationTime reservationTime) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("start_at", reservationTime.getStartAt());
Long id = (Long) jdbcInsert.executeAndReturnKey(parameters);
reservationTime.setId(id);
return id;
}
주목할 부분은 다음과 같다.
jdbcInsert.executeAndReturnKey(Map 자료구조);
형식을 통해 insert한 값의 id를 받아올 수 있다.
그런데 단순히 SimpleJdbcInsert를 생성하기만 하면 Key를 리턴받을 수 없고, withTableName()
와 usingGeneratedKeyColumns()
을 통해 생성자에 기본 세팅을 마쳐야 Key를 받아올 수 있다.
this.jdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("reservation_time")
.usingGeneratedKeyColumns("id");
...
Long id = (Long) jdbcInsert.executeAndReturnKey(parameters);
쿼리 문자열도 필요 없어져서 매우 간결한 코드로 작성 가능했다.