EJB란 Enterprise JavaBeans의 약어로 쉽게 말하자면 애플리케이션의 업무로직을 가지고 있는 서버 애플리케이션이다.
우선 EJB Transaction을 정리하는 이유는 회사에서 트랜잭션을 다루는데 기존 사용법, 그리고 내가 제대로 알지도 못하고 사용함으로써 트랜잭션 관리가 불안해서 정리하게 되었다.
EJB Transaction은 Container에 의해 트랜잭션이 관리되거나 Bean에서 트랜잭션을 관리할 수 있다고 한다.
EJB Container/Servers are transaction servers and handles transactions context propagation and distributed transactions. Transactions can be managed by the container or by custom code handling in bean's code.
Container Managed Transactions− In this type, the container manages the transaction states.
- 컨테이너에 의해 관리
Bean Managed Transactions− In this type, the developer manages the life cycle of transaction states.
- Bean에서 관리
Container Managed Transactions
REQUIRED− Indicates that business method has to be executed within transaction, otherwise a new transaction will be started for that method.
- 비지니스 메소드가 트랜잭션 내에서 실행되어야 함을 나타낸다. 그렇지 않으면 새로운 트랜잭션이 그 메소드에서 시작된다.
REQUIRES_NEW− Indicates that a new transaction, is to be started for the business method.
- 새로운 트랜잭션이 시작되는 것을 알림.
SUPPORTS− Indicates that business method will execute as part of transaction.
- 메소드가 트랜잭션의 일부에서 실행되어야 함.
NOT_SUPPORTED− Indicates that business method should not be executed as part of transaction.
- 메소드가 트랜잭션의 일부에서 실행되면 안됨.
MANDATORY− Indicates that business method will execute as part of transaction, otherwise exception will be thrown.
- 메소드가 트랜잭션의 일부에서 실행되지 않는다면 예외가 발생한다.
NEVER− Indicates if business method executes as part of transaction, then an exception will be thrown.
- 메소드가 트랜잭션의 일부에서 실행된다면 예외를 발생한다.
Transaction Attribute | Client`s Transaction | Business Method`s Transaction |
---|---|---|
Required | NONE | T2 |
T1 | T2 | |
RequiredNew | None | T2 |
T1 | T2 | |
Mandatory | NONE | ERROR |
T1 | T1 | |
Not Supported | NONE | NONE |
T1 | NONE | |
Supports | NONE | NONE |
T1 | T1 | |
NEVER | NONE | NONE |
T1 | ERROR |
/**
* 테스트1 : insert 1회 트랜잭션.
* TestB의 insert() 메소드 실행도중 예외가 발생한다면 insert된 정보가 롤백된다.
*/
class TestA {
public void insertUser() {
TestB testB = new TestB();
testB.insert();
}
}
@TransactionManagement(TransactionManagementType.CONTAINER)
class TestB {
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean insert() {
//유저 생성 후 db인서트.
}
}
/**
* 테스트2: 반복문 트랜잭션
* TestB의 updateAll() 메소드를 실행하면 반복문에서 updateUser()를 매번 실행한다.
* updateUser()가 실행될 때마다 트랜잭션이 생성되며 메소드가 정상 종료될 때마다 커밋된다.
* 즉 i가 5일 때 예외가 발생했다면 i가 4일때까지의 작업은 모두 DB에 반영되어 있으며 롤백에 영향을 받지 않는다.
*/
class TestA {
public void updateAllUser() {
TestB testB = new TestB();
testB.updateAll();
}
}
@TransactionManagement(TransactionManagementType.CONTAINER)
class TestB {
public void updateAll() {
for(int i=0; i<10; i++0) {
updateUser(i);
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRED_NEW)
public void updateUser(int i) {
User user = getUser();
user.setNum(i);
//유저 업데이트
}
}
Bean Managed Transactions
In Bean Managed Transactions, Transactions can be managed by handling exceptions at application level.
Following are the key points to be considered −
Start− When to start a transaction in a business method.
Sucess− Identify success scenario when a transaction is to be committed.
Failed− Identify failure scenario when a transaction is to be rollback.
아래의 컨트롤러 - 서비스의 관계에서 어노테이션을 어떻게 설정해줘야 할까? UserService에서는 Transaction이 NULL이어야 정상적으로 동작했다. Transaction이 T1 or T2처럼 있을 경우에는 Nested Transaction Not Suppoerted 예외가 발생하는데, 아마 빈에서 트랜잭션을 관리하려고 UserTransaction 인터페이스를 사용하는데 메소드에서 이미 트랜잭션이 존재해서 예외가 발생하는 것 같다.
결론적으로 UserTransaction을 사용하는 메소드에서는 전달된 트랜잭션이 NULL이 되어야 한다는 것. 따라서 나는 아래와 같이 설정했다. TestService에서는 트랜잭션이 존재하지만 UserService에서는 Bean에서 직접 트랜잭션을 관리하기 때문에 NOT_SUPPORTED로 지정하고 UserTransaction 인터페이스를 사용하여 트랜잭션을 관리하였다.
옳게 사용하고 있느지 확신이 안든다...ㅠㅠ
Class ControllerTest {
public String update() {
//TestService의 updateAllUser() 호출;
}
}
Class TestService {
@TransactionAttribute(TransactionAttribute.SUPPORTED)
public void updateAllUser() {
//UserService의 updateAll() 호출;
}
}
Class UserService {
@Resource;
private UserTransaction tr;
@TransactionAttribute(TransactionAttribute.NOT_SUPPORTED)
public void updateAll() {
for(int i=0; i<10; i++) {
tr.begin();
//유저업데이트.
tr.commit();
}
}
}