F-LAB JAVA · 7주차 · Phase 6 · PlatformTransactionManager
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
PlatformTransactionManager 의 3가지 구현체 — DataSourceTransactionManager (JDBC / JdbcTemplate, DataSource 받아 Connection.setAutoCommit(false) 호출), HibernateTransactionManager (Hibernate 직접, SessionFactory 받음, 옛 스타일), JpaTransactionManager (JPA / Spring Data JPA, EntityManagerFactory 받음) — 사용 기술에 따라 선택되며 JPA + JdbcTemplate 혼용 환경에선 JpaTransactionManager 가 DataSource 도 인식해 단일 트랜잭션 보장이 가능하고 Spring Boot 는 의존성에 따라 자동 선택한다.
Spring 의PlatformTransactionManager는 3가지 핵심 구현체 가 있으며 사용 기술에 따라 선택한다.
(1) DataSourceTransactionManager — 가장 기본적, JDBC / JdbcTemplate / MyBatis 사용 시,DataSource빈을 받아 내부적으로Connection.setAutoCommit(false)→commit()→rollback()을 호출.
(2) HibernateTransactionManager — Hibernate 를 JPA 없이 직접 사용 시 (드묾, 옛 스타일),SessionFactory빈을 받아Session.beginTransaction()호출.
(3) JpaTransactionManager — JPA / Spring Data JPA 사용 시 (현대 표준),EntityManagerFactory빈을 받아EntityManager.getTransaction().begin()호출.
선택 기준 — JdbcTemplate 만 = DataSourceTM, Hibernate 직접 = HibernateTM, JPA = JpaTM 인데 — JPA + JdbcTemplate 혼용 시 JpaTransactionManager 사용 (JPA 의 EntityManagerFactory 가 같은 DataSource 를 공유하므로 단일 Connection / 단일 트랜잭션 가능, JdbcTemplate 도 같은 Connection 으로 동작 = 두 도구가 같은 트랜잭션 공유).
Spring Boot 는 —spring-boot-starter-data-jpa의존성 시 JpaTransactionManager,spring-boot-starter-jdbc만 시 DataSourceTransactionManager 자동 등록.
3가지 구현체 = 3 변속기:
상황:
- 같은 핸들 (PlatformTransactionManager)
- 다른 변속기 (구현체)
- 운전법 같음 (인터페이스)
[1] DataSourceTM (수동 변속기, 가장 단순):
- JDBC 직접 (Connection)
- JdbcTemplate / MyBatis 가 사용
- 기본 / 가벼움
[2] HibernateTM (자동 변속기 옛 모델, 드묾):
- Hibernate 직접 (SessionFactory)
- JPA 안 쓸 때
- 거의 안 씀
[3] JpaTM (자동 변속기 신모델, 표준):
- JPA / Spring Data JPA
- EntityManagerFactory
- 현대 표준
선택:
- 사용 기술 보고
- Spring Boot 자동
- 코드 변경 X
혼용 (JPA + JdbcTemplate):
- JpaTM 으로 통합
- 같은 DataSource 공유
- 같은 트랜잭션
ILIC:
- JPA 주력 → JpaTransactionManager
- JdbcTemplate 보조 → 같은 TM 으로
→ 3 구현체, 인터페이스 같음, 사용 기술 따라 선택, 혼용 시 JpaTM.
1. 3가지 구현체 개요
2. DataSourceTransactionManager
3. HibernateTransactionManager
4. JpaTransactionManager
5. 각 구현체의 내부 동작
6. 선택 기준
7. JPA + JdbcTemplate 혼용
8. Spring Boot 자동 구성
9. ILIC 의 선택 + Phase 6.3 예고
| 구현체 | 사용 기술 | 받는 빈 | 인기 |
|---|---|---|---|
| DataSourceTransactionManager | JDBC, JdbcTemplate, MyBatis | DataSource | ★★ |
| HibernateTransactionManager | Hibernate 직접 (JPA 없이) | SessionFactory | ★ (드묾) |
| JpaTransactionManager | JPA, Spring Data JPA | EntityManagerFactory | ★★★ (표준) |
모두 같은 인터페이스:
3 구현체 모두:
- PlatformTransactionManager 구현
- 3 메서드 (getTransaction/commit/rollback)
애플리케이션:
- 인터페이스만 의존
- 구현체 모름
→ 5주차 DIP / OCP
내부 동작 다름:
DataSourceTM:
- Connection.setAutoCommit(false)
- Connection.commit / rollback
HibernateTM:
- Session.beginTransaction()
- Transaction.commit / rollback
JpaTM:
- EntityManager.getTransaction().begin()
- EntityTransaction.commit / rollback
→ 내부는 다르지만 외부는 같음
// ILIC 의 구현체 (Spring Boot 자동)
// 사용 빈 (Spring Boot 가 자동 생성)
@Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
// → ILIC = JPA 사용
// → JpaTransactionManager 자동
// → 모든 @Transactional 이 이 TM 사용
class PlatformTransactionManager {}
class JpaTransactionManager extends PlatformTransactionManager {
JpaTransactionManager(EntityManagerFactory emf) {}
}
class EntityManagerFactory {}
@interface Bean {}
EntityManagerFactory emf;
3가지 구현체 개요는?
답:
1. 3가지:
인터페이스:
내부:
표준:
DataSourceTransactionManager:
JDBC 기반 트랜잭션 관리:
- DataSource 받음
- Connection 사용
- 가장 기본
사용 케이스:
- JdbcTemplate (6주차)
- MyBatis
- JDBC 직접
@Configuration
public class TransactionConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource(/* config */);
}
@Bean
public PlatformTransactionManager transactionManager(
DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
class HikariDataSource implements DataSource {}
interface DataSource {}
class DataSourceTransactionManager implements PlatformTransactionManager {
DataSourceTransactionManager(DataSource ds) {}
}
interface PlatformTransactionManager {}
@interface Configuration {}
@interface Bean {}
내부 동작 (개념):
getTransaction:
1. dataSource.getConnection() (풀에서)
2. conn.setAutoCommit(false) ← 트랜잭션 시작
3. ThreadLocal 에 Connection 저장
4. TransactionStatus 반환
commit:
1. conn.commit()
2. conn.setAutoCommit(true)
3. ThreadLocal 정리
4. conn.close() (풀 반환)
rollback:
1. conn.rollback()
2. conn.setAutoCommit(true)
3. ThreadLocal 정리
4. conn.close()
ThreadLocal:
Spring 의 트랜잭션 동기화:
- 같은 트랜잭션 = 같은 쓰레드
- 같은 쓰레드 = 같은 Connection
- ThreadLocal 로 저장
효과:
- JdbcTemplate 호출 시
- 같은 Connection 사용
- 단일 트랜잭션
→ TransactionSynchronizationManager
// JdbcTemplate 자동 연동
@Service
public class ShipmentDao {
@Autowired JdbcTemplate jdbcTemplate;
public void update(Shipment s) {
// jdbcTemplate 가 자동으로:
// 1. 현재 트랜잭션 Connection 확인 (ThreadLocal)
// 2. 있으면 그 Connection 사용
// 3. 없으면 새 Connection
jdbcTemplate.update("UPDATE shipments SET status = ?", s.getStatus());
}
}
@Service
public class ShipmentService {
@Autowired ShipmentDao dao;
@Transactional // DataSourceTransactionManager 가 처리
public void process(Long id) {
dao.update(s1); // 같은 Connection
dao.update(s2); // 같은 Connection
// 단일 트랜잭션
}
}
class Shipment { String getStatus() { return null; } }
class JdbcTemplate { void update(String s, Object... a) {} }
JdbcTemplate jdbcTemplate;
ShipmentDao dao;
Shipment s1, s2;
@interface Service {}
@interface Autowired {}
@interface Transactional {}
ILIC 가 JdbcTemplate 만 사용한다면 (가정)
설정:
@Bean DataSourceTransactionManager
사용:
@Transactional
public void process() {
jdbcTemplate.update(...);
jdbcTemplate.query(...);
// 같은 Connection / 단일 트랜잭션
}
Spring Boot:
- spring-boot-starter-jdbc
- 자동 DataSourceTransactionManager
→ 단순 / 가벼움
→ 하지만 ILIC = JPA 주력
DataSourceTransactionManager 는?
답:
1. DataSourceTM:
사용:
내부:
ThreadLocal:
HibernateTransactionManager:
Hibernate 직접 사용 시 (JPA 없이):
- SessionFactory 받음
- Session 사용
현재는 드묾:
- JPA 가 표준
- Hibernate 직접 케이스 적음
@Configuration
public class HibernateConfig {
@Bean
public SessionFactory sessionFactory() {
// Hibernate 직접 설정
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
return cfg.buildSessionFactory();
}
@Bean
public PlatformTransactionManager transactionManager(
SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
}
class SessionFactory {}
class Configuration {
Configuration configure(String s) { return this; }
SessionFactory buildSessionFactory() { return null; }
}
class HibernateTransactionManager implements PlatformTransactionManager {
HibernateTransactionManager(SessionFactory sf) {}
}
interface PlatformTransactionManager {}
@interface Configuration {}
@interface Bean {}
// Hibernate 직접 사용 (드묾)
@Repository
public class ShipmentDao {
@Autowired SessionFactory sessionFactory;
public Shipment findById(Long id) {
Session session = sessionFactory.getCurrentSession();
return session.get(Shipment.class, id);
}
public void save(Shipment s) {
Session session = sessionFactory.getCurrentSession();
session.save(s);
}
}
// 사용
@Service
public class ShipmentService {
@Autowired ShipmentDao dao;
@Transactional // HibernateTransactionManager
public void process(Long id) {
Shipment s = dao.findById(id);
s.markAsShipped();
dao.save(s);
}
}
class Shipment { void markAsShipped() {} }
class SessionFactory { Session getCurrentSession() { return null; } }
class Session { <T> T get(Class<T> c, Object id) { return null; } void save(Object o) {} }
SessionFactory sessionFactory;
ShipmentDao dao;
@interface Repository {}
@interface Service {}
@interface Autowired {}
@interface Transactional {}
왜 드묾?:
Hibernate 직접:
- JPA 보다 강력 (Hibernate 고유 기능)
- 하지만 표준 X
JPA + Hibernate:
- JPA 표준 사용
- 내부적으로 Hibernate (구현체)
- 표준 + 강력
→ 거의 모두 JPA 사용
→ HibernateTransactionManager 거의 X
옛 시스템 케이스:
- 2000년대 초중반 시스템
- JPA 표준 이전 시스템
- 마이그레이션 어려운 레거시
→ 현재 신규는 JPA
내부 동작 (개념):
getTransaction:
1. sessionFactory.openSession()
2. session.beginTransaction()
3. ThreadLocal 저장
4. TransactionStatus 반환
commit:
1. session.getTransaction().commit()
2. session.close()
3. ThreadLocal 정리
rollback:
1. session.getTransaction().rollback()
2. session.close()
ILIC = HibernateTransactionManager X
ILIC = JPA + Hibernate (JPA 모드):
- JpaTransactionManager 사용
- HibernateTransactionManager 사용 X
- 표준 JPA 인터페이스
Hibernate 고유 기능 (드물게):
- @DynamicUpdate
- @BatchSize
- HQL (HibernateTransactionManager 없이도 OK)
→ JpaTransactionManager 만으로 충분
HibernateTransactionManager 는?
답:
1. HibernateTM:
SessionFactory:
현재:
이유:
JpaTransactionManager:
JPA / Spring Data JPA:
- EntityManagerFactory 받음
- EntityManager 사용
현대 표준:
- 99% 사용
- Spring Boot 자동
@Configuration
public class JpaConfig {
@Bean
public EntityManagerFactory entityManagerFactory() {
// JPA 설정 (보통 Spring Boot 자동)
return null;
}
@Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
class EntityManagerFactory {}
class JpaTransactionManager implements PlatformTransactionManager {
JpaTransactionManager(EntityManagerFactory emf) {}
}
interface PlatformTransactionManager {}
@interface Configuration {}
@interface Bean {}
// JPA Repository (Spring Data JPA)
public interface ShipmentRepository extends JpaRepository<Shipment, Long> {
// 자동 구현
}
// 서비스
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo;
@Transactional // JpaTransactionManager
public void process(Long id) {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
// 트랜잭션 commit 시 Dirty Checking → UPDATE
}
}
class Shipment { void markAsShipped() {} }
interface JpaRepository<T, ID> { java.util.Optional<T> findById(ID id); }
interface ShipmentRepository extends JpaRepository<Shipment, Long> {}
ShipmentRepository repo;
@interface Service {}
@interface Autowired {}
@interface Transactional {}
내부 동작 (개념):
getTransaction:
1. emf.createEntityManager()
2. em.getTransaction().begin()
3. EntityManager 를 ThreadLocal 저장
4. TransactionStatus 반환
commit:
1. em.flush() (변경 사항 DB 반영)
2. em.getTransaction().commit()
3. em.close()
4. ThreadLocal 정리
rollback:
1. em.getTransaction().rollback()
2. em.close()
3. ThreadLocal 정리
영속성 컨텍스트 ↔ 트랜잭션:
JPA 의 영속성 컨텍스트:
- 트랜잭션 시작 시 생성
- 트랜잭션 commit 시 flush
- 트랜잭션 종료 시 close
Dirty Checking:
- 영속성 컨텍스트의 객체 변경 감지
- flush 시 UPDATE 자동
- JPA 의 강력한 기능
→ JpaTransactionManager 가 모두 처리
OSIV (Open Session In View):
- Spring Boot 기본 ON
- 영속성 컨텍스트를 View 까지 유지
- Controller 에서 Lazy Loading 가능
단점:
- Connection 점유 길어짐
- 풀 부하
운영 권장:
spring.jpa.open-in-view: false
Hibernate (JPA 모드):
JpaTransactionManager 가 EntityManagerFactory 받음:
- 내부 구현은 Hibernate
- 하지만 JPA 표준 인터페이스로 동작
- Hibernate 고유 기능도 활용 가능
→ 표준 + 강력
# ILIC application.yml
spring:
datasource:
url: jdbc:mysql://...
username: ...
password: ...
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 100
open-in-view: false # 운영 권장
// ILIC 의 표준 (자동)
// Spring Boot 가 자동 등록:
// - HikariDataSource (DataSource)
// - EntityManagerFactory (JPA, Hibernate 구현)
// - JpaTransactionManager (TM)
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo;
@Transactional // JpaTransactionManager 가 처리
public void process(Long id) {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
// Dirty Checking → UPDATE 자동
}
}
// → 102 테이블 / 1020 메서드 모두 같은 패턴
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Service {}
@interface Autowired {}
@interface Transactional {}
JpaTransactionManager 는?
답:
1. JpaTM:
EntityManagerFactory:
현대 표준:
영속성 컨텍스트:
| 항목 | DataSourceTM | HibernateTM | JpaTM |
|---|---|---|---|
| 받는 빈 | DataSource | SessionFactory | EntityManagerFactory |
| 트랜잭션 객체 | Connection | Session | EntityManager |
| 시작 메서드 | setAutoCommit(false) | beginTransaction() | getTransaction().begin() |
| 커밋 | conn.commit() | tx.commit() | tx.commit() (+ flush) |
| 롤백 | conn.rollback() | tx.rollback() | tx.rollback() |
| 종료 | conn.close() | session.close() | em.close() |
| ThreadLocal | Connection | Session | EntityManager |
ThreadLocal 의 키:
DataSourceTM:
- DataSource → Connection 매핑
JpaTM:
- EntityManagerFactory → EntityManager 매핑
TransactionSynchronizationManager:
- Spring 의 트랜잭션 동기화 클래스
- getResource(key) / bindResource(key, value)
도구와 자동 연동:
JdbcTemplate:
- DataSource 받음
- DataSourceUtils.getConnection(dataSource)
- ThreadLocal 에서 Connection 가져옴 (있으면)
- 또는 새 Connection
Spring Data JPA:
- EntityManagerFactory 받음
- 같은 방식
→ 자동 동기화
// DataSourceTransactionManager (간략)
public class DataSourceTransactionManager
extends AbstractPlatformTransactionManager {
private DataSource dataSource;
@Override
protected Object doGetTransaction() {
// ThreadLocal 에서 ConnectionHolder 가져옴
ConnectionHolder holder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(dataSource);
return new DataSourceTransactionObject(holder);
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false); // ★ 핵심
// ThreadLocal 바인딩
TransactionSynchronizationManager.bindResource(
dataSource, new ConnectionHolder(conn));
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
ConnectionHolder holder = (ConnectionHolder) status.getTransaction();
holder.getConnection().commit(); // ★ 핵심
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
ConnectionHolder holder = (ConnectionHolder) status.getTransaction();
holder.getConnection().rollback(); // ★ 핵심
}
}
class AbstractPlatformTransactionManager {}
class DataSourceTransactionObject { DataSourceTransactionObject(ConnectionHolder h) {} }
class ConnectionHolder {
ConnectionHolder(Connection c) {}
Connection getConnection() { return null; }
}
class TransactionSynchronizationManager {
static Object getResource(Object key) { return null; }
static void bindResource(Object key, Object value) {}
}
class TransactionDefinition {}
class DefaultTransactionStatus { Object getTransaction() { return null; } }
DataSource dataSource;
interface DataSource { Connection getConnection() throws SQLException; }
// JpaTransactionManager (간략)
public class JpaTransactionManager
extends AbstractPlatformTransactionManager {
private EntityManagerFactory entityManagerFactory;
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin(); // ★ JPA 방식
// ThreadLocal 에 바인딩
TransactionSynchronizationManager.bindResource(
entityManagerFactory, new EntityManagerHolder(em));
// DataSource 도 인식 (혼용 가능!)
// 같은 Connection 공유
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
EntityManagerHolder holder = (EntityManagerHolder) status.getTransaction();
EntityManager em = holder.getEntityManager();
em.flush(); // ★ 변경 사항 DB 반영
em.getTransaction().commit(); // ★ 커밋
}
}
class EntityManagerHolder {
EntityManagerHolder(EntityManager em) {}
EntityManager getEntityManager() { return null; }
}
class EntityManager {
EntityTransaction getTransaction() { return null; }
void flush() {}
}
class EntityTransaction { void begin() {} void commit() {} }
class AbstractPlatformTransactionManager {}
class TransactionDefinition {}
class DefaultTransactionStatus { Object getTransaction() { return null; } }
class TransactionSynchronizationManager {
static void bindResource(Object key, Object value) {}
}
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
EntityManagerFactory entityManagerFactory;
ILIC = JpaTransactionManager
ILIC 의 동작:
1. @Transactional 호출
2. JpaTransactionManager.doBegin()
- EntityManager 생성
- em.getTransaction().begin()
- ThreadLocal 에 EntityManagerHolder
- + DataSource 에 ConnectionHolder (혼용용)
3. 비즈니스 로직 (Repository / JdbcTemplate)
- 모두 같은 Connection 사용
4. JpaTransactionManager.doCommit()
- em.flush() (Dirty Checking)
- em.getTransaction().commit()
- em.close()
→ 모든 도구가 같은 트랜잭션
각 구현체의 내부 동작 차이는?
답:
1. DataSourceTM:
HibernateTM:
JpaTM:
공통:
| 사용 기술 | 선택 |
|---|---|
| JdbcTemplate / MyBatis 만 | DataSourceTransactionManager |
| Hibernate 직접 (JPA 없이) | HibernateTransactionManager (드묾) |
| JPA / Spring Data JPA | JpaTransactionManager |
| JPA + JdbcTemplate 혼용 | JpaTransactionManager (다음 섹션) |
| Hibernate (JPA 모드) | JpaTransactionManager |
의사 결정:
Q: JPA 사용?
Y → JpaTransactionManager
N → Q: Hibernate 직접?
Y → HibernateTransactionManager (드묾)
N → Q: JdbcTemplate 만?
Y → DataSourceTransactionManager
N → ... (특수)
실무 비율:
JpaTransactionManager: 80%+
- JPA / Spring Data JPA 사용
- 현대 표준
DataSourceTransactionManager: 10-15%
- JdbcTemplate / MyBatis 만
- 통계 / 분석 시스템
HibernateTransactionManager: <5%
- 옛 시스템
- 거의 X
변경 시:
JPA → JdbcTemplate 만:
- JpaTM → DataSourceTM
- 빈 변경
- 코드는?
- @Transactional 그대로
- Repository 만 변경
애플리케이션 코드:
- 인터페이스 의존
- 최소 변경
ILIC 의 선택:
ILIC = Spring Boot + JPA + Spring Data JPA
→ JpaTransactionManager 자동 (Spring Boot)
추가로:
- JdbcTemplate 도 사용 (복잡 통계)
- 혼용 환경 → 다음 섹션
선택 기준은?
답:
1. JPA:
JdbcTemplate:
혼용:
자동:
혼용 시나리오:
대부분 JPA + 일부 JdbcTemplate:
- JPA: 단순 CRUD
- JdbcTemplate: 복잡 통계
같은 트랜잭션 안에서:
- JPA save (Dirty Checking)
- JdbcTemplate 통계 쿼리
- 모두 같은 트랜잭션?
JpaTM 의 마법:
JpaTransactionManager 는:
- EntityManagerFactory 받지만
- 내부적으로 DataSource 도 인식
- 같은 Connection 사용
결과:
- JPA 도 같은 Connection
- JdbcTemplate 도 같은 Connection
- 단일 트랜잭션 보장
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo; // JPA
@Autowired JdbcTemplate jdbcTemplate; // JDBC
@Transactional
public void process(Long id) {
// 1. JPA 로 조회
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
// → Dirty Checking 예약
// 2. JdbcTemplate 로 복잡 통계 업데이트
jdbcTemplate.update("""
UPDATE shipment_stats
SET shipped_count = shipped_count + 1
WHERE customer_id = ?
""", s.getCustomerId());
// 3. 트랜잭션 commit:
// - JPA flush (UPDATE shipments)
// - + 위 UPDATE shipment_stats
// - 모두 같은 Connection
// - 단일 트랜잭션
// - 원자적 (모두 성공 / 모두 실패)
}
}
class Shipment { void markAsShipped() {} Long getCustomerId() { return null; } }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
JdbcTemplate jdbcTemplate;
class JdbcTemplate { void update(String s, Object... a) {} }
@interface Service {}
@interface Autowired {}
@interface Transactional {}
ThreadLocal 의 공유:
JpaTM doBegin():
1. EntityManager 생성
2. em.getTransaction().begin()
3. ThreadLocal 에:
- EntityManagerHolder (JPA 키)
- ConnectionHolder (DataSource 키, 같은 Connection)
4. JdbcTemplate 호출 시 → 같은 Connection
5. JPA 호출 시 → 같은 Connection
→ 통합 트랜잭션
주의 사항:
1. flush 시점:
- JPA 의 변경 사항이 DB 에 언제 반영?
- 보통 commit 직전 자동 flush
- JdbcTemplate 쿼리 전에 flush 안 됨!
- 명시적 em.flush() 필요할 수 있음
2. 캐시 일관성:
- JPA 1차 캐시 vs DB
- JdbcTemplate 직접 DB 수정 시
- JPA 캐시는 모름
- em.refresh() 또는 em.clear() 필요
→ 혼용 시 주의
@Transactional
public void process(Long id) {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
// 명시적 flush
entityManager.flush(); // ← Dirty Checking → UPDATE SQL 실행
// 이제 JdbcTemplate 쿼리에서
// 최신 status 반영됨
jdbcTemplate.query(...);
}
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
class EntityManager { void flush() {} }
EntityManager entityManager;
JdbcTemplate jdbcTemplate;
class JdbcTemplate { void query(Object... a) {} }
@interface Transactional {}
// ILIC 의 혼용 (실제 패턴)
@Service
public class ShipmentReportService {
@Autowired ShipmentRepository repo; // JPA
@Autowired JdbcTemplate jdbcTemplate; // JDBC
@Transactional
public ShipmentReport generateReport(Long id) {
// 1. JPA: 도메인 작업
Shipment s = repo.findById(id).orElseThrow();
s.markAsReported();
// 2. JdbcTemplate: 복잡 통계
List<Map<String, Object>> stats = jdbcTemplate.queryForList("""
WITH ranked AS (
SELECT region, amount,
RANK() OVER (PARTITION BY region ORDER BY amount) AS rk
FROM shipments
)
SELECT * FROM ranked WHERE rk <= 5
""");
// 3. JpaTM 이 모두 처리
// - 같은 Connection
// - 단일 트랜잭션
// - 원자성 보장
return ShipmentReport.builder()
.shipment(s)
.stats(stats)
.build();
}
}
// → ILIC = JpaTransactionManager + JPA + JdbcTemplate 혼용
class Shipment { void markAsReported() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
JdbcTemplate jdbcTemplate;
class JdbcTemplate { java.util.List<java.util.Map<String, Object>> queryForList(String s) { return null; } }
class ShipmentReport {
static Builder builder() { return null; }
static class Builder {
Builder shipment(Shipment s) { return this; }
Builder stats(java.util.List<java.util.Map<String, Object>> s) { return this; }
ShipmentReport build() { return null; }
}
}
@interface Service {}
@interface Autowired {}
@interface Transactional {}
JPA + JdbcTemplate 혼용 환경의 TM 선택은?
답:
1. JpaTM:
마법:
단일 트랜잭션:
주의:
의존성 기반 자동:
build.gradle 의존성 →
Spring Boot 자동 구성:
spring-boot-starter-data-jpa:
→ JpaTransactionManager 자동
spring-boot-starter-jdbc (만):
→ DataSourceTransactionManager 자동
둘 다:
→ JpaTransactionManager 자동 (JPA 우선)
Spring Boot 의 자동 빈 (JPA):
1. HikariDataSource (DataSource)
2. EntityManagerFactory
3. JpaTransactionManager (PlatformTransactionManager)
4. JdbcTemplate (있으면)
5. NamedParameterJdbcTemplate
모두 자동 / 연결
# application.yml (Spring Boot 자동)
spring:
datasource:
url: jdbc:mysql://...
username: ...
password: ...
hikari:
maximum-pool-size: 20
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
format_sql: true
open-in-view: false
# 코드 X
# 자동 구성
// 수동 빈 설정 (다중 DataSource 등)
@Configuration
public class TransactionConfig {
@Primary
@Bean
public PlatformTransactionManager transactionManager(
@Qualifier("mainEmf") EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
@Bean
public PlatformTransactionManager logTransactionManager(
@Qualifier("logDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// 사용 시 명시
@Transactional("transactionManager")
public void mainBusiness() { ... }
@Transactional("logTransactionManager")
public void logBusiness() { ... }
class JpaTransactionManager implements PlatformTransactionManager {
JpaTransactionManager(EntityManagerFactory emf) {}
}
class DataSourceTransactionManager implements PlatformTransactionManager {
DataSourceTransactionManager(DataSource ds) {}
}
interface PlatformTransactionManager {}
class EntityManagerFactory {}
interface DataSource {}
@interface Configuration {}
@interface Primary {}
@interface Bean {}
@interface Qualifier { String value(); }
@interface Transactional { String value(); }
자동 구성의 가치:
1. 빠른 시작:
- 의존성만
- 코드 X
2. 표준:
- 모두 같은 패턴
- 학습 ↓
3. 안전:
- 검증된 설정
- 실수 X
4. 유지보수:
- Spring Boot 가 관리
→ Spring Boot 의 강점
// ILIC build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// 자동 포함:
// - JPA + Hibernate
// - Spring Data JPA
// - JpaTransactionManager (자동)
// - HikariCP (자동)
runtimeOnly 'com.mysql:mysql-connector-j'
}
// ILIC 의 코드 (빈 설정 없음, 자동)
@SpringBootApplication
public class IlicApplication {
public static void main(String[] args) {
SpringApplication.run(IlicApplication.class, args);
}
}
// → Spring Boot 가 모두 자동
// → JpaTransactionManager 자동 등록
// → 모든 @Transactional 이 이걸 사용
@interface SpringBootApplication {}
class SpringApplication {
static void run(Class<?> c, String[] args) {}
}
Spring Boot 자동 구성은?
답:
1. 자동:
자동 빈:
application.yml:
가치:
ILIC 의 선택:
주력 TM:
- JpaTransactionManager (자동)
도구 조합:
- JPA (주력) ✓
- Spring Data JPA ✓
- Querydsl ✓
- JdbcTemplate (보조 - 복잡 통계) ✓
통합 트랜잭션:
- JpaTM 이 모두 처리
- 단일 Connection
- 원자성
→ 한국 기업 표준
// ILIC 의존성
dependencies {
// 핵심
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// Querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
// (자동 포함: spring-boot-starter-jdbc → JdbcTemplate)
// 추가
implementation 'org.springframework.boot:spring-boot-starter-web'
// DB
runtimeOnly 'com.mysql:mysql-connector-j'
}
Phase 6.3 — 사용 전후 비교:
Before (수동):
- tx.begin / commit / rollback
- 보일러플레이트
- 함정
After (PlatformTM):
- 인터페이스 의존
- 일관 코드
- 추상화
After 2 (TransactionTemplate):
- 람다
- 자동
After 3 (@Transactional):
- 어노테이션 1줄
- Phase 7 ★★★
진화의 흐름:
Phase 5 (수동):
- 결합 / 함정
- 자동화 동기
Phase 6 (PlatformTM):
- 인터페이스 추상화
- 구현체 분리
- TransactionTemplate
Phase 7 (@Transactional):
- 어노테이션 + AOP
- 5가지 함정
- 면접 정점
→ 점진적 자동화
| Q | 핵심 답변 |
|---|---|
| 3 구현체? | DS/Hibernate/JPA TM |
| DataSourceTM? | JDBC / Connection |
| HibernateTM? | Hibernate 직접 (드묾) |
| JpaTM? | JPA / 현대 표준 |
| 선택 기준? | 사용 기술 |
| 혼용? | JpaTM |
| ThreadLocal? | 트랜잭션 동기화 |
| Spring Boot 자동? | 의존성 기반 |
| 다중 TM? | @Qualifier |
| ILIC 선택? | JpaTM |
답:
답:
답:
답:
답:
1. 3가지 구현체
2. 선택 기준
3. ILIC = JpaTransactionManager
이번 Unit에서 3가지 구현체를 봤다면, 다음은 사용 전후 비교 (Phase 6 마지막).
🎯 Phase 6 — PlatformTransactionManager
✅ Unit 6.1 인터페이스 추상화
✅ Unit 6.2 3가지 구현체 ← 여기
⏭ Unit 6.3 사용 전후 비교 — Phase 6 완주
🗂️ Part A (완주)
✅ Phase 1-4 (16)
🔄 Part B — 트랜잭션 추상화의 진화
✅ Phase 5 (2)
🎯 Phase 6 (2/3)
총: 20/24 Unit (83%)