Framework/Spring

Spring 공부내용 정리 : 3. Spring 에서의 JDBC #2

Cs.Woo 2021. 8. 1. 22:59

  해당 내용의 출처는 김영한 님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술의 강의 내용이고,본 포스트는 수강후 개인적으로 공부한 내용을 필기하고 다시 복습하기 위하여 작성되었고, 기타 내용을 보충하기 위한 부분은 관련링크로서 출처를 밝힌다. 

  출처 관련 링크는 #1에 기재하였으니 참고하기 바란다.

 

1. 서론

 본 장에서는 JDBC Template를 이용하여 DB를 접근하는 방법, 그리고 그 원리에  대한 학습을 정리해 놓았다. Spring내부에 있는 기능으로 DB에 접근하는 방법에 대하여 이해하기 위해서는, 근본적인 내용을 알아야 한다고 생각하기 때문에 해당 내용을 알아보기 용이하도록 정리해놓았다.

 

  실습 설정은 이전 내용인 순수 JDBC와 같다. 참고하여 세팅하여 준다. 스프링 JDBC Tempalte는 MyBatis와 비슷한 기능을 제공한다. JDBC Template와 MyBatis와 같은 라이브러리들은 JDBC의 API에서 반복코드를 대부분 제거해준다. 그러나 SQL은 직접 작성하여야 한다.

 

// 데이터베이스에서 넘어온 데이터를 DTO의 객체와 매핑해주어 할당하는 로직
    private RowMapper<Member> memberRowMapper() {
        // RowMapper라는 클래스를 이용한다.
        return new RowMapper<Member>() {
            @Override
            public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
                // 우선 매핑할 객체를 생성
                Member member = new Member();

                // 해당 객체의 Id 필드에 rs(DB와의 통신으로 얻은 결과)의  long데이터타입 key가 "id"인 값을  할당
                member.setId(rs.getLong("id"));
                // 해당 객체의 name 필드에 rs(DB와의 통신으로 얻은 결과)의 String데이터타입 key가 "name"인 값을  할당
                member.setName(rs.getString("name"));

                return member;
            }
        }
    }
    
// 상기 코드를 람다식으로 변경
private RowMapper<Member> memberRowMapper() {
        // RowMapper라는 클래스를 이용한다.
        return (rs, rowNum) -> {
            // 우선 매핑할 객체를 생성
            Member member = new Member();
            
            // 해당 객체의 Id 필드에 rs(DB와의 통신으로 얻은 결과)의  long데이터타입 key가 "id"인 값을  할당
            member.setId(rs.getLong("id"));
            // 해당 객체의 name 필드에 rs(DB와의 통신으로 얻은 결과)의 String데이터타입 key가 "name"인 값을  할당
            member.setName(rs.getString("name"));

            return member;
        }
    }

  상기 예제처럼 RowMapper를 이용하여 발송될 sql과 프로젝트 내부 bean의 필드와 매핑을 한 다음,

 

@Override
    public Optional<Member> findById(Long id) {
        List<Member> result = jdbcTemplate.query("SELECT * FROM `member` WHERE `id` = ?", memberRowMapper());
        
        // 결과는 List에 담긴다. stream하여 List내부의 요소들을 전부 꺼낸다음, findany로 요소를 담아 리턴하면 된다.
        return result.stream().findAny();
    }

  해당 기능 로직에 상기와 같이 연결해준다면 DB접근 및 데이터 조회를 간단히 끝낼 수 있다. 데이터베이스에 데이터 삽입 예제는 다음과 같다.

 

@Override
    public Member save(Member member) {
        // SimpleJdbcInsert 객체는 jdbcTemplate를 주입받아 생성되는 인스턴스이다.
        SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
        // SimpleJdbcInsert를 이용하면, 이전에 순수하게 JDBC로만 구현한 것처럼
        // generate key 옵션을 사용할 필요도 없고, 쿼리를 작성할 필요도 없이 하기 메소드만 있으면
        // 해결된다.
        jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", member.getName());

        Number key = jdbcInsert.executeAndReturnKey(new
                MapSqlParameterSource(parameters));
        member.setId(key.longValue());

        return member;
    }

  전체 회원을 반환하는 예제는 다음과 같다.

 

@Override
    public List<Member> findAll() {
        return jdbcTemplate.query("SELECT * FROM `member`", memberRowMapper());
    }

  JDBCTemplate는 List를 반환하기 때문에, RowMapper를 이용하여 맵핑만 해서 반환하면 원하는 값을 얻을 수 있다.

 

3.  결론

  JDBC에서 중복적으로 다루던 코드, 그리고 복잡한 로직을 보다 쉽고 간결하게 사용할 수 있게 되었다는 것을 쉽게  직관적으로 확인할 수 있었다.

  순수하게  JDBC를 구현하는 로직에서의 여러가지 복잡한 부분 그리고 문제점을 내부적으로 해결하고 보다 직관적인 코드로 변경하여 사용할 수 있게 구성하여 생산성이 훨씬 많이 상승되었다는 것 역시 확인할 수 있다.