SpringStudy

JDBC 학습 정리(1)

dding-shark 2025. 8. 21. 15:19
728x90

실습 코드 : https://github.com/DDINGJOO/Spring5PlayGround.git

JDBC 학습 정리:

목차

  • 0) 데이터 모델 소개 (Singer/Album)
  • 1) 순수 JDBC로 시작하기 (PlainJdbcDemo, PlainSingerDao)
  • 2) 애노테이션 기반 설정과 내장 DB (H2) 활용 (ch6_03, ch6_04)
  • 3) 스프링의 예외 변환 (SQLErrorCodeSQLExceptionTranslator)
  • 4) JdbcTemplate: 위치 기반 파라미터("?")
  • 5) NamedParameterJdbcTemplate: 네임드 파라미터(":name")
  • 6) 스프링 부트 JPA Native Query에서 ":name" 권장
  • 7) 마무리 정리 및 팁

0) 데이터 모델 소개

파일: ch6_02_DataModel/entities/Singer.java, ch6_02_DataModel/entities/Album.java

  • Singer(id, firstName, lastName, birthDay)
  • Album(id, singerId, title, releaseDate)
  • 엔티티는 순수 JDBC 단계부터 템플릿 기반 단계까지 공통으로 사용됩니다.

1) 순수 JDBC로 시작하기

파일: ch6_02_DataModel/PlainJdbcDemo.java, ch6_02_DataModel/dao/PlainSingerDao.java

  • 핵심: 애플리케이션이 직접 드라이버 로딩, 커넥션 획득, PreparedStatement 생성/실행, ResultSet 매핑을 처리합니다.
  • 파라미터 바인딩: ?(위치 기반) 사용 → 순서에 매우 민감합니다.

예시 코드 (PlainSingerDao.java):

PreparedStatement statement = connection.prepareStatement(
    "insert into singer(first_name, last_name, birth_date) values(?,?,?)",
    Statement.RETURN_GENERATED_KEYS
);
// 위치 기반("?") 파라미터 바인딩: 순서가 매우 중요합니다.
statement.setString(1, singer.getFirstName());
statement.setString(2, singer.getLastName());
statement.setDate(3, new Date(singer.getBirthDay().getTime()));
statement.executeUpdate();

장단점

  • 장점: 의존 라이브러리가 거의 없고 JDBC 작동 원리를 이해하기 좋음
  • 단점: 반복 코드가 많고, 자원 정리/예외 처리/매핑 코드가 누적됨 → 유지보수 어려움

2) 애노테이션 기반 설정과 내장 DB(H2) 활용

파일: ch6_03_Annotations/EmbeddedDbConfig.java, ch6_04_Embedded/EmbeddedJdbcConfig.java, ch6_04_Embedded/JdbcSingerDao.java

  • 내장 DB(H2) + 스크립트(schema.sql, test-data.sql)로 빠르게 환경 구성
  • 이 단계에서는 DataSource를 빈으로 구성하고, DAO에 주입하는 형태를 연습합니다.

예시 코드 (EmbeddedJdbcConfig.java):

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.H2)
        .addScript("classpath:db/schema.sql")
        .addScript("classpath:db/test-data.sql")
        .build();
}

3) 스프링의 예외 변환

파일: ch6_05_execptions/MySQLErrorCodesTransfer.java

  • DB 벤더 별 SQL 오류코드를 스프링의 DataAccessException 계층으로 변환하여 서비스 계층에서 일관되게 처리할 수 있게 합니다.
public class MySQLErrorCodesTransfer extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException customTranslate(String task, String sql, SQLException ex) {
        if (ex.getErrorCode() == -12345) {
            return new DeadlockLoserDataAccessException(task, ex);
        }
        return null;
    }
}

4) JdbcTemplate: 위치 기반 파라미터("?")

파일: ch6_06_JDBCTemplate/JdbcSingerDao_jdbctemplate.java

  • 핵심: ? 순서대로 값을 바인딩합니다.
  • 장점: 순수 JDBC 대비 반복 코드(자원 관리/예외 변환 등) 제거
  • 단점: 파라미터가 많아질수록 순서 오류가 발생하기 쉬우며 가독성이 떨어짐
@Override
public String findNameById(Long id) {
    return jdbcTemplate.queryForObject(
        "select first_name || ' ' || last_name from singer where id = ?",
        new Object[]{id}, String.class);
}

5) NamedParameterJdbcTemplate: 네임드 파라미터(":name")

파일: ch6_07_NamedParameterJdbcTemplate/JdbcSingerDao.java

  • 핵심: :파라미터명으로 바인딩합니다.
  • 장점: 가독성 향상, 파라미터 순서에 덜 민감, 동적 SQL에서 유지보수 용이
String sql = "SELECT first_name || ' ' || last_name FROM singer WHERE id = :singerId";
Map<String, Object> params = new HashMap<>();
params.put("singerId", id);
return namedParameterJdbcTemplate.queryForObject(sql, params, String.class);

INSERT 예시 아이디어:

String sql = "INSERT INTO singer(first_name, last_name) VALUES (:firstName, :lastName)";
Map<String, Object> params = Map.of("firstName", singer.getFirstName(), "lastName", singer.getLastName());
namedParameterJdbcTemplate.update(sql, params);

6) 스프링 부트 JPA Native Query에서의 권장 사항

  • Native Query 작성 시 ? 보다는 :name을 사용하세요.
  • 예시 (JPA Repository):
    @Query(value = "SELECT * FROM singer s WHERE s.id = :singerId", nativeQuery = true)
    Optional<Singer> findByIdNative(@Param("singerId") Long id);
    이렇게 하면 서비스/레포지토리 계층에서 파라미터를 명확히 매핑할 수 있으며, 유지보수성이 향상됩니다.

7) 마무리 정리 및 팁

  • 학습 결론: ?(위치 기반)보다 :name(네임드) 사용을 습관화하면 실수 줄이고 유지보수성을 높일 수 있습니다.
  • 템플릿 사용: JdbcTemplate/NamedParameterJdbcTemplate를 통해 자원 관리/예외 처리를 일관성 있게 처리하세요.
  • 네임드 파라미터는 스프링 부트 JPA Native Query에서도 일관되게 사용하면 팀 전체 생산성이 올라갑니다.
  • 본 프로젝트의 각 단계 파일에 한국어 주석을 추가하여 학습 흐름을 코드와 함께 이해할 수 있도록 했습니다.
728x90

'SpringStudy' 카테고리의 다른 글

Spring Hibernate  (7) 2025.08.26
JDBC 최종  (4) 2025.08.21
Pointcut 문법 정리  (0) 2025.08.20
SpringAop -final  (0) 2025.08.20
Spring AOP  (1) 2025.08.19