Hibernate의 connection release mode는 JDBC 커넥션을 언제 획득하고 언제 풀에 반환할지를 결정하는 전략이다.
이 설정은 단독으로 동작하는 것이 아니라, Spring의 @Transactional 전파 전략과 맞물려서 실제 커넥션 생명주기를 결정한다.
이 글에서는 Spring Boot + JPA 환경에서 커넥션이 풀에서 빠져나가고 돌아오는 과정을 코드 레벨까지 확인한다.
1. Connection Release Mode란?
1. Hibernate의 5가지 커넥션 획득/반환 모드
Hibernate의 커넥션 관리 전략은 PhysicalConnectionHandlingMode라는 enum으로 정의된다. 이 enum은 두 가지 축의 조합으로 구성된다.
획득 시점 (Connection Acquisition Mode)
- IMMEDIATE: Session 생성 시점에 즉시 커넥션 획득
- AS_NEEDED (DELAYED): 실제 SQL이 필요한 시점까지 획득을 지연
반환 시점 (Connection Release Mode)
- ON_CLOSE (HOLD): Session이 닫힐 때까지 커넥션 유지
- AFTER_STATEMENT: 매 SQL 실행 후 즉시 반환
- AFTER_TRANSACTION: 트랜잭션 종료 후 반환
- BEFORE_TRANSACTION_COMPLETION: 트랜잭션 커밋/롤백 직전에 반환
이 축들의 조합으로 다음과 같은 모드들이 만들어진다:
| 모드 | 획득 | 반환 |
| DELAYED_ACQUISITION_AND_HOLD | 지연 | Session 종료 시 |
| DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT | 지연 | 매 SQL 후 |
| DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION | 지연 | 트랜잭션 후 |
| DELAYED_ACQUISITION_AND_RELEASE_BEFORE_TRANSACTION_COMPLETION | 지연 | 커밋/롤백 직전 |
| IMMEDIATE_ACQUISITION_AND_HOLD | 즉시 | Session 종료 시 |
Q. 왜 모드가 8개가 아닌 5개만 존재할까?
커넥션 획득이 즉시일 때를 봐보자.
커넥션을 세션 수립 시 즉시 획득했는데 세션 해제 전 커넥션을 해제하는 경우는 3가지이다.
세션 종료 전 조기 반환 모드 3가지
- AFTER_STATEMENT / AFTER_TRANSACTION / BEFORE_TRANSACTION_COMPLETION
그러면 커넥션을 세션 해제 전에 조기에 해제했고, 세션 내에서 다시 트랜잭션을 사용하는 경우 커넥션 획득이 어떻게 이뤄지는걸까?
이려면 커넥션을 세션 중간에 다시 획득하는 DELAYED 상황이라는 모순적 상황이 발생한다.
따라서, 세션 시작 시 커넥션 즉시 획득의 경우 세션 종료까지 커넥션을 유지하는 HOLD 모드와만 조합할 수 있게 되는 것이다.
그래서 모순적 모드 3개는 제공되지 않는다.
Q. 세션 수립 시 커넥션 획득 주의점 (feat. OSIV)
Hibernate 세션 수립 시점은 언제일까?
OSIV 상태에 따라 달라진다.
OSIV가 true라면 클라이언트의 HTTP 요청이 들어오는 순간, 서블릿 필터나 인터셉터 단에서 이미 Hibernate 세션이 생성된다. 그러면 커넥션 획득 모드가 즉시인 경우 DB 트랜잭션 등 비지니스 로직이 전혀 발생하지 않았는데도 먼저 DB 커넥션 풀에서 커넥션을 하나 빼오고 세션이 종료될 때까지 들고있게 된다.
이렇게 되면 매우 비효율적인 DB 커넥션 사용이 이뤄지게 되므로 커넥션 풀을 급격히 고갈시키고 성능 문제를 발생시키게 된다.
OSIV가 false인 경우는 @Transactional 이 달려있는 메소드부터 Hibernate 세션이 수립된다.
2. Hibernate 기본값 vs Spring Boot 기본값 — 흔한 오해
여기서 많은 개발자가 혼동하는 지점이 있다. Hibernate 자체의 기본값과 Spring Boot 환경에서의 기본값이 다르다.
- Hibernate 단독 (resource-local): DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION
지연 획득 트랜잭션 후 반환 - Spring Boot + JPA (via HibernateJpaVendorAdapter): DELAYED_ACQUISITION_AND_HOLD
지연 획득 세션 종료까지 유지
왜 Spring은 Hibernate의 기본값을 덮어쓸까? HibernateJpaVendorAdapter 소스 코드를 확인해보자.
// HibernateJpaVendorAdapter.java (Spring Framework)
private Map<String, Object> buildJpaPropertyMap(boolean connectionReleaseOnClose) {
Map<String, Object> jpaProperties = new HashMap<>();
// ...
if (connectionReleaseOnClose) {
jpaProperties.put(
"hibernate.connection.handling_mode",
PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_HOLD
);
}
}
위 코드의 `connectionReleaseOnClose`는 `HibernateJpaDialect`의 `prepareConnection` 플래그에서 오며, 이 값은 기본적으로 `true`다.
Spring은 `@Transactional`로 트랜잭션을 시작할 때 단순히 DB에 트랜잭션 시작 명령만 내리지 않는다. 내부적으로 JDBC Connection을 가져와 격리 수준(isolation level)과 readOnly 속성을 직접 설정하는 사전 작업(`prepareConnection`)을 수행한다.
Q. 만약 Hibernate가 트랜잭션 도중 커넥션을 풀에 반납해 버리면 어떻게 될까?
A. 동일한 논리적 트랜잭션 안에서 다음 쿼리를 실행(또는 트랜잭션 전파)할 때, 풀에서 완전히 새로운 물리적 커넥션을 꺼내오게 된다. 이 새로운 커넥션에는 Spring이 처음에 설정해 둔 JDBC 상태(격리 수준, readOnly 등)가 적용되어 있지 않기 때문에 애써 적용한 설정이 무용지물이 되는 현상이 발생하게 된다.
결국 Spring은 트랜잭션 생명주기 동안 자신이 직접 커넥션 설정을 온전히 통제하기 위해, "세션이 닫힐 때까지 원래 쓰던 커넥션을 유지하라(HOLD)"고 Hibernate의 기본값을 강제로 덮어쓰는 것이다.
핵심
Spring Boot + JPA 환경에서는 별도 설정 없이 DELAYED_ACQUISITION_AND_HOLD가 기본값이다.
커넥션은 Session(EntityManager)이 닫힐 때 반환된다.
이 글은 운영 환경 기반으로 spring.jpa.open-in-view=false 설정을 기본 전제로 한다. OSIV=ON 환경에서 특별히 다르게 동작하는 부분이 있을 때만 별도로 표시한다.
2. 트랜잭션 전파 전략과 커넥션 생명주기
Spring Boot의 기본 모드가 HOLD라는 것은 확인했다. 이제 @Transactional의 전파 전략에 따라 커넥션 생명주기가 어떻게 달라지는지 살펴보자.
1. REQUIRED — 트랜잭션 종료 시 반환
가장 일반적인 케이스다:
@Transactional // propagation = REQUIRED (기본값)
fun updateTemplate() {
repository.findById(1) // ← 커넥션 획득 (DELAYED)
repository.save(entity) // ← 같은 커넥션 유지
} // ← 트랜잭션 커밋 → EntityManager 종료 → 커넥션 반환
REQUIRED는 실제 트랜잭션을 시작한다.
actualTransactionActive는 true가 되고, EntityManager는 트랜잭션에 바인딩된다. 트랜잭션이 커밋(또는 롤백)되면 EntityManager가 닫히고, 그때 HOLD 모드에 따라 커넥션이 풀에 반환된다.
2. SUPPORTS — 메서드 종료 시 반환
이제 핵심적인 SUPPORTS 케이스다. 바깥에 트랜잭션이 없는 상태에서 SUPPORTS로 진입한 경우를 보자.
@Transactional(propagation = SUPPORTS, readOnly = true)
fun search(keyword: String) {
repository.findByKeyword(keyword) // EM 생성 → Conn 획득 → SELECT → EM close → Conn 반환
repository.findRelated(ids) // 또 새 EM 생성 → 새 Conn 획득 → ... → 반환
}
SUPPORTS는 기존 트랜잭션이 없으면 트랜잭션을 시작하지 않는다.
OSIV=FALS + SUPPORTS + outer Tx 없음 이라면 JpaTransactionManager.doBegin()이 호출되지 않는다.
따라서 TransactionSynchronizationManager에 EM이 바인딩되지 않는다. 매 repository 호출마다 EM을 새로 만들고 close 하게 된다.
3. HOLD 모드에서의 중요한 사실
Spring Boot 기본값인 HOLD 모드에서 OSIV가 TRUE라면 SUPPORTS라 하더라도 커넥션이 쿼리마다 반환되지 않는다. 첫 번째 쿼리에서 획득한 커넥션이 메서드 끝까지 유지된다.
OSIV에 따른 동작 차이 !중요!
- OSIV ON
- Session이 HTTP 요청 단위로 살아있어 HOLD가 의미를 갖는다. 첫 쿼리의 Connection이 메서드 종료 후에도 hold된다.
따라서, SUPPORTS의 커넥션 수립은 실제 쿼리가 발생할 때까지 DELAYED 되더라도 반환 시점은 세션 종료 시점이 된다. - OSIV OFF
- SharedEntityManagerCreator가 statement 단위로 EM을 생성/close하므로, 두 쿼리는 별도의 Connection을 사용한다. 사실상 AFTER_STATEMENT처럼 동작하게 된다.
이 원고에서는 OSIV = FALSE 가 설정되어있다고 가정하고 다룬다.
그렇다면 기본 전파전략인 REQUIRED 와 SUPPORTS의 차이점이 뭘까?
| REQUIRED | SUPPORTS (outer tx 없음) | |
| 획득 시점 | 메서드 진입 시 (setAutoCommit(false) 시점) | 매 쿼리 직전 |
| 반환 시점 | 트랜잭션 커밋 시 | 매 쿼리 직후 |
| Tx 오버헤드 | BEGIN/COMMIT 있음 | 없음 |
| EM 수명 | 메서드 전체 | statement 단위 |
- 첫째, 커넥션 획득 시점이 다르다.
REQUIRED는 물리적 DB 트랜잭션을 새로 시작(setAutoCommit(false))해야 하므로, 메서드 진입과 동시에 커넥션 풀에서 즉시 커넥션을 확보한다. 반면 SUPPORTS는 트랜잭션을 시작할 필요가 없으므로, 실제 첫 DB 쿼리가 날아가는 순간까지 커넥션 획득을 최대한 지연시킨다. - 둘째, 커넥션 유지 시간이 다르다.
REQUIRED는 커넥션을 메서드 진입 시점부터 메소드 종료 시점까지 커넥션을 풀에서 가져와서 들고있게된다. REPEATABLE READ라면 이 기간동안 MySQL DB의 언두로그를 읽게 된다. 따라서 롱 트랜잭션인 경우 언두로그가 오랫동안 쌓이는 부하가 있을 수 있다. 반면에 SUPPORTS는 쿼리직전에 가져와서 쿼리 직후에 바로 반환하므로 커넥션풀이 효율적으로 관리된다. 대신 그만큼 정합성이 떨어진다. - 셋째, 트랜잭션 제어 오버헤드가 다르다.
REQUIRED는 쿼리 외에도 데이터베이스에 명시적으로 BEGIN과 COMMIT (또는 ROLLBACK) 명령을 주고받아야 하므로 추가적인 오버헤드가 발생한다. 반면 SUPPORTS는 BEGIN/COMMIT 없이 실행되므로 DB 측 트랜잭션 관리 비용이 절약된다.
4. 하이버네이트 커넥션 관리 모드를 변경하고 싶다면?
hibernate.connection.handling_mode를 명시적으로 설정하면 된다.
spring:
jpa:
properties:
hibernate:
connection:
handling_mode: DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION
이렇게 설정하면, SUPPORTS에서 실제 트랜잭션이 없을 때(actualTransactionActive = false) Hibernate의 LogicalConnectionManagedImpl.afterTransaction()이 호출되어 커넥션이 반환된다. "트랜잭션 후 반환"인데 트랜잭션이 없으니, 사실상 쿼리 실행 후 바로 반환되는 효과가 나타난다.
단, 이 설정은 OSIV가 true일 때 prepareConnection과 충돌될 수 있으므로 주의해야한다.
Spring이 트랜잭션 시작 시 Connection에 직접 설정하는 격리 수준/readOnly 플래그 값이 있는데, 쿼리마다 커넥션이 바껴버리면 설정해둔게 무의미해지기 때문이다. 이를 사용하려면 prepareConnection의 동작을 함께 고려해야 한다.
물론 "쿼리마다 커넥션이 바뀌는" 이슈는 트랜잭션 외부에서 동일 세션이 여러 쿼리를 처리할 때 발생하는 것이다. 따라서 OSIV가 꺼져 있다면 이 시나리오 자체가 발생하지 않으므로, prepareConnection과의 충돌 우려는 없다보면 된다.
3. SUPPORTS에서 readOnly 라우팅이 동작하는 이유
Read/Write DB 분리 아키텍처에서 AbstractRoutingDataSource를 사용하는 경우, SUPPORTS 전파 전략에서도 readOnly 라우팅이 동작한다. 이것이 가능한 이유를 알기하기 위해 TransactionSynchronizationManager의 ThreadLocal 관리 메커니즘을 알아본다.
1. TransactionSynchronizationManager의 ThreadLocal들
TransactionSynchronizationManager는 다음 값들을 ThreadLocal로 관리한다.
- currentTransactionReadOnly — readOnly 플래그
- currentTransactionIsolationLevel — 격리 수준
- actualTransactionActive — 실제 트랜잭션 존재 여부
- currentTransactionName — 트랜잭션 이름
- resources — 바인딩된 리소스 (EntityManager, Connection 등)
- synchronizations — 트랜잭션 콜백 리스트
2. SYNCHRONIZATION_ALWAYS의 역할
AbstractPlatformTransactionManager의 transactionSynchronization 기본값은 SYNCHRONIZATION_ALWAYS다. 이 설정은 실제 트랜잭션이 없는 "빈 트랜잭션" 상태에서도 동기화를 활성화한다.

@Transactional(propagation = SUPPORTS, readOnly = true)로 진입하면,
- Spring은 실제 트랜잭션을 시작하지 않는다.
- Empty Transaction 상태로 진입한다.
- 하지만 SYNCHRONIZATION_ALWAYS이므로 newSynchronization=true가 된다.
- prepareTransactionStatus()가 호출될 때 newSync=true이므로 ThreadLocal이 세팅된다.
- 이때 TransactionSynchronizationManager.setCurrentTransactionReadOnly(true)가 실행된다.
- actualTransactionActive는 false로 설정된다.
- 결과적으로 RoutingDataSource가 read DB로 라우팅된다.
3. RoutingDataSource와의 연결
AbstractRoutingDataSource를 상속한 RoutingDataSource에서 determineCurrentLookupKey()를 오버라이드할 때, 보통 아래와 같이 구현한다.
@Override
protected Object determineCurrentLookupKey() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (enableLogging) {
boolean isTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
if (isReadOnly) {
log.info("SQL READ 데이터소스 사용 / 트랜잭션 활성화: {}", isTransactionActive);
} else {
log.info("SQL WRITE 데이터소스 사용 / 트랜잭션 활성화: {}", isTransactionActive);
}
}
return isReadOnly ? READ.getLookupKey() : WRITE.getLookupKey();
}
SUPPORTS에서 isCurrentTransactionReadOnly()가 true를 반환하므로, 실제 트랜잭션이 없는 isActualTransactionActive()는 false 이지만 read DB로 라우팅이 정상적으로 이루어진다.
4. 전파 전략별 비교 _ SUPPORTS vs REQUIRED vs NOT_SUPPORTED vs REQUIRES_NEW
각 전파 전략에 따른 커넥션 관리 동작을 표로 정리하면 다음과 같다. readOnly = true를 가정한다.
1. 바깥 트랜잭션이 없는 경우 (컨트롤러에서 직접 호출)
| 전파 전략 | 라우팅 키 | actualTransactionActive | 커넥션 반환 시점 (HOLD 모드) |
| SUPPORTS | read | false | 매 쿼리 후 (statement-level) |
| REQUIRED | read | true | 트랜잭션 커밋 시 |
| NOT_SUPPORTED | read | false | 매 쿼리 후 (statement-level) |
| REQUIRES_NEW | read | true | 트랜잭션 커밋 시 |
바깥 트랜잭션이 없을 때는 네 가지 전략 모두 read로 라우팅된다.
- SUPPORTS와 NOT_SUPPORTED는 실제 트랜잭션 없이 동작.
- REQUIRED와 REQUIRES_NEW는 실제 트랜잭션과 함께 동작.
2. 바깥에 write 트랜잭션이 있는 경우
| 전파 전략 | 라우팅 키 | actualTransactionActive | 커넥션 반환 시점 (HOLD 모드) |
| SUPPORTS | write (바깥 tx 합류) | true (바깥 tx) | 바깥 트랜잭션 종료 시 |
| REQUIRED | write (바깥 tx 합류) | true (바깥 tx) | 바깥 트랜잭션 종료 시 |
| NOT_SUPPORTED | read (바깥 tx 중지) | false | 매 쿼리 후. 단, suspend된 outer 커넥션은 별도 보유 |
| REQUIRES_NEW | read (새 tx 시작) | true | 내부 트랜잭션 커밋 시 |
여기서 SUPPORTS의 특징이 드러난다.
바깥에 write 트랜잭션이 존재하면, SUPPORTS는 그 트랜잭션에 합류하면서 isCurrentTransactionReadOnly()가 false가 된다. 결과적으로 read DB가 아닌 write DB로 라우팅된다.
NOT_SUPPORTED 전략 참고 사항
- NOT_SUPPORTED는 outer 트랜잭션을 suspend하지만, suspend된 트랜잭션의 커넥션은 풀에 반환되지 않고 DataSourceTransactionObject.suspendedResources가 보유한다.
따라서 outer write tx 안에서 NOT_SUPPORTED 메서드를 호출하면 read 커넥션 + suspend된 write 커넥션 = 동시 2개 점유 상태가 된다. 호출 체인이 깊거나 풀이 작은 환경에선 SUPPORTS보다 풀 압박이 클 수 있다.
3. 유스케이스별 권장 전파 전략
유스케이스 권장 전파 전략 이유
| 유스케이스 | 권장 전파 전략 | 이유 |
| 검색 서비스 (항상 컨트롤러에서 호출) | SUPPORTS | 심플하고 효율적 |
| 검색 서비스 (다른 서비스에서도 호출 가능) | NOT_SUPPORTED | 바깥 tx 영향 차단 |
| 가격 계산, 결제 관련 조회 | REQUIRED | 트랜잭션 일관성 보장 |
| 독립적인 감사 로그 쓰기 (Audit Log) | REQUIRES_NEW | 바깥 tx 롤백과 무관하게 저장 |
5. 트레이드오프 _ 커넥션 효율 vs 일관성
1. SUPPORTS의 이점
SUPPORTS의 가장 큰 이점은 커넥션 점유 시간 최소화다.
SharedEntityManagerCreator가 statement 단위로 EM을 생성/close하므로, SUPPORTS의 커넥션 점유는 각 쿼리 실행 시간만큼으로 한정된다. HOLD 모드의 hold 효과 자체가 사실상 발휘되지 않는다.
또한 DB 측 트랜잭션(BEGIN/COMMIT)이 없으므로 오버헤드가 줄고, HikariCP 커넥션 풀의 Active Connection 수가 낮게 유지된다.
2. SUPPORTS의 비용
MySQL 기본 격리 수준 Repeatable Read 무력화
@Transactional(propagation = SUPPORTS, readOnly = true)
public BoardPage getBoard() {
long total = repo.count(); // 스냅샷 #A
List<Post> list = repo.findTop20(); // 스냅샷 #B
return new BoardPage(total, list);
}
SUPPORTS는 호출자에 트랜잭션이 없을 때 새로 시작하지 않으므로, 각 SELECT는 개별 Tx 안에서 실행되고 즉시 종료된다.
같은 메서드 내 SELECT가 각각 다른 Read View를 보게 되므로, REPEATABLE READ를 설정해두어도 사실상 READ COMMITTED와 동등한 일관성으로 떨어진다. 페이지 카운트와 목록을 함께 조회하는 두 쿼리 사이에 다른 트랜잭션이 커밋한 변경에 노출될 수 있다.
3. 실무 판단 기준
검색 서비스처럼 읽기 전용이고 최종 일관성을 허용하는 서비스라면 SUPPORTS가 합리적이다. 검색 결과는 본질적으로 "지금 시점의 가장 근사한 결과"이다.
반면, 할인 가격 계산이나 결제 관련 서비스처럼 쿼리 간 정합성이 비즈니스 정확성에 직결되는 경우에는 REQUIRED를 사용해야 한다. 사용자가 주문하기를 누른 시점에는 유효했던 쿠폰이 트랜잭션 진행중에 만료되어 적용되지 않는다면 사용자 경험을 크게 해칠 수 있다. 이런 문제는 MySQL REPEATABLE READ 환경에서 트랜잭션으로 묶는 것만으로도 스냅샷 일관성을 확보할 수 있다.
6. LogicalConnectionManagedImpl — 커넥션 반환 내부 로직
Hibernate 내부에서 커넥션 반환 결정이 실제로 어떻게 이루어지는지 코드 레벨에서 확인해보자.
LogicalConnectionManagedImpl은 Hibernate가 JDBC 커넥션의 획득과 반환을 관리하는 핵심 클래스다.
매 SQL 실행 후 afterStatement()가 호출된다.

- afterStatement()는 release mode가 AFTER_STATEMENT일 때만 커넥션을 반환한다.
(단, hasRegisteredResources()로 열린 JDBC 리소스 체크 후 없다면) - Spring Boot 기본값인 HOLD 모드(ON_CLOSE)에서는 이 조건에 걸리지 않으므로, 쿼리 후에도 커넥션이 유지된다.
트랜잭션 종료 시에는 afterTransaction()이 호출된다.

- afterTransaction()은 release mode가 ON_CLOSE가 아닌 경우에 커넥션을 반환한다.
- HOLD 모드에서는 이 조건에도 걸리지 않는다. 결국 HOLD 모드에서 커넥션은 Session이 close()될 때 비로소 반환된다.
이 동작을 정리하면
SQL 실행 후 (afterStatement) (리소스가 없을 때 가정)
├─ AFTER_STATEMENT 모드 → 커넥션 반환
├─ AFTER_TRANSACTION 모드 → 유지
└─ ON_CLOSE(HOLD) 모드 → 유지
트랜잭션 종료 후 (afterTransaction) (리소스가 없을 때 가정)
├─ AFTER_STATEMENT 모드 → 커넥션 반환 (이전에 리소스 때문에 보류된 경우)
├─ AFTER_TRANSACTION 모드 → 커넥션 반환
└─ ON_CLOSE(HOLD) 모드 → 유지
Session 종료 (close):
└─ 모든 모드 → 커넥션 반환
전파전략 별 세션 범위 및 HOLD 시간 정리
| 전파전략 | Seesion 종료 시점 | HOLD 모드가 holding하는 실제 시간 |
| REQUIRED | tx 커밋/롤백 시 | 트랜잭션 전체 |
| SUPPORTS (outer tx 없음) | 매 statement 후 | 거의 0 (statement 길이) |
| SUPPORTS (outer tx 합류) | outer tx 종료 시 | outer tx 전체 |
마무리
핵심 요약
- Spring Boot의 기본 커넥션 관리 모드는 DELAYED_ACQUISITION_AND_HOLD다.
Hibernate의 기본값 AFTER_TRANSACTION과 다르다. - HOLD 모드에서는 Session이 닫힐 때 커넥션이 반환된다.
OSIV=FALSE 환경에서 Session 수명은 전파 전략에 따라 달라진다
REQUIRED/REQUIRES_NEW는 트랜잭션 전체, SUPPORTS/NOT_SUPPORTED(outer tx 없음)는 statement 단위가 된다. - SUPPORTS에서 readOnly 라우팅이 동작하는 이유는 SYNCHRONIZATION_ALWAYS 때문이다.
실제 트랜잭션이 없어도 ThreadLocal에 readOnly=true가 세팅되어 RoutingDataSource가 read DB로 라우팅한다. - SUPPORTS의 함정: 바깥 write 트랜잭션이 있으면 합류한다.
항상 read DB로 라우팅해야 한다면 NOT_SUPPORTED를 고려한다. - 커넥션 풀 효율과 일관성은 트레이드오프다.
검색 서비스는 SUPPORTS가 합리적이고, 정합성이 중요한 서비스는 REQUIRED가 적합하다.
참고 자료
- Hibernate ORM — LogicalConnectionManagedImpl 소스 코드
- Spring Framework — HibernateJpaVendorAdapter 소스 코드
- Spring Framework — AbstractPlatformTransactionManager API
- Spring Framework — TransactionSynchronizationManager API
- Vlad Mihalcea — Spring Transaction and Connection Management
- Vlad Mihalcea — How does aggressive connection release work in Hibernate
- Spring Framework Issue #19116 — Default connection release mode inconsistent with Hibernate 5.1.1
'기술 학습' 카테고리의 다른 글
| Redis Cluster _ Lettuce Client의 Topology Refresh (1) | 2026.04.25 |
|---|---|
| 분산 시스템 CAP 정리 (0) | 2026.03.31 |
| HTTP/1.1, HTTP/2, HTTP/3 프로토콜 비교 정리 (1) | 2026.03.31 |
| MSA에서 CORS 문제를 해결하는 4가지 전략 (0) | 2026.03.27 |
| TCP/IP 체크섬(Checksum) 내부 동작 원리 (0) | 2026.03.23 |