이번글은 공식 문서에서 소개하는 Controlling Database Connection에 대해 정리했습니다.
Using DataSource
Spring obtains a connection to the database through a DataSource. A DataSource is part of the JDBC specification and is a generalized connection factory. It lets a container or a framework hide connection pooling and transaction management issues from the application code. As a developer, you need not know details about how to connect to the database. That is the responsibility of the administrator who sets up the datasource. You most likely fill both roles as you develop and test code, but you do not necessarily have to know how the production data source is configured.
Spring은 DataSource를 통해 데이터베이스와 연결을 획득합니다. DataSource는 JDBC 명세의 일부로, 일반화된 커넥션 팩토리입니다. 이는 컨테이너나 프레임워크가 커넥션 풀링과 트랜잭션 관리와 같은 문제를 애플리케이션 코드로부터 감출 수 있도록 해줍니다. 개발자인 여러분은 데이터베이스에 어떻게 연결하는지에 대한 세부 사항을 꼭 알 필요는 없습니다. 이러한 설정은 데이터 소스를 구성하는 관리자의 책임입니다. 여러분이 개발과 테스트를 할 때는 개발자이자 관리자 역할을 동시에 수행할 가능성이 크지만, 운영 환경에서 데이터 소스가 어떻게 설정되어 있는지는 꼭 알 필요는 없습니다.
When you use Spring’s JDBC layer, you can obtain a data source from JNDI, or you can configure your own with a connection pool implementation provided by a third party. Traditional choices are Apache Commons DBCP and C3P0 with bean-style DataSource classes; for a modern JDBC connection pool, consider HikariCP with its builder-style API instead.
Spring의 JDBC 계층을 사용할 때, JNDI로부터 데이터 소스를 얻을 수 있으며, 또는 제3자가 제공하는 커넥션 풀 구현을 사용하여 자신만의 데이터 소스를 구성할 수도 있습니다. 전통적인 선택지로는 bean 스타일의 DataSource 클래스를 사용하는 Apache Commons DBCP와 C3P0가 있습니다. 좀 더 현대적인 JDBC 커넥션 풀을 원한다면, 빌더 스타일 API를 사용하는 HikariCP를 고려해 보세요.
You should use the DriverManagerDataSource and SimpleDriverDataSource classes (as included in the Spring distribution) only for testing purposes! Those variants do not provide pooling and perform poorly when multiple requests for a connection are made.
DriverManagerDataSource와 SimpleDriverDataSource 클래스(이들은 Spring 배포판에 포함되어 있음)는 오직 테스트 용도로만 사용해야 합니다 이러한 변형 클래스들은 커넥션 풀링을 제공하지 않으며, 여러 개의 커넥션 요청이 발생할 때 성능이 좋지 않습니다.
The following section uses Spring’s DriverManagerDataSource implementation. Several other DataSource variants are covered later.
To configure a DriverManagerDataSource:
- Obtain a connection with DriverManagerDataSource as you typically obtain a JDBC connection.
- Specify the fully qualified class name of the JDBC driver so that the DriverManager can load the driver class.
- Provide a URL that varies between JDBC drivers. (See the documentation for your driver for the correct value.)
- Provide a username and a password to connect to the database.
The following example shows how to configure a DriverManagerDataSource:
다음 섹션에서는 Spring의 DriverManagerDataSource 구현을 사용합니다. 여러 다른 DataSource 변형들은 이후에 다룰 예정입니다.
DriverManagerDataSource를 구성하려면 다음과 같이 합니다
• 일반적으로 JDBC 연결을 얻는 방식처럼 DriverManagerDataSource를 사용하여 연결을 획득합니다.
• DriverManager가 드라이버 클래스를 로드할 수 있도록, JDBC 드라이버의 완전한 클래스 이름(Fully Qualified Class Name) 을 지정합니다.
• JDBC 드라이버마다 다른 URL을 제공합니다. (정확한 값은 사용하는 드라이버의 문서를 참조하세요.)
• 데이터베이스에 연결하기 위한 사용자 이름과 비밀번호를 제공합니다.
다음 예제는 DriverManagerDataSource를 어떻게 구성하는지 보여줍니다.
@Bean
DriverManagerDataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
The next two examples show the basic connectivity and configuration for DBCP and C3P0. To learn about more options that help control the pooling features, see the product documentation for the respective connection pooling implementations.
The following example shows DBCP configuration:
다음 두 가지 예제는 DBCP와 C3P0의 기본적인 연결 및 설정 방법을 보여줍니다.
풀링 기능을 제어하는 데 도움이 되는 더 많은 옵션들을 알고 싶다면, 각 커넥션 풀링 구현체의 제품 문서를 참조하세요.
다음 예제는 DBCP 설정을 보여줍니다.
@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;
}
The following example shows C3P0 configuration:
다음 예제는 C3P0 설정을 보여줍니다.
@Bean(destroyMethod = "close")
ComboPooledDataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("org.hsqldb.jdbcDriver");
dataSource.setJdbcUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUser("sa");
dataSource.setPassword("");
return dataSource;
}
Using DataSourceUtils
The DataSourceUtils class is a convenient and powerful helper class that provides static methods to obtain connections from JNDI and close connections if necessary. It supports a thread-bound JDBC Connection with DataSourceTransactionManager but also with JtaTransactionManager and JpaTransactionManager.
Note that JdbcTemplate implies DataSourceUtils connection access, using it behind every JDBC operation, implicitly participating in an ongoing transaction.
DataSourceUtils 클래스는 JNDI로부터 커넥션을 얻고 필요할 경우 커넥션을 닫는 정적 메서드를 제공하는 편리하고 강력한 도우미 클래스입니다.
이 클래스는 DataSourceTransactionManager와 함께 스레드에 바인딩된 JDBC 커넥션을 지원하며,
JtaTransactionManager 및 JpaTransactionManager와도 함께 사용할 수 있습니다.
JdbcTemplate은 내부적으로 모든 JDBC 작업마다 DataSourceUtils를 사용하여 커넥션에 접근하므로,
진행 중인 트랜잭션에 암묵적으로 참여하게 된다는 점에 주의하세요.
JNDI(Java Native and Directory Interface)는 자바 애플리케이션이 이름을 통해 자원(리소스)을 찾을 수 있도록 해주는 표준 API 입니다.
Implementing SmartDataSource
The SmartDataSource interface should be implemented by classes that can provide a connection to a relational database. It extends the DataSource interface to let classes that use it query whether the connection should be closed after a given operation. This usage is efficient when you know that you need to reuse a connection.
SmartDataSource 인터페이스는 관계형 데이터베이스에 대한 커넥션을 제공할 수 있는 클래스들이 구현해야 하는 인터페이스입니다.
이 인터페이스는 DataSource 인터페이스를 확장하며, 이를 사용하는 클래스가 특정 작업 후에 커넥션을 닫아야 하는지 여부를 질의할 수 있도록 해줍니다. 이러한 사용 방식은 커넥션을 재사용해야 한다는 것을 알고 있는 경우에 효율적입니다.
Extending AbstractDataSource
AbstractDataSource is an abstract base class for Spring’s DataSource implementations. It implements code that is common to all DataSource implementations. You should extend the AbstractDataSource class if you write your own DataSource implementation.
AbstractDataSource는 Spring의 DataSource 구현을 위한 추상 기반 클래스입니다.
이 클래스는 모든 DataSource 구현에서 공통적으로 사용되는 코드를 구현하고 있습니다.
자신만의 DataSource 구현을 작성하려는 경우에는 이 AbstractDataSource 클래스를 상속해야 합니다.
Using SingleConnectionDataSource
The SingleConnectionDataSource class is an implementation of the SmartDataSource interface that wraps a single Connection that is not closed after each use. This is not multi-threading capable.
If any client code calls close on the assumption of a pooled connection (as when using persistence tools), you should set the suppressClose property to true. This setting returns a close-suppressing proxy that wraps the physical connection. Note that you can no longer cast this to a native Oracle Connection or a similar object.
SingleConnectionDataSource is primarily a test class. It typically enables easy testing of code outside an application server, in conjunction with a simple JNDI environment. In contrast to DriverManagerDataSource, it reuses the same connection all the time, avoiding excessive creation of physical connections.
SingleConnectionDataSource 클래스는 SmartDataSource 인터페이스의 구현체로,
하나의 커넥션을 감싸고 있으며, 사용 후에도 해당 커넥션을 닫지 않습니다. 이 클래스는 멀티스레드를 지원하지 않습니다.
클라이언트 코드가 풀링된 커넥션이라고 가정하고 close()를 호출하는 경우
(예: 퍼시스턴스 도구를 사용할 때처럼), suppressClose 속성을 true로 설정해야 합니다.
이 설정을 하면 실제 커넥션을 감싸는 close() 호출을 무시하는 프록시를 반환합니다.
단, 이 경우 더 이상 해당 커넥션을 Oracle의 네이티브 커넥션이나 이와 유사한 객체로 캐스팅할 수 없습니다.
SingleConnectionDataSource는 주로 테스트용 클래스입니다.
이는 애플리케이션 서버 외부에서의 코드 테스트를 단순한 JNDI 환경과 함께 쉽게 수행할 수 있도록 해줍니다.
DriverManagerDataSource와는 달리, 이 클래스는 항상 동일한 커넥션을 재사용하여 물리적인 커넥션이 과도하게 생성되는 것을 방지합니다.
Using DriverManagerDataSource
The DriverManagerDataSource class is an implementation of the standard DataSource interface that configures a plain JDBC driver through bean properties and returns a new Connection every time.
This implementation is useful for test and stand-alone environments outside of a Jakarta EE container, either as a DataSource bean in a Spring IoC container or in conjunction with a simple JNDI environment. Pool-assuming Connection.close() calls close the connection, so any DataSource-aware persistence code should work. However, using JavaBean-style connection pools (such as commons-dbcp) is so easy, even in a test environment, that it is almost always preferable to use such a connection pool over DriverManagerDataSource.
DriverManagerDataSource 클래스는 표준 DataSource 인터페이스의 구현체로,
빈 프로퍼티를 통해 일반적인 JDBC 드라이버를 구성하고, 호출할 때마다 새로운 Connection을 반환합니다.
이 구현은 Jakarta EE 컨테이너 외부의 테스트나 독립 실행형 환경에서 유용합니다.
예를 들어 Spring IoC 컨테이너 내의 DataSource 빈으로 사용하거나, 간단한 JNDI 환경과 함께 사용할 수 있습니다.
이 클래스는 커넥션 풀을 가정한 Connection.close() 호출이 실제로 커넥션을 닫기 때문에, DataSource를 인식하는 퍼시스턴스 코드가 정상적으로 작동할 수 있습니다.
그러나 테스트 환경에서도 JavaBean 스타일의 커넥션 풀(예: commons-dbcp)을 사용하는 것이 매우 쉽기 때문에,
대부분의 경우에는 DriverManagerDataSource보다 그러한 커넥션 풀을 사용하는 것이 더 바람직합니다.
커넥션 풀링은 미리 커넥션을 여러 개 만들어두고, 필요할 때 꺼내 쓰고, 다 쓰면 다시 돌려보내는 구조를 말합니다.
Using TransactionAwareDataSourceProxy
TransactionAwareDataSourceProxy is a proxy for a target DataSource. The proxy wraps that target DataSource to add awareness of Spring-managed transactions. In this respect, it is similar to a transactional JNDI DataSource, as provided by a Jakarta EE server.
TransactionAwareDataSourceProxy는 대상 DataSource에 대한 프록시(proxy)입니다.
이 프록시는 대상 DataSource를 감싸서 Spring이 관리하는 트랜잭션에 대한 인식 기능을 추가합니다.
이 점에서, 이는 Jakarta EE 서버가 제공하는 트랜잭션 지원 JNDI DataSource와 유사합니다.
트랜잭션 인식 기능이란, 현재 Spring 트랜잭션이 열려 있는지를 인식하고, 그에 맞게 커넥션을 처리하는 능력을 말합니다.
TransactionAwareDataSourceProxy는 Spring 트랜잭션이 이미 시작된 상태인지 확인하고, 이미 열려 있는 트랜잭션 커넥션이 있는 경우 그걸 반환하고, 커넥션을 close()해도 진짜로 닫지 않고, 트랜잭션 종료 시까지 유지해줍니다.
It is rarely desirable to use this class, except when already existing code must be called and passed a standard JDBC DataSource interface implementation. In this case, you can still have this code be usable and, at the same time, have this code participating in Spring managed transactions. It is generally preferable to write your own new code by using the higher level abstractions for resource management, such as JdbcTemplate or DataSourceUtils.
이 클래스를 사용하는 것은, 이미 존재하는 코드에서 표준 JDBC DataSource 인터페이스 구현체를 호출하고 전달해야 하는 경우를 제외하고는 거의 바람직하지 않습니다.
이러한 경우에는, 해당 코드가 Spring이 관리하는 트랜잭션에 참여하면서도 여전히 사용할 수 있도록 만들 수 있습니다.
일반적으로는 리소스 관리를 위한 더 높은 수준의 추상화, 예를 들어 JdbcTemplate이나 DataSourceUtils를 사용하여
새로운 코드를 직접 작성하는 것이 더 바람직합니다.
Using DataSourceTransactionManager / JdbcTransactionManager
The DataSourceTransactionManager class is a PlatformTransactionManager implementation for a single JDBC DataSource. It binds a JDBC Connection from the specified DataSource to the currently executing thread, potentially allowing for one thread-bound Connection per DataSource.
DataSourceTransactionManager 클래스는 하나의 JDBC DataSource를 위한 PlatformTransactionManager 구현체입니다.
이 클래스는 지정된 DataSource로부터 가져온 JDBC 커넥션을 현재 실행 중인 스레드에 바인딩합니다.
이로 인해 DataSource 하나당 스레드에 바인딩된 커넥션이 하나 존재할 수 있게 됩니다.
Application code is required to retrieve the JDBC Connection through DataSourceUtils.getConnection(DataSource) instead of Java EE’s standard DataSource.getConnection. It throws unchecked org.springframework.dao exceptions instead of checked SQLExceptions. All framework classes (such as JdbcTemplate) use this strategy implicitly. If not used with a transaction manager, the lookup strategy behaves exactly like DataSource.getConnection and can therefore be used in any case.
애플리케이션 코드는 Java EE의 표준 DataSource.getConnection() 대신, DataSourceUtils.getConnection(DataSource)를 통해 JDBC 커넥션을 획득해야 합니다. 이 방식은 SQLException(체크 예외)이 아닌 org.springframework.dao 패키지의 언체크 예외를 발생시킵니다. JdbcTemplate과 같은 모든 Spring 프레임워크 클래스들은 이 전략을 내부적으로 사용합니다.
트랜잭션 매니저 없이 사용할 경우, 이 조회 방식은 DataSource.getConnection()과 동일하게 동작하므로 언제든 사용 가능합니다.
The DataSourceTransactionManager class supports savepoints (PROPAGATION_NESTED), custom isolation levels, and timeouts that get applied as appropriate JDBC statement query timeouts. To support the latter, application code must either use JdbcTemplate or call the DataSourceUtils.applyTransactionTimeout(..) method for each created statement.
DataSourceTransactionManager는 세이브포인트 (PROPAGATION_NESTED), 사용자 지정 격리 수준, 트랜잭션 타임아웃 기능을 제공합니다.
이 중 타임아웃은 JDBC 쿼리의 statement-level timeout으로 반영됩니다.
이 기능을 제대로 사용하려면 애플리케이션 코드에서 JdbcTemplate을 사용하거나,
각 쿼리문에 대해 DataSourceUtils.applyTransactionTimeout(..) 메서드를 호출해야 합니다.
You can use DataSourceTransactionManager instead of JtaTransactionManager in the single-resource case, as it does not require the container to support a JTA transaction coordinator. Switching between these transaction managers is just a matter of configuration, provided you stick to the required connection lookup pattern. Note that JTA does not support savepoints or custom isolation levels and has a different timeout mechanism but otherwise exposes similar behavior in terms of JDBC resources and JDBC commit/rollback management.
단일 자원만 사용하는 경우에는 JtaTransactionManager 대신 DataSourceTransactionManager를 사용할 수 있습니다,
왜냐하면 이 매니저는 컨테이너가 JTA 트랜잭션 코디네이터를 지원할 필요가 없기 때문입니다.
이 두 트랜잭션 매니저 간의 전환은 단순히 설정 변경으로 해결될 수 있으며,
단, 필수적인 커넥션 조회 방식(DataSourceUtils 등)을 지켜야 합니다.
JTA는 다음 기능들을 지원하지 않습니다:
• 세이브포인트
• 사용자 정의 격리 수준
또한 타임아웃 처리 방식도 다르지만,
JDBC 자원 관리나 커밋/롤백 동작 면에서는 유사한 동작을 제공합니다.
For JTA-style lazy retrieval of actual resource connections, Spring provides a corresponding DataSource proxy class for the target connection pool: see LazyConnectionDataSourceProxy. This is particularly useful for potentially empty transactions without actual statement execution (never fetching an actual resource in such a scenario), and also in front of a routing DataSource which means to take the transaction-synchronized read-only flag and/or isolation level into account (for example, IsolationLevelDataSourceRouter).
JTA 방식처럼 실제 리소스 커넥션을 지연(lazy)하여 가져오고 싶다면,
Spring은 해당 커넥션 풀을 감싸는 LazyConnectionDataSourceProxy 프록시 클래스를 제공합니다.
이 클래스는 특히 다음과 같은 상황에서 유용합니다:
• 실제로 쿼리를 실행하지 않는 빈 트랜잭션 (리소스를 가져올 필요 없음)
• 라우팅 DataSource 앞단에서 사용 (예: 읽기 전용 여부, 격리 수준 등을 트랜잭션과 동기화할 필요가 있는 경우)
예시로는 IsolationLevelDataSourceRouter가 있습니다.
LazyConnectionDataSourceProxy also provides special support for a read-only connection pool to use during a read-only transaction, avoiding the overhead of switching the JDBC Connection’s read-only flag at the beginning and end of every transaction when fetching it from the primary connection pool (which may be costly depending on the JDBC driver).
LazyConnectionDataSourceProxy는 읽기 전용 트랜잭션 중 사용할 수 있도록
읽기 전용 전용 커넥션 풀에 대한 특별한 지원도 제공합니다.
이를 통해 다음과 같은 비용을 줄일 수 있습니다:
• 매 트랜잭션마다 JDBC 커넥션의 read-only 플래그를 켜고 끄는 오버헤드
이러한 설정은 일부 JDBC 드라이버에서는 꽤 비싸게 작동하기 때문에,
해당 플래그를 매번 조작하지 않도록 최적화하는 데에 도움이 됩니다.
1. DataSourceTransactionManager는 하나의 JDBC DataSource에 대한 트랜잭션을 관리하며, 커넥션을 현재 스레드에 바인딩한다.
2. 애플리케이션은 커넥션을 DataSourceUtils.getConnection()을 통해 얻어야 하며, Spring은 SQLException 대신 런타임 예외를 사용한다.
3. 이 트랜잭션 매니저는 세이브포인트, 격리 수준, 쿼리 타임아웃을 지원하며, 타임아웃은 별도 메서드나 JdbcTemplate을 통해 적용해야 한다.
4. 단일 자원 환경에서는 JTA보다 DataSourceTransactionManager가 간단하며, JTA는 세이브포인트나 사용자 격리 수준을 지원하지 않는다.
5. Spring의 LazyConnectionDataSourceProxy는 실제 커넥션을 지연 로딩하며, 빈 트랜잭션이나 라우팅 데이터소스 앞단에서 유용하다.
6. LazyConnectionDataSourceProxy는 읽기 전용 트랜잭션에서 read-only 플래그 설정 비용을 줄이기 위해 별도의 커넥션 풀을 사용할 수 있게 해준다.
As of 5.3, Spring provides an extended JdbcTransactionManager variant which adds exception translation capabilities on commit/rollback (aligned with JdbcTemplate). Where DataSourceTransactionManager will only ever throw TransactionSystemException (analogous to JTA), JdbcTransactionManager translates database locking failures etc to corresponding DataAccessException subclasses. Note that application code needs to be prepared for such exceptions, not exclusively expecting TransactionSystemException. In scenarios where that is the case, JdbcTransactionManager is the recommended choice.
5.3 버전부터, Spring은 예외 변환 기능을 추가한 확장된 JdbcTransactionManager 변형 클래스를 제공합니다.
이 클래스는 커밋/롤백 시점에서의 예외를 변환해주며, 이는 JdbcTemplate의 동작과 일치합니다.
DataSourceTransactionManager는 항상 TransactionSystemException만을 던지지만
(JTA의 방식과 유사하게),
JdbcTransactionManager는 데이터베이스 잠금 실패 등의 오류를 DataAccessException의 하위 클래스들로 변환합니다.
따라서 애플리케이션 코드는 이러한 예외들(TransactionSystemException만이 아닌 여러 예외)에
대비되어 있어야 하며,
그러한 상황이라면 JdbcTransactionManager를 사용하는 것이 권장되는 선택입니다.
JDBC나 JPA는 기본적으로 SQLException을 던지는데, 이건 체크드 예외입니다.
체크드 예외는 throws로 명시하거나 try-catch로 감싸야 해서, 비즈니스 로직에 많은 예외 처리 코드가 들어가게 됩니다.
Spring은 이를 언체크드 예외인 DataAccessException 계열로 변환해서, 서비스 계층이 깔끔하게 예외를 위임하고
필요한 곳에서만 명시적으로 처리할 수 있도록 해줍니다.
In terms of exception behavior, JdbcTransactionManager is roughly equivalent to JpaTransactionManager and also to R2dbcTransactionManager, serving as an immediate companion/replacement for each other. DataSourceTransactionManager on the other hand is equivalent to JtaTransactionManager and can serve as a direct replacement there.
예외 처리 동작 측면에서 볼 때, JdbcTransactionManager는 JpaTransactionManager 및 R2dbcTransactionManager와 대체로 유사하며, 서로 즉각적인 보완자 또는 대체재로 사용할 수 있습니다.
반면, DataSourceTransactionManager는 JtaTransactionManager와 유사하며, 직접적인 대체재로 사용될 수 있습니다.
정리
DataSourceTransactionManager | 단일 JDBC 연결을 사용하는 경우 |
JdbcTransactionManager | JDBC + 예외 세분화 (Spring 5.3부터) |
JpaTransactionManager | JPA (Hibernate) 기반 환경 |
JtaTransactionManager | 분산 트랜잭션이 필요한 경우 (여러 DB 또는 MQ 등) |
R2dbcTransactionManager | 리액티브 환경에서 사용 |
'공식문서' 카테고리의 다른 글
[Spring Docs] Simplifying JDBC Operations with the SimpleJdbc Classes (0) | 2025.04.09 |
---|---|
[Spring Docs] JDBC Batch Operations (0) | 2025.04.04 |
Garbage Collector #4 (0) | 2025.04.01 |
[Spring Docs] Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling #3 (0) | 2025.04.01 |
[Spring Docs] Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling #2 (0) | 2025.03.30 |