ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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;
    }
Designed by Tistory.