티스토리 뷰
원인
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;
}