1. 개요

이 튜토리얼에서는 Spring JDBC 모듈의 실제 사용 사례를 살펴볼 것입니다.

Spring JDBC의 모든 클래스는 4 개의 개별 패키지로 나뉩니다.

  • core — JDBC의 핵심 기능. 이 패키지의 중요한 클래스에는 JdbcTemplate , SimpleJdbcInsert , SimpleJdbcCall NamedParameterJdbcTemplate이 있습니다.
  • datasource — 데이터 소스에 액세스하기위한 유틸리티 클래스입니다. 또한 Jakarta EE 컨테이너 외부에서 JDBC 코드를 테스트하기위한 다양한 데이터 소스 구현이 있습니다.
  • object — 객체 지향 방식의 DB 액세스. 쿼리를 실행하고 결과를 비즈니스 개체로 반환 할 수 있습니다. 또한 비즈니스 개체의 열과 속성간에 쿼리 결과를 매핑합니다.
  • 지원 코어 객체 패키지아래의 클래스에 대한 지원 클래스 (예 : SQLException 변환 기능 제공)

2. 구성

데이터 소스의 간단한 구성부터 시작하겠습니다.

MySQL 데이터베이스를 사용합니다.

@Configuration
@ComponentScan("com.baeldung.jdbc")
public class SpringJdbcConfig {
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");

        return dataSource;
    }
}

또는 개발 또는 테스트를 위해 임베디드 데이터베이스를 잘 활용할 수도 있습니다.

다음은 H2 임베디드 데이터베이스의 인스턴스를 만들고 간단한 SQL 스크립트로 미리 채우는 빠른 구성입니다.

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

마지막으로 데이터 소스에 대한 XML 구성을 사용하여 동일한 작업을 수행 할 수 있습니다 .

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/springjdbc"/>
    <property name="username" value="guest_user"/>
    <property name="password" value="guest_password"/>
</bean>

3. JdbcTemplate 및 쿼리 실행

3.1. 기본 쿼리

JDBC 템플릿은 관심있는 대부분의 기능에 액세스 할 수있는 주요 API입니다.

  • 연결 생성 및 종료
  • 문 및 저장 프로 시저 호출 실행
  • ResultSet 반복 및 결과 반환

먼저 JdbcTemplate무엇을 할 수 있는지 알아보기 위해 간단한 예제로 시작해 보겠습니다 .

int result = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM EMPLOYEE", Integer.class);

다음은 간단한 INSERT입니다.

public int addEmplyee(int id) {
    return jdbcTemplate.update(
      "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill", "Gates", "USA");
}

?를 사용하여 매개 변수를 제공하는 표준 구문에 주목하십시오 . 캐릭터.

다음으로이 구문의 대안을 살펴 보겠습니다.

3.2. 명명 된 매개 변수가있는 쿼리

명명 된 매개 변수에 대한 지원 을 얻기 위해 프레임 워크에서 제공하는 다른 JDBC 템플릿 인 NamedParameterJdbcTemplate을 사용 합니다.

또한 이것은 JbdcTemplate을 래핑하고 ?를 사용하는 기존 구문에 대한 대안을 제공합니다. 매개 변수를 지정합니다.

내부적으로는 명명 된 매개 변수를 JDBC ? 쿼리를 실행 하기 위해 래핑 된 JDCTemplate 에 대한 자리 표시 자 및 위임 :

SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", 1);
return namedParameterJdbcTemplate.queryForObject(
  "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);

MapSqlParameterSource사용 하여 명명 된 매개 변수의 값을 제공하는 방법을 확인하십시오.

명명 된 매개 변수를 결정하기 위해 Bean의 속성을 사용하는 방법을 살펴 보겠습니다.

Employee employee = new Employee();
employee.setFirstName("James");

String SELECT_BY_ID = "SELECT COUNT(*) FROM EMPLOYEE WHERE FIRST_NAME = :firstName";

SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(employee);
return namedParameterJdbcTemplate.queryForObject(
  SELECT_BY_ID, namedParameters, Integer.class);

이전과 같이 수동으로 명명 된 매개 변수를 지정하는 대신 BeanPropertySqlParameterSource 구현을 사용하는 방법에 유의하십시오 .

3.3. 쿼리 결과를 Java 개체에 매핑

또 다른 매우 유용한 기능은 RowMapper 인터페이스 를 구현하여 쿼리 결과를 Java 개체에 매핑하는 기능 입니다.

예를 들어, 쿼리에서 반환 된 모든 행에 대해 Spring은 행 매퍼를 사용하여 Java Bean을 채 웁니다.

public class EmployeeRowMapper implements RowMapper<Employee> {
    @Override
    public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();

        employee.setId(rs.getInt("ID"));
        employee.setFirstName(rs.getString("FIRST_NAME"));
        employee.setLastName(rs.getString("LAST_NAME"));
        employee.setAddress(rs.getString("ADDRESS"));

        return employee;
    }
}

그 후, 이제 행 매퍼를 쿼리 API에 전달하고 완전히 채워진 Java 객체를 가져올 수 있습니다.

String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
Employee employee = jdbcTemplate.queryForObject(
  query, new Object[] { id }, new EmployeeRowMapper());

4. 예외 번역

Spring은 기본적 으로 DataAccessException 을 루트 예외로 사용하는 자체 데이터 예외 계층 구조와 함께 제공되며 모든 기본 원시 예외를 여기로 변환합니다.

따라서 우리는 저수준 지속성 예외를 처리하지 않음으로써 온 전성을 유지합니다. 또한 Spring이 DataAccessException 또는 하위 클래스 중 하나 에서 저수준 예외를 래핑한다는 사실로부터 이익을 얻습니다 .

이것은 또한 우리가 사용하는 기본 데이터베이스와 독립적 인 예외 처리 메커니즘을 유지합니다.

기본 SQLErrorCodeSQLExceptionTranslator 외에도 SQLExceptionTranslator 의 자체 구현을 제공 할 수도 있습니다 .

다음은 사용자 지정 구현의 간단한 예입니다. 중복 키 위반이있을 때 오류 메시지를 사용자 지정 하여 H2를 사용할 때 오류 코드 23505발생합니다 .

public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException
      customTranslate(String task, String sql, SQLException sqlException) {
        if (sqlException.getErrorCode() == 23505) {
          return new DuplicateKeyException(
            "Custom Exception translator - Integrity constraint violation.", sqlException);
        }
        return null;
    }
}

이 사용자 정의 예외 변환기를 사용하려면 setExceptionTranslator () 메서드를 호출 하여 JdbcTemplate전달해야합니다 .

CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator = 
  new CustomSQLErrorCodeTranslator();
jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);

5. SimpleJdbc 클래스를 사용한 JDBC 작업

SimpleJdbc 클래스는 SQL 문을 구성하고 실행하는 쉬운 방법을 제공합니다. 이러한 클래스는 데이터베이스 메타 데이터를 사용하여 기본 쿼리를 작성합니다. 따라서 SimpleJdbcInsertSimpleJdbcCall 클래스는 삽입 및 저장 프로 시저 호출을 실행하는 더 쉬운 방법을 제공합니다.

5.1. SimpleJdbcInsert

최소한의 구성으로 간단한 삽입 문을 실행하는 방법을 살펴 보겠습니다.

INSERT 문은 SimpleJdbcInsert 구성을 기반으로 생성됩니다 . 테이블 이름, 열 이름 및 값을 제공하기 만하면됩니다.

먼저 SimpleJdbcInsert를 만들어 보겠습니다  .

SimpleJdbcInsert simpleJdbcInsert = 
  new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");

다음으로 열 이름과 값을 제공하고 작업을 실행 해 보겠습니다.

public int addEmplyee(Employee emp) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("ID", emp.getId());
    parameters.put("FIRST_NAME", emp.getFirstName());
    parameters.put("LAST_NAME", emp.getLastName());
    parameters.put("ADDRESS", emp.getAddress());

    return simpleJdbcInsert.execute(parameters);
}

또한 executeAndReturnKey () API를 사용하여 데이터베이스가 기본 키를 생성 할 수 있습니다. 또한 실제 자동 생성 된 열을 구성해야합니다.

SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                        .withTableName("EMPLOYEE")
                                        .usingGeneratedKeyColumns("ID");

Number id = simpleJdbcInsert.executeAndReturnKey(parameters);
System.out.println("Generated id - " + id.longValue());

마지막으로 BeanPropertySqlParameterSourceMapSqlParameterSource를 사용하여이 데이터를 전달할 수도 있습니다 .

5.2. SimpleJdbcCall을 사용한 저장 프로 시저

저장 프로 시저 실행도 살펴 보겠습니다.

SimpleJdbcCall 추상화를 사용할 것입니다 .

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)
		                     .withProcedureName("READ_EMPLOYEE");
public Employee getEmployeeUsingSimpleJdbcCall(int id) {
    SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);
    Map<String, Object> out = simpleJdbcCall.execute(in);

    Employee emp = new Employee();
    emp.setFirstName((String) out.get("FIRST_NAME"));
    emp.setLastName((String) out.get("LAST_NAME"));

    return emp;
}

6. 배치 작업

또 다른 간단한 사용 사례는 여러 작업을 일괄 처리하는 것입니다.

6.1. JdbcTemplate을 사용한 기본 배치 작업

사용 JdbcTemplate을 , 일괄 작업은 비아 실행할 수 의 batchUpdate () API.

여기서 흥미로운 부분은 간결하지만 매우 유용한 BatchPreparedStatementSetter 구현입니다.

public int[] batchUpdateUsingJdbcTemplate(List<Employee> employees) {
    return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",
        new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1, employees.get(i).getId());
                ps.setString(2, employees.get(i).getFirstName());
                ps.setString(3, employees.get(i).getLastName());
                ps.setString(4, employees.get(i).getAddress();
            }
            @Override
            public int getBatchSize() {
                return 50;
            }
        });
}

6.2. NamedParameterJdbcTemplate을 사용한 일괄 작업

NamedParameterJdbcTemplatebatchUpdate () API를 사용하여 작업을 일괄 처리하는 옵션도 있습니다 .

이 API는 이전 API보다 간단합니다. 따라서 매개 변수 값을 설정하기위한 내부 준비된 명령문 setter가 있으므로 매개 변수를 설정하기 위해 추가 인터페이스를 구현할 필요가 없습니다.

대신 매개 변수 값 SqlParameterSource 배열로 batchUpdate () 메서드에 전달할 수 있습니다 .

SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
    "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);
return updateCounts;

7. 스프링 부트를 사용한 스프링 JDBC

Spring Boot는 관계형 데이터베이스와 함께 JDBC를 사용하기위한 스타터 spring-boot-starter-jdbc 를 제공합니다.

모든 Spring Boot 스타터와 마찬가지로 이것은 우리가 애플리케이션을 빠르게 시작하고 실행하는 데 도움이됩니다.

7.1. Maven 의존성

우리는해야합니다  스프링 부팅 스타터 JDBC 기본 하나 같은 의존성을. 또한 사용할 데이터베이스에 대한 의존성도 필요합니다. 우리의 경우 이것은 MySQL입니다 .

<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>

7.2. 구성

Spring Boot는 데이터 소스를 자동으로 구성합니다. 속성 파일에 속성을 제공하면 됩니다.

spring.datasource.url=jdbc:mysql://localhost:3306/springjdbc
spring.datasource.username=guest_user
spring.datasource.password=guest_password

그리고 그게 다야. 우리의 응용 프로그램은 이러한 구성 만 수행하여 실행됩니다. 이제 다른 데이터베이스 작업에 사용할 수 있습니다.

표준 Spring 애플리케이션에 대해 이전 섹션에서 본 명시 적 구성이 이제 Spring Boot 자동 구성의 일부로 포함됩니다.

8. 결론

이 기사에서 우리는 Spring Framework의 JDBC 추상화를 살펴 보았다. Spring JDBC에서 제공하는 다양한 기능을 실제 예제와 함께 다루었습니다.

또한 Spring Boot JDBC 스타터를 사용하여 Spring JDBC를 빠르게 시작하는 방법도 살펴 보았습니다.

예제의 소스 코드는 GitHub에서 사용할 수 있습니다 .