The getKey method should only be used when a single key is returned

작성: 2022.06.16

수정: 2022.06.16

읽는시간: 00 분

Development/Daily Error

반응형

Intro

JDBC의 KeyHolder를 사용 하던 중 위와 같은 에러를 만났습니다.

에러 전문은 아래와 같습니다.

image-20220616215857791

org.springframework.dao.InvalidDataAccessApiUsageException: The getKey method should only be used when a single key is returned. The current key entry contains multiple keys: [{id=13, item_name=M2Air, price=1690000, quantity=12}]

keyHolder.getKey() 를 했을때 하나의 키가 반환 되어야 하지만 여러개의 키가 반환 되어 발생한 에러라고 메시지에 쓰여 있습니다.

상황

NamedParameterJdbcTemplate 을 활용해 save 메서드를 구현 하고 있었고 코드는 아래와 같습니다.

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

무난하게 key를 뽑아 와야 하는데.. id 를 포함한 4개의 모든 컬럼이 넘어오고 있습니다.

img

PK를 설정 안했나 싶어 다시 한번 확인을 해 보았지만 PK도 정상적으로 설정 되어 있습니다.

영한님의 강의에서는 H2 데이터베이스를 활용 하는데, 저는 회사에 조금 일찍 출근해서, 그리고 퇴근 후에 집에서 서로 다른 컴퓨터로 강의를 듣다 보니 연속성을 유지하기 위해 개인적으로 PostgreSQL 데이터베이스를 하나 띄워놓고 연결 해 사용 하고 있는데요.

혹시나 DB 가 문제인가 싶어 H2로 연결해 같은 코드를 실행 해 보았더니 keyHolder.getKey() 가 정상적으로 동작 하였습니다.

문제

jdbc 드라이버가 key가 무엇인지를 알아내지 못해 multiple key를 반환 하고 있는 상황으로 보입니다.

https://stackoverflow.com/questions/17771306/spring-how-to-use-keyholder-with-postgresql 를 읽어 보면 postgresql-jdbc 의 특징인 것 같기도 합니다.

해결

두가지 해결 방법을 찾았는데 어떤게 더 나은지는 모르겠습니다.

방법1

img

update 메서드의 파라미터를 보던 중, keyColumnNames 를 문자열 배열로 받는게 보이길래 new String[]{"id"} 로 Key Column을 명시 해 주었더니 keyHolder.getKey().longValue() 코드가 문제 없이 잘 동작 하였습니다.

template.update(sql, param, keyHolder, new String[]{"id"});

방법2

template.update(sql, param, keyHolder);
long key = (long) keyHolder.getKeys().get("id");

multiple keys 라고 했으니 여러개의 key를 받는 코드가 있나 싶어 메서드 목록을 확인 했더니 Map<String, Object> 을 반환하는 .getKeys()가 있었습니다. 그래서 거기에서 바로 "id"로 꺼내 반환된 객체 타입을 확인 해 보았습니다.

String으로 반환되면 parseLong을 하려고 했지만 다행히도 Long 타입으로 반환이 되어 간단하게 타입 캐스팅만 해서 바로 key를 사용 할 수 있었고 코드도 문제 없이 동작 하였습니다.

마치며

문제 해결은 비교적 간단하게 했지만 KeyHolder의 자세한 동작 방법에 대한 이해가 선행되지 않았다보니 데이터베이스에 따라 getKey()가 의도대로 동작하지 못한 문제의 정확한 원인을 파악하지는 못해 아쉬움이 있습니다.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/support/KeyHolder.html

Spring의 KeyHolder에 대한 문서도 찾아봤지만 multiple key가 나올 수 있는 상황에 대한 부연설명이 있는건 아니고, multiple key를 마주했을때 발생하는 에러와 그때 쓸만한 메서드 안내 정도만 되어 있습니다.

감사합니다.

반응형