목차
- JDBC란?
- Spring Boot에서 JDBC 사용하기
- 자동 설정 원리
- JdbcTemplate
- Swagger 설정 및 테스트
- CRUD 연산 테스트
- 에러 해결 방법
一. JDBC란?
JDBC(Java Database Connectivity)는 Java 표준 API 중 하나로, Java 애플리케이션이 데이터베이스에 연결하여 SQL문을 실행할 수 있게 해주는 기술입니다. JDBC는 다음과 같은 주요 기능을 제공합니다:
- 데이터베이스への接続
- SQL 문 실행
- 결과 데이터 처리
二. Spring Boot에서 JDBC 사용하기
2.1 프로젝트 생성 및 의존성 추가
Spring Boot 프로젝트를 생성할 때 다음 의존성을 추가해야 합니다:
- spring-boot-starter-jdbc: JDBC 지원
- MySQL 드라이버: MySQL 데이터베이스 연결용
- spring-boot-starter-web: 테스트용 웹 환경
POM.xml에 추가할 의존성:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.2 데이터베이스 연결 설정
application.yml 파일에 데이터베이스 연결 정보를 설정합니다:
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/study-spring-boot?serverTimezone=UTC&useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
driverClassName: com.mysql.cj.jdbc.Driver
참고: 구버전 드라이버 클래스(com.mysql.jdbc.Driver)는 더 이상 사용되지 않으며,新版(com.mysql.cj.jdbc.Driver)를 사용해야 합니다.
2.3 데이터소스 및 연결 확인
다음 테스트 코드로 데이터소스와 데이터베이스 연결을 확인할 수 있습니다:
package com.example.springboot;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@SpringBootTest
class SpringbootDataApplicationTests {
@Autowired
DataSource dataSource;
@Test
void testDataSource() throws SQLException {
System.out.println("데이터소스: " + dataSource.getClass());
Connection conn = dataSource.getConnection();
System.out.println("데이터베이스 연결: " + conn);
conn.close();
}
}
실행 결과:
- 기본 데이터소스:
class com.zaxxer.hikari.HikariDataSource - 데이터베이스 연결:
HikariProxyConnection@xxxxxxx wrapping com.mysql.cj.jdbc.ConnectionImpl@xxxxxxx
三. 자동 설정 원리
Spring Boot의 자동 설정은 org.springframework.boot.autoconfigure.jdbc 패키지에서 관리됩니다. DataSourceConfiguration 클래스가 다양한 조건을 기반으로 적절한 데이터소스를 자동 구성합니다.
3.1 데이터소스 자동 선택
사용 가능한 데이터소스 구현체:
// Tomcat Pool DataSource 설정
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type",
havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
matchIfMissing = true)
static class Tomcat {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
public DataSource dataSource() {
// Tomcat 풀 데이터소스 생성
}
}
3.2 지원되는 데이터소스 유형
Spring Boot는 다음 데이터소스를 기본으로 지원합니다:
- org.apache.tomcat.jdbc.pool (기본값)
- com.zaxxer.hikari.HikariDataSource
- org.apache.commons.dbcp2
3.3 커스텀 데이터소스 지원
DataSourceBuilder를 사용하여 사용자 정의 데이터소스를 생성할 수 있습니다:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
DataSource dataSource(DataSourceProperties properties) {
// Reflection을 사용하여指定된 타입의 데이터소스를 생성하고 속성 바인딩
return properties.initializeDataSourceBuilder().build();
}
}
3.4 DataSource 초기화 실행기
DataSourceInitializerInvoker 클래스는 데이터베이스 스키마 및 데이터 스크립트를 자동으로 실행합니다:
- 스키마 생성:
schema-*.sql파일로 테이블 생성 - 데이터 초기화:
data-*.sql파일로 초기 데이터 삽입
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
if (resources != null) {
return getResources(propertyName, resources, true);
}
String platform = this.properties.getPlatform();
List<String> fallbackResources = new ArrayList<>();
fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
fallbackResources.add("classpath*:" + fallback + ".sql");
return getResources(propertyName, fallbackResources, false);
}
스크립트 실행 모드:
private boolean isEnabled() {
DataSourceInitializationMode mode = this.properties.getInitializationMode();
if (mode == DataSourceInitializationMode.NEVER) {
return false;
}
if (mode == DataSourceInitializationMode.EMBEDDED && !isEmbedded()) {
return false;
}
return true;
}
실행 모드 옵션:
- NEVER: 스크립트 실행 안 함
- EMBEDDED: 임베디드 데이터소스에만 실행 (기본값)
- ALWAYS: 항상 실행
3.5 사용자 정의 스크립트 지정
application.yml에서 실행할 스크립트를 지정할 수 있습니다:
spring:
datasource:
schema:
- classpath:department.sql
initialization-mode: always
四. JdbcTemplate
JdbcTemplate은 JDBC를 간단하고 효율적으로 사용할 수 있도록 하는 Spring의 핵심 클래스입니다. JdbcTemplateAutoConfiguration이 자동으로 설정합니다:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {
}
五. Swagger 설정
5.1 Swagger 의존성 추가
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
5.2 Swagger 설정 클래스
package com.example.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Spring Boot REST API 문서")
.description("Spring Boot RESTful API 문서입니다.")
.version("1.0")
.build();
}
}
5.3 Swagger 문서 접근
애플리케이션 실행 후 다음 URL로 Swagger 문서에 접근합니다:
http://localhost:8080/swagger-ui.html
六. CRUD 연산 테스트
JdbcTemplate을 사용하여 부서(Department) 테이블에 대한 CRUD 연산을 테스트합니다.
6.1 부서 생성
@ApiOperation(value = "1.부서 생성")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "부서명")
})
@PostMapping("/create")
public int createDepartment(@RequestParam String name) {
String sql = String.format("INSERT INTO department(departmentName) VALUES('%s')", name);
int result = jdbcTemplate.update(sql);
return result;
}
6.2 모든 부서 조회
@ApiOperation(value = "2.모든 부서 조회")
@GetMapping("/getAllDepartment")
public List<Map<String, Object>> getAllDepartment() {
List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * FROM department");
return list;
}
6.3 특정 부서 조회 (ID로)
@ApiOperation(value = "3.특정 부서 조회")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "조회할 부서 ID")
})
@GetMapping("/{id}")
public Map<String, Object> getDepartmentById(@PathVariable Long id) {
String sql = "SELECT * FROM department WHERE id = " + id;
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
return list.get(0);
}
6.4 부서 정보 수정
@ApiOperation(value = "4.부서 정보 수정")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "수정할 부서 ID"),
@ApiImplicitParam(name = "name", value = "새 부서명")
})
@PostMapping("/update")
public int updateDepartmentById(@RequestParam Long id, @RequestParam String name) {
String sql = String.format("UPDATE department SET departmentName = '%s' WHERE id = %d", name, id);
int result = jdbcTemplate.update(sql);
return result;
}
6.5 부서 삭제
@ApiOperation(value = "5.부서 삭제")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "삭제할 부서 ID")
})
@PostMapping("/delete")
public int deleteDepartment(@RequestParam Long id) {
String sql = String.format("DELETE FROM department WHERE id = %d", id);
int result = jdbcTemplate.update(sql);
return result;
}
七. 에러 해결 방법
에러 1: MySQL 서버 연결 거부
에러 메시지:
java.sql.SQLQLException: null, message from server: "Host '호스트명' is not allowed to connect to this MySQL server"
해결 방법:
USE mysql;
SELECT host FROM user;
UPDATE user SET host = '%' WHERE user = 'root';
FLUSH PRIVILEGES;
에러 2: timezone 오류
에러 메시지:
com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value '타임존값' is unrecognized...
해결 방법:
application.yml의 datasource URL에 timezone 파라미터를 추가합니다:
spring:
datasource:
url: jdbc:mysql://localhost:3306/dbname?serverTimezone=UTC&...