Spring/Spring

[Spring][Data Access] 1-3. Transaction Manager: Synchronizing Resources with Transactions

noahkim_ 2024. 8. 11. 10:36

1. 트랜잭션 매니저와 리소스의 연관성

  • 트랜잭션 매니저는 특정 리소스와 연결되어 있음 (Database connection, Hibernate session)
  • 트랜잭션 매니저는 자신과 연결된 리소스를 사용해서 트랜잭션을 제어하고 자동으로 관리함 (생성 - 재사용 - 정리)

 

2. Transaction Synchronization

  • 트랜잭션 시작과 종료 시점에 여러 리소스들을 함께 연결되도록 하는 역할
  • 트랜잭션 컨텍스트에 묶어서 리소스들을 함께 연결시킴

 

동작 순서

단계 설명
트랜잭션 시작
ThreadLocal에 트랜잭션 컨텍스트 생성
리소스들을 트랜잭션 컨텍스트에 연결
트랜잭션 도중
컨텍스트 내의 리소스를 재사용하며 작업 수행
트랜잭션 종료
리소스들을 커밋 또는 롤백
ThreadLocal에서 컨텍스트 해제

 

예제) EntityManager

더보기
@Transactional
public void service() {
    // 트랜잭션 시작됨 → 트랜잭션 컨텍스트 생성
    
    // EntityManager 생성되고 컨텍스트에 등록됨
    EntityManager em1 = entityManagerFactory.createEntityManager();

    // 같은 트랜잭션 범위 내에서 다른 DAO나 서비스도 이 EntityManager를 재사용함
    dao.doSomething(); // 여기서도 같은 em1 사용

    // 작업 후 트랜잭션 커밋 → em1도 정리됨
}

 

실제 도구들

도구 역할
TransactionSynchronization
리소스들이 트랜잭션과 동기화되도록 콜백 인터페이스 제공
AbstractPlatformTransactionManager
위의 동기화 메커니즘을 내부적으로 제어
TransactionSynchronizationManager
스레드별로 트랜잭션 컨텍스트 관리 (바인딩)

 

3. High-level Synchronization Approach

접근 방식 설명 주요 예시 API
Template-based API 데이터 접근 과정을 자동으로 처리 JdbcTemplate
HibernateTemplate
Transaction-aware Proxy 리소스를 트랜잭션 컨텍스트에 등록 및 동기화 EntityManagerProxy
DataSourceProxy
ORM API 연동 ORM에게 Transaction-aware Proxy에서 제공하는 리소스를 연결 EntityManager

 

예제) JdbcTemplate

더보기
@Repository
public class UserRepository {

    private final JdbcTemplate jdbcTemplate;

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

    public List<User> findAllUsers() {
        String sql = "SELECT * FROM users";
        return jdbcTemplate.query(sql, new UserRowMapper());
    }

    public void saveUser(User user) {
        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
        jdbcTemplate.update(sql, user.getName(), user.getEmail());
    }

    private static class UserRowMapper implements RowMapper<User> {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getLong("id"));
            user.setName(rs.getString("name"));
            user.setEmail(rs.getString("email"));
            return user;
        }
    }
}

 

예제) EntityManagerProxy + EntityManager

더보기
@Service
public class OrderService {

    @PersistenceContext
    private EntityManager entityManager; // EntityManagerProxy가 주입됨

    @Transactional
    public void placeOrder(Order order) {
        entityManager.persist(order);
        // 다른 비즈니스 로직...
    }
}

 

4. Low-level Synchronization Approach

  • Spring의 유틸리티 클래스를 사용하여, 애플리케이션 코드가 직접 Native Persistence API의 리소스를 다루도록 하는 방식
유틸 클래스 사용 대상 동작 요약
DataSourceUtils JDBC
현재 트랜잭션에 참여된 커넥션을 반환.
없으면 새로 생성하고 트랜잭션에 동기화
EntityManagerFactoryUtils JPA 트랜잭션에 바인딩된 EntityManager를 반환.
없으면 예외
SessionFactoryUtils Hibernate 트랜잭션에 바인딩된 Hibernate Session 반환.
필요 시 새로 열기도 가능

 

예외) DataSourceUtils

더보기
DataSourceUtils.getConnection(datasource)
  • Spring의 예외 처리 구조로 변환됨 (일관성)
  • SQLException을 CannotGetJdbcConnectionException으로 변환

 

예외) EntityManagerFactoryUtils

더보기
public class JpaRepository {

    private final EntityManagerFactory emf;

    public JpaRepository(EntityManagerFactory emf) {
        this.emf = emf;
    }

    public void save(Member member) {
        EntityManager em = EntityManagerFactoryUtils.getTransactionalEntityManager(emf);
        em.persist(member); // 트랜잭션 바운드 EntityManager 사용
    }
}

 

예외) SessionFactoryUtils

더보기
public class HibernateRepository {

    private final SessionFactory sessionFactory;

    public HibernateRepository(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void save(Member member) {
        Session session = SessionFactoryUtils.getSession(sessionFactory, true); // 트랜잭션 바운드 Session
        session.save(member);
    }
}

 

 

출처