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 |