본문 바로가기
공식문서

[Spring Docs] Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling #1

by sangyunpark99 2025. 3. 28.

이번글은 공식 문서에서 소개하는 Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling에 대해 정리했습니다.

 

This section covers how to use the JDBC core classes to control basic JDBC processing, including error handling. It includes the following topics

이 섹션은 기본적인 JDBC 처리를 제어하기 위해 JDBC 코어 클래스를 어떻게 사용하는지를 다루며, 오류 처리도 포함됩니다. 다음 주제들을 포함합니다.

 

Using JdbcTemplate

JdbcTemplate is the central class in the JDBC core package. It handles the creation and release of resources, which helps you avoid common errors, such as forgetting to close the connection. It performs the basic tasks of the core JDBC workflow (such as statement creation and execution), leaving application code to provide SQL and extract results. The JdbcTemplate class:

  • Runs SQL queries
  • Updates statements and stored procedure calls
  • Performs iteration over ResultSet instances and extraction of returned parameter values.
  • Catches JDBC exceptions and translates them to the generic, more informative, exception hierarchy defined in the org.springframework.dao package. (See Consistent Exception Hierarchy.)

jdbcTemplate은 JDBC 코어 패키지에서 중심이 되는 클래스입니다. 이 클래스는 자원의 생성과 해제를 처리하여, 연결(connection)을 닫는 것을 잊는 것과 같은 일반적인 오류를 피할 수 있도록 도와줍니다. JdbcTemplate은 핵심 JDBC 워크플로우의 기본 작업(예: statement 생성 및 실행)을 수행하며, 애플리케이션 코드는 SQL 제공 및 결과 추출만 하면 됩니다. JdbcTemplate 클래스는 다음과 같은 작업을 수행합니다.

 

 SQL 쿼리를 실행합니다.

 update 문과 저장 프로시저 호출을 수행합니다.

 ResultSet 인스턴스에 대해 반복(iteration)을 수행하고 반환된 파라미터 값을 추출합니다.

 JDBC 예외를 포착하여 org.springframework.dao 패키지에 정의된, 보다 일반적이고 정보가 풍부한 예외 계층 구조로 변환합니다. (일관된 예외 계층 구조 참조)

 

저장 프로시저(Stored Procedure)는 데이터베이스에 미리 저장된 SQL 구문들의 집합입니다.

 

When you use the JdbcTemplate for your code, you need only to implement callback interfaces, giving them a clearly defined contract. Given a Connection provided by the JdbcTemplate class, the PreparedStatementCreator callback interface creates a prepared statement, providing SQL and any necessary parameters. The same is true for the CallableStatementCreator interface, which creates callable statements. The RowCallbackHandler interface extracts values from each row of a ResultSet.

You can use JdbcTemplate within a DAO implementation through direct instantiation with a DataSource reference, or you can configure it in a Spring IoC container and give it to DAOs as a bean reference.

 

코드에서 JdbcTemplate을 사용할 때는, 콜백 인터페이스를 구현하기만 하면 되며, 이 인터페이스들은 명확하게 정의된 계약(Contract)을 제공합니다. JdbcTemplate 클래스가 제공하는 Connection을 바탕으로, PreparedStatementCreator 콜백 인터페이스는 SQL 및 필요한 파라미터를 제공하여 prepared statement를 생성합니다. CallableStatementCreator 인터페이스도 마찬가지로 callable statement를 생성합니다. RowCallbackHandler 인터페이스는 ResultSet의 각 행에서 값을 추출합니다.

 

JdbcTemplate은 DAO 구현 내에서 DataSource 참조를 통해 직접 인스턴스화하여 사용할 수 있으며, 또는 Spring IoC 컨테이너에서 설정한 후, DAO에 빈(bean) 참조로 전달하여 사용할 수도 있습니다.

 


JdbcTemplate의 핵심 기능

1. DB 연결과 자원 해제 자동 처리
→ 커넥션을 직접 열고 닫을 필요 없음

2. SQL 실행 및 결과 처리 지원
→ 쿼리 실행, 업데이트, 저장 프로시저 호출 등

3. 예외 처리 일관성 제공
→ SQLException을 Spring의 DataAccessException 계층으로 변환

4. 콜백 인터페이스로 유연한 처리
→ PreparedStatementCreator, RowCallbackHandler 등 사용

5. DAO에서 쉽게 사용 가능
→ 직접 생성하거나 Spring 빈으로 주입

JdbcTemplate은 반복적인 JDBC 작업을 대신 처리해주고, 개발자는 SQL과 결과 처리만 집중하면 됩니다.

 

The DataSource should always be configured as a bean in the Spring IoC container. In the first case the bean is given to the service directly; in the second case it is given to the prepared template.

 

DataSource는 항상 Spring IoC 컨테이너에서 빈(bean)으로 설정되어야 합니다. 첫 번째 경우에는 이 빈이 서비스에 직접 전달되고, 두 번째 경우에는 준비된 템플릿에 전달됩니다.

DataSource는 IoC 컨테이너에서 빈으로 등록해야 하고, 그걸 어디에 먼저 주느냐에 따라 방식이 달라진다.
서비스에 직접 줄 수도 있고, JdbcTemplate에 먼저 줘서, JdbcTemplate을 사용하는 구조로 할 수도 있습니다.
두 방식 모두 가능하지만, 현재는 두 번째 방식(JdbcTemplate에 먼저 주고, 이를 DAO 서비스에 주입)하는 걸 더 많이 합니다.

 

All SQL issued by this class is logged at the DEBUG level under the category corresponding to the fully qualified class name of the template instance (typically JdbcTemplate, but it may be different if you use a custom subclass of the JdbcTemplate class).

The following sections provide some examples of JdbcTemplate usage. These examples are not an exhaustive list of all of the functionality exposed by the JdbcTemplate. See the attendant javadoc for that.

 

이 클래스에 의해 실행되는 모든 SQL은 템플릿 인스턴스의 완전한 클래스 이름에 해당하는 카테고리 아래에서 DEBUG 레벨로 로그에 기록됩니다 (일반적으로는 JdbcTemplate이지만, JdbcTemplate 클래스의 사용자 정의 하위 클래스를 사용할 경우에는 다를 수 있습니다).

 

다음 섹션에서는 JdbcTemplate 사용 예시 몇 가지를 제공합니다.

이 예시들은 JdbcTemplate이 제공하는 모든 기능을 포괄하는 것은 아닙니다.

자세한 내용은 관련 javadoc을 참조하세요.

 

Querying (SELECT)

The following query gets the number of rows in a relation:

다음 쿼리는 하나의 릴레이션(테이블)에서 행(row)의 수를 가져옵니다.

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);

 

 

The following query uses a bind variable:

다음 쿼리는 바인드 변수를 사용합니다.

int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
		"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");

 

The following query looks for a String:

다음 쿼리는 문자열(String)을 찾습니다.

String lastName = this.jdbcTemplate.queryForObject(
		"select last_name from t_actor where id = ?",
		String.class, 1212L);

 

The following query finds and populates a single domain object:

다음 쿼리는 하나의 도메인 객체를 찾아서 값을 채웁니다.

Actor actor = jdbcTemplate.queryForObject(
		"select first_name, last_name from t_actor where id = ?",
		(resultSet, rowNum) -> {
			Actor newActor = new Actor();
			newActor.setFirstName(resultSet.getString("first_name"));
			newActor.setLastName(resultSet.getString("last_name"));
			return newActor;
		},
		1212L);

 

The following query finds and populates a list of domain objects:

다음 쿼리는 도메인 객체들의 목록을 찾아서 값을 채웁니다.

List<Actor> actors = this.jdbcTemplate.query(
		"select first_name, last_name from t_actor",
		(resultSet, rowNum) -> {
			Actor actor = new Actor();
			actor.setFirstName(resultSet.getString("first_name"));
			actor.setLastName(resultSet.getString("last_name"));
			return actor;
		});

 

 

If the last two snippets of code actually existed in the same application, it would make sense to remove the duplication present in the two RowMapper lambda expressions and extract them out into a single field that could then be referenced by DAO methods as needed. For example, it may be better to write the preceding code snippet as follows:

 

만약 마지막 두 코드 조각이 실제로 같은 애플리케이션에 존재한다면, 두 개의 RowMapper 람다 표현식에 존재하는 중복을 제거하고, 그것들을 하나의 필드로 추출하여 DAO 메서드에서 필요할 때 참조하는 것이 타당할 것입니다. 예를 들어, 앞서 나온 코드 조각은 다음과 같이 작성하는 것이 더 나을 수 있습니다.

private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
	Actor actor = new Actor();
	actor.setFirstName(resultSet.getString("first_name"));
	actor.setLastName(resultSet.getString("last_name"));
	return actor;
};

public List<Actor> findAllActors() {
	return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
}

 

Updating (INSERT, UPDATE, and DELETE) with JdbcTemplate

You can use the update(..) method to perform insert, update, and delete operations. Parameter values are usually provided as variable arguments or, alternatively, as an object array.

 

update(..) 메서드는 insert, update, delete 작업을 수행하는 데 사용할 수 있습니다. 파라미터 값은 보통 가변 인자(variable arguments)로 제공되거나, 또는 객체 배열(object array)로 제공됩니다.

 

The following example inserts a new entry:

다음 예제는 새로운 항목을 삽입합니다.

this.jdbcTemplate.update(
		"insert into t_actor (first_name, last_name) values (?, ?)",
		"Leonor", "Watling");

 

The following example updates an existing entry:

다음 예제는 기존 항목을 업데이트합니다.

this.jdbcTemplate.update(
		"update t_actor set last_name = ? where id = ?",
		"Banjo", 5276L);

 

The following example deletes an entry:

다음 예제는 항목을 삭제합니다.

this.jdbcTemplate.update(
		"delete from t_actor where id = ?",
		Long.valueOf(actorId));

 

 

Other JdbcTemplate Operations

You can use the execute(..) method to run any arbitrary SQL. Consequently, the method is often used for DDL statements. It is heavily overloaded with variants that take callback interfaces, binding variable arrays, and so on. The following example creates a table:

 

execute(..) 메서드는 임의의 SQL을 실행하는 데 사용할 수 있습니다. 따라서 이 메서드는 종종 DDL 문을 실행하는 데 사용됩니다. 이 메서드는 콜백 인터페이스, 바인드 변수 배열 등을 받는 다양한 형태로 과부하(overload)되어 있습니다. 다음 예제는 테이블을 생성합니다.

this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

 

The following example invokes a stored procedure:

다음 예제는 저장 프로시저를 호출합니다.

this.jdbcTemplate.update(
		"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
		Long.valueOf(unionId));

 

 

JdbcTemplate Best Practices

Instances of the JdbcTemplate class are thread-safe, once configured. This is important because it means that you can configure a single instance of a JdbcTemplate and then safely inject this shared reference into multiple DAOs (or repositories). The JdbcTemplate is stateful, in that it maintains a reference to a DataSource, but this state is not conversational state.

 

JdbcTemplate 클래스의 인스턴스는 한 번 설정되면 스레드에 안전(thread-safe)합니다.

이것은 중요한데, 그 이유는 JdbcTemplate 인스턴스를 하나만 설정한 뒤, 이 공유된 참조를 여러 DAO(또는 레포지토리)에 안전하게 주입할 수 있다는 것을 의미하기 때문입니다.

JdbcTemplateDataSource에 대한 참조를 유지한다는 점에서 상태(state)를 가지는 객체이지만, 이 상태는 대화형 상태(conversational state)는 아닙니다

A common practice when using the JdbcTemplate class (and the associated NamedParameterJdbcTemplate class) is to configure a DataSource in your Spring configuration file and then dependency-inject that shared DataSource bean into your DAO classes. The JdbcTemplate is created in the setter for the DataSource or in the constructor. This leads to DAOs that resemble the following:

 

JdbcTemplate 클래스(또는 관련된 NamedParameterJdbcTemplate 클래스)를 사용할 때의 일반적인 관행은, Spring 설정 파일에 DataSource를 설정하고, 공유된 DataSource 빈을 DAO 클래스에 의존성 주입하는 것입니다. JdbcTemplateDataSource의 세터 메서드나 생성자 안에서 생성됩니다.

public class JdbcCorporateEventDao implements CorporateEventDao {

	private final JdbcTemplate jdbcTemplate;

	public JdbcCorporateEventDao(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}

 

 

The following example shows the corresponding configuration:

다음 예제는 이에 해당하는 설정을 보여줍니다.

@Bean
JdbcCorporateEventDao corporateEventDao(DataSource dataSource) {
	return new JdbcCorporateEventDao(dataSource);
}

@Bean(destroyMethod = "close")
BasicDataSource dataSource() {
	BasicDataSource dataSource = new BasicDataSource();
	dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
	dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}

 

An alternative to explicit configuration is to use component-scanning and annotation support for dependency injection. In this case, you can annotate the class with @Repository (which makes it a candidate for component-scanning). The following example shows how to do so:

명시적인 설정에 대한 대안으로, 컴포넌트 스캔과 의존성 주입을 위한 애노테이션 지원을 사용할 수 있습니다. 이 경우, 해당 클래스에 @Repository 애노테이션을 붙이면 컴포넌트 스캔의 후보가 됩니다. 다음 예제는 이를 수행하는 방법을 보여줍니다.

@Repository
public class JdbcCorporateEventRepository implements CorporateEventRepository {

	private JdbcTemplate jdbcTemplate;

	// Implicitly autowire the DataSource constructor parameter
	public JdbcCorporateEventRepository(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	// JDBC-backed implementations of the methods on the CorporateEventRepository follow...
}

 

Spring에서 컴포넌트 스캔을 사용하고 DataSource를 주입할 때, application.yml (또는 application.properties)에 작성된 설정 정보를 자동으로 읽어서 DataSource를 구성해 줍니다.

 

The following example shows the corresponding configuration

다음 예제는 이에 해당하는 설정을 보여줍니다.

@Configuration
@ComponentScan("org.example.jdbc")
public class JdbcCorporateEventRepositoryConfiguration {

	@Bean(destroyMethod = "close")
	BasicDataSource dataSource() {
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
		dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
		dataSource.setUsername("sa");
		dataSource.setPassword("");
		return dataSource;
	}

}

 

If you use Spring’s JdbcDaoSupport class and your various JDBC-backed DAO classes extend from it, your sub-class inherits a setDataSource(..) method from the JdbcDaoSupport class. You can choose whether to inherit from this class. The JdbcDaoSupport class is provided as a convenience only.

 

Spring의 JdbcDaoSupport 클래스를 사용하고, 여러분의 다양한 JDBC 기반 DAO 클래스들이 이 클래스를 상속한다면, 서브클래스는 JdbcDaoSupport 클래스로부터 setDataSource(..) 메서드를 상속받게 됩니다. 이 클래스를 상속할지는 선택 사항입니다. JdbcDaoSupport 클래스는 단지 편의를 위해 제공되는 것입니다.

 

Regardless of which of the above template initialization styles you choose to use (or not), it is seldom necessary to create a new instance of a JdbcTemplate class each time you want to run SQL. Once configured, a JdbcTemplate instance is thread-safe. If your application accesses multiple databases, you may want multiple JdbcTemplate instances, which requires multiple DataSources and, subsequently, multiple differently configured JdbcTemplate instances.

 

위에서 언급한 템플릿 초기화 방식들 중 어떤 것을 사용하든(혹은 사용하지 않든) 간에, SQL을 실행할 때마다 JdbcTemplate 클래스의 새 인스턴스를 생성할 필요는 거의 없습니다.

한 번 설정되면, JdbcTemplate 인스턴스는 스레드에 안전합니다.

애플리케이션이 여러 데이터베이스에 접근해야 하는 경우, 여러 개의 JdbcTemplate 인스턴스가 필요할 수 있으며, 이는 여러 개의 DataSource가 필요하고, 그에 따라 각각 다르게 설정된 여러 JdbcTemplate 인스턴스가 필요함을 의미합니다.