트랜잭션(1) - 트랜잭션이란? 자바 API 별 트랜잭션 처리
들어가기 전에
최근 개발을 하다가 @Transactional 이라는 어노테이션을 사용하면서 궁금증이 생겨 트랜잭션에 대해 찾아본 내용을 정리했습니다.
목차
- 트랜잭션이란?
- 자바 API별 트랜잭션 처리
- JDBC
- JPA
- JMS
- JTA
- 스프링의 트랜잭션 처리(To be continued...)
트랜잭션이란?
트랜잭션의 사전적 의미
1. 거래 행위를 할 때 이뤄지는 작업
2. 어떤 행위를 할 때 이뤄지는 과정
출처 : Oxford Learner's Dictionaries
프로그래밍에서 트랜잭션이란 하나의 논리적인 작업을 수행하기 위해 필요한 연속적인 작업의 묶음을 의미합니다. 연속적인 작업 도중 하나라도 실패하면, 모든 작업을 작업 실행 이전의 상태로 되돌려야 합니다. 트랜잭션에서 작업을 수행하는 방법에는 대표적으로 데이터베이스, 메세지 큐 등이 있습니다.
자바 API별 트랜잭션 처리
1. JDBC(Java Database Connectivity)
JDBC는 데이터베이스에 접근하기 위한 자바 API입니다.
Connection connection = DriverManager.getConnection(CONNECTION_URL, USER, PASSWORD);
try {
//자동 커밋 설정을 끔
connection.setAutoCommit(false);
PreparedStatement firstStatement = connection .prepareStatement("UPDATE ACCOUNT SET BALANCE = 1000 WHERE MEMBER_ID = 1 ");
firstStatement.executeUpdate();
PreparedStatement secondStatement = connection .prepareStatement("UPDATE ACCOUNT SET BALANCE = 0 WHERE MEMBER_ID = 2 ");
secondStatement.executeUpdate();
connection.commit();
} catch (Exception e) {
connection.rollback();
}
JDBC의 Connection은 기본 설정으로 statement마다 auto-commit이 활성화되어 있으나, 위 코드 예시처럼 여러 개의 쿼리를 하나의 트랜잭션으로 묶어주기 위해서 Connection.setAutoCommit(false)로 설정하면 commit() 이 호출되기 전까지 커밋이 되지 않습니다.
2. JPA(Java Persistence API)
JPA의 EntityManager는 여러 개의 엔티티를 하나의 영속성 컨텍스트에서 관리하기 위한 인터페이스를 제공합니다. 영속성 컨텍스트의 기본 범위는 트랜잭션 단위로 수행되는데, 아래에서처럼 수동으로 EntityManager를 생성하고 트랜잭션 범위를 지정해줄 수 있습니다.
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpa-example");
EntityManager entityManager = entityManagerFactory.createEntityManager();
try {
entityManager.getTransaction().begin();
entityManager.persist(firstEntity);
entityManager.persist(secondEntity);
entityManager.getTransaction().commit();
} catch (Exception e) {
entityManager.getTransaction().rollback();
}
3. JMS(Java Messaging Service)
JMS는 메세지를 통해 비동기 통신을 지원하는 자바 명세입니다. JMS API는 한 번의 트랜잭션 안에서 여러 개의 메세지를 송신/수신할 수 있도록 지원합니다. 단, 같은 트랜잭션 안에서 송신/수신이 동시에 이루어 질 수는 없고, 송신은 송신대로, 수신은 수신대로 각각 여러 개의 메세지가 한 트랜잭션 안에서 수행될 수 있습니다.
connection.start();
try {
Session session = connection.createSession(true, 0);
Destination = destination = session.createTopic("TEST.FOO");
MessageProducer producer = session.createProducer(destination);
producer.send(firstMessage);
producer.send(secondMessage);
session.commit();
} catch (Exception e) {
session.rollback();
}
4. JTA(Java Transaction API)
위 세 가지 방법에서는 각각 DB면 DB, 메세지면 메세지처럼 형태가 같은 자원에 대해서만 트랜잭션 처리를 합니다. 한 트랜잭션 안에 데이터베이스와 메세지 큐를 모두 담고 싶다면 어떻게 해야할까요? 이런 상황을 위해 JTA는 TransactionManager를 통해 데이터베이스, 메세지 큐 등 자원을 넘나들며 트랜잭션 처리를 지원합니다.
try {
//트랜잭션 시작
userTransaction.begin();
//데이터베이스 작업
java.sql.Connection dbConnection = dataSource.getConnection();
PreparedStatement preparedStatement = dbConnection.prepareStatement(SQL_INSERT);
preparedStatement.executeUpdate();
//메세지 작업
javax.jms.Connection mbConnection = connectionFactory.createConnection();
Session session = mbConnection.createSession(true, 0);
Destination destination = session.createTopic("TEST.FOO");
MessageProducer producer = session.createProducer(destination);
producer.send(MESSAGE);
//트랜잭션 커밋
userTransaction.commit();
} catch (Exception e) {
userTransaction.rollback();
}
Spring에서의 트랜잭션 처리
지금까지 다양한 JAVA API에서 트랜잭션 처리를 하는 법에 대해 알아보았습니다. 뭔가 공통된 점이 보이는 것 같기도 합니다. 사용하는 객체나 작업 내용은 조금씩 달라도 어떤 공통된 형태를 가지고 있는 것 같습니다!
트랜잭션 시작
작업 1
작업 2
...
작업 N
작업 N까지 무사히 성공시, 트랜잭션 커밋
도중에 실패한다면 트랜잭션 롤백
이런 형태가 보이나요? 스프링은 이렇게 공통된 형태를 추상화한 모델을 제공합니다. 따라서 훨씬 간단하고 쉽게 트랜잭션 처리를 할 수 있습니다. 대표적으로 @Transactional이라는 어노테이션 방식으로 선언적 트랜잭션 관리를 수행할 수 있으며, 코드를 통해 더 세밀한 설정을 해주어야 할 경우에도 스프링을 이용하면 JAVA API를 통해 트랜잭션 관리를 수행하는 것보다 더 쉽게 설정할 수 있습니다.
다음 편에서는 스프링에서 트랜잭션 관리를 어떻게 지원하는지에 대해 알아보겠습니다.
출처
https://www.baeldung.com/java-transactions
https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html