[Spring] JdbcTemplate DB 처리 방법

hyozkim·2020년 1월 21일
2

Repository는 jdbc 처리가 이루어지는 곳으로, 데이터 처리를 구현하며 코드 리뷰받은 내용을 정리하려 한다.
1) JdbcTemplate 사용전
2) JdbcTemplate 사용후

1) JdbcTemplate 사용 전

  • NamedParameterJdbcTemplate 사용
  • SimpleJdbcInsert 사용
  • DataSource 주입

@Repository
public class JdbcUserRepository {
 
    private NamedParameterJdbcTemplate jdbc;
    private SimpleJdbcInsert insertAction;
    public UserDao(DataSource dataSource) {
        this.jdbc = new NamedParameterJdbcTemplate(dataSource);
        this.rowMapper = new BeanPropertyRowMapper<User>(User.class);
        this.insertAction = new SimpleJdbcInsert(dataSource)
                .withTableName("USER")
                .usingColumns("EMAIL", "PASSWD")
                .usingGeneratedKeyColumns("seq");
        // usingColumns 함수:
        // SimpleJdbcInsert시 사용할 컬럼을 지정.
        // 그렇지 않으면 User 모든 컬럼을 insert 쿼리 value에 넣으므로 param에 없는 컬럼은 null이 됨.
    }

    @Override
    public List<User> selectUsersAll() {
        return jdbc.query("SELECT SEQ, EMAIL, PASSWD , LOGIN_COUNT , LAST_LOGIN_AT , CREATE_AT  FROM USER ORDER BY SEQ", Collections.emptyMap(), rowMapper);
    }

    @Override
    public User selectUserById(Long seq) {
        Map<String, Object> params = new HashMap<>();
        params.put("seq", seq);
        return jdbc.queryForObject("SELECT SEQ, EMAIL, PASSWD, LOGIN_COUNT, LAST_LOGIN_AT, CREATE_AT FROM USER WHERE SEQ = ? ORDER BY SEQ", params, rowMapper);
    }

    @Override
    public SignupResponse insert(SignupRequest signupRequest) {
        SqlParameterSource params = new BeanPropertySqlParameterSource(signupRequest);
		return insertAction.executeAndReturnKey(params).longValue();
    }
}

2) JdbcTemplate 사용 후

  • 생성자 주입 -> immutability
  • UserMapper 클래스 따로 뺴서 람다로 처리
  • UserRepository 인터페이스 생성하여 사용 -> 나중에 JPA 사용 고려, mocking도 하기 쉬움.
  • 예외처리(미완성)
@Repository
public class JdbcUserRepository implements UserRepository {
    private final JdbcTemplate jdbcTemplate;
    private final MessageSource messageSource;

    public JdbcUserRepository(JdbcTemplate jdbcTemplate, MessageSource messageSource) {
        this.jdbcTemplate = jdbcTemplate;
        this.messageSource = messageSource;
    }

    @Override
    public List<User> findAll() {
        List<User> users = jdbcTemplate.query("SELECT SEQ, EMAIL, PASSWD , LOGIN_COUNT , LAST_LOGIN_AT , CREATE_AT  FROM USER ORDER BY SEQ", userMapper);
        return !users.isEmpty() ? users : null;
    }

    @Override
    public User findById(Long seq) {
        try {
            return jdbcTemplate.queryForObject("SELECT SEQ, EMAIL, PASSWD, LOGIN_COUNT, LAST_LOGIN_AT, CREATE_AT FROM USER WHERE SEQ = ? ORDER BY SEQ", new Object[]{seq}, userMapper);
        } catch(EmptyResultDataAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public SignupResponse join(SignupRequest signupRequest) {
        return jdbcTemplate.update("INSERT INTO USER(EMAIL,PASSWD,CREATE_AT) VALUES(?,?,?)", new Object[]{signupRequest.getPrincipal(),signupRequest.getCredentials(), new Date() }) > 0  ?
                new SignupResponse(true, messageSource.getMessage("insert.response.success", new Object[]{}, Locale.KOREA))
                : new SignupResponse(false, messageSource.getMessage("insert.response.failure", new Object[]{}, Locale.KOREA));
    }

    // 맵퍼 람다 처리
    static RowMapper<User> userMapper = (rs, rowNum) -> new User( rs.getLong("SEQ"),
            rs.getString("EMAIL"),
            rs.getString("PASSWD"),
            rs.getInt("LOGIN_COUNT"),
            rs.getDate("LAST_LOGIN_AT"),
            rs.getDate("CREATE_AT"));
}

느낀점

이를 구현하면서 Spring-Jdbc 기초를 한번 다시 되짚어보게 되었다.
Spring Boot 를 사용하면서 이전에 Spring Framework에서 구현할 때보다 훨씬 생산성 높은 코드 작성이 가능하다고 느껴졌다. DataSource도 주입받지 않고, application.yml 에 db 정보를 작성하여 Spring boot Auto-Configuration 기능으로 받아와 사용할 수 있었다. (@EnableAutoConfiguration)

그리고 이전에 insert, select query를 작성하기 위해 주입해야할 클래스가 많았다.
(Spring 에도 있었지만) 이번 기회로 JdbcTemplate 클래스를 처음 사용해보면서 이를 활용하여 꽤나 쉽게(?) Repository를 작성할 수 있었다. JPA를 함께 공부하고 있는데 Repository를 interface를 생성하는 이유와 함수의 이름도 JPA에서 사용하는 그대로 써보기 위해 노력했다.

확실히 이전보다 직관성 높은 코드 작성과 효율적인 코딩이 가능해졌다고 느낄 수 있었다.
하지만 전혀 개념을 알지 못하고 사용하면 후에 문제가 생겼을 때, 원인 파악이 어려울 거라 생각한다. 지금 많이 삽질 하자

참고

다음에 해볼것

  • Lombok Builder 사용하여 DTO 구현
  • Spring 메시징 처리
  • Mocking 테스트 구현
  • 예외처리를 Service Layer에서 구현 (+) @Valid로 처리
profile
차근차근 develog

0개의 댓글