-
[Spring] JdbcTemplate을 사용하여 DB에 저장할 때, 키(PK) 값 가져오기legacy/Spring 2023. 11. 1. 17:16
원인
Repository를 테스트할 때 key 값에 null이 저장되어 있어 DB 조회가 불가능한 상황이다.
DB
DB의 키(PK) 값은 auto_increment 전략을 사용하고 있다. Application 시점에서는 DB에 값을 저장하더라도 키 값을 알 수가 없다. 이는 테스트 코드에서 DB에 정상적으로 저장됐는지 확인하는 과정에서 문제가 발생한다.
Application
jdbcTemplate.update("INSERT INTO Member(login_id, name, password) VALUES(?, ?, ?)",member.getLoginId(), member.getName(), member.getPassword());
JdbcTemplate을 사용하여 DB에 Member 객체를 저장하였다.
Test
@Test void save() throws SQLException { Member member = new Member(); member.setLoginId("아이디"); member.setName("이름"); member.setPassword("비밀번호"); memberRepository.save(member); Member findMember = memberRepository.findById(member.getId()); Assertions.assertThat(member.getLoginId()).isEqualTo(findMember.getLoginId()); Assertions.assertThat(member.getName()).isEqualTo(findMember.getName()); Assertions.assertThat(member.getPassword()).isEqualTo(findMember.getPassword()); }
DB에 Member가 정상적으로 저장되는지 테스트하는 코드이다. memberRepository.save() 시점에서 Member 객체가 DB에 저장된다. 하지만 해당 Member 객체는 Application 시점에서는 키 값을 가지고 있지 않다(id == null)
따라서 findById(member.getId()) 시점에서 null을 인자로 넘기기 때문에 에러가 터진다.
해결
위의 문제를 해결하기 위해서 JdbcTemplate를 수정해야 한다.
KeyHolder 사용
KeyHolder keyHolder = new GeneratedKeyHolder();
KeyHolder를 통해 DB에 저장된 객체의 id값을 Application으로 가져올 수 있다.
JdbcTemplate Insert
jdbcTemplate.update(conn -> { PreparedStatement stmt = conn.prepareStatement("INSERT INTO Member(login_id, name, password) VALUES(?, ?, ?)", new String[]{"id"}); stmt.setString(1, member.getLoginId()); stmt.setString(2, member.getName()); stmt.setString(3, member.getPassword()); return stmt; }, keyHolder);
update 메서드에 람다식을 넘긴다. Connection을 인자로 넘겨 파라미터 바인딩 Statement를 생성한다.
keyHolder.getKey().longValue();
update를 수행하면 KeyHolder에 DB에 저장된 id 값을 가져올 수 있다. KeyHolder는 Number 타입이다. Application에서 id 타입을 Long으로 설정했기 때문에 타입 변환(longValue())을 해준다.
최종 코드
public Member save(Member member) throws SQLException { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(conn -> { PreparedStatement stmt = conn.prepareStatement("INSERT INTO Member(login_id, name, password) VALUES(?, ?, ?)", new String[]{"id"}); stmt.setString(1, member.getLoginId()); stmt.setString(2, member.getName()); stmt.setString(3, member.getPassword()); return stmt; }, keyHolder); member.setId(keyHolder.getKey().longValue()); return member; }
결론
수정 전
public Member save(Member member) throws SQLException { jdbcTemplate.update("INSERT INTO Member(login_id, name, password) VALUES(?, ?, ?)",member.getLoginId(), member.getName(), member.getPassword()); return member; }
수정 후
public Member save(Member member) throws SQLException { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(conn -> { PreparedStatement stmt = conn.prepareStatement("INSERT INTO Member(login_id, name, password) VALUES(?, ?, ?)", new String[]{"id"}); stmt.setString(1, member.getLoginId()); stmt.setString(2, member.getName()); stmt.setString(3, member.getPassword()); return stmt; }, keyHolder); member.setId(keyHolder.getKey().longValue()); return member; }