트랜잭션 처리
트랜잭션은 Controller가 정상 실행되면 Commit되고 Exception이 Throw되면 Rollback 된다.
업무 소스 영역에서 명시적으로 Commit 또는 Rollback 처리는 할 수 없다.
트랜잭션을 설정하기 위해 프레임워크는 @Transactional 어노테이션을 제공하고 있다.
1. 기본 설정
@Transactional 어노테이션은 Controller의 메소드에 아래와 같이 설정을 한다.
@PutMapping("/{empNo}")
@Transactional
public EmployeeIO updateEmployee(@PathVariable("empNo") int empNo, @RequestBody EmployeeIO emp) {
emp.setEmpNo(empNo);
EmployeeIO result = employeeService.updateEmployee(emp);
return result;
}
2. Propagation(전파옵션)
트랜잭션의 해당 옵션에 따라 새로운 트랜잭션을 시작하거나, 부모의 트랜잭션을 그대로 사용 할 수 있다.
Propagation | 설명 |
---|---|
REQUIRED |
DEFAULT. 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없는 경우 새로운 트랜잭션을 생성한다 |
REQUIRED_NEW |
부모 트랜잭션을 무시하고, 항상 새로운 트랜잭션을 생성한다. |
SUPPORTS |
이미 시작된 트랜잭션이 있으면 참여하고, 그렇지 않으면 트랜잭션 없이 진행한다. |
NOT_SUPPORTED |
트랜잭션을 사용하지 않게 하고, 이미 트랜잭션이 있으면 보류시킨다. |
MANDATORY |
이미 시작된 트랜잭션이 있으면 트랜잭션에 참여한다. 만일 트랜잭션이 없는 경우에는 예외를 발생시킨다. |
NEVER |
트랜잭션을 사용하지 않도록 한다. 이미 진행 중인 트랜잭션이 있는 경우 예외를 발생시킨다 |
다음은 어노테이션 예제코드이다.
// 항상 새로운 트랜잭션으로 수행한다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int updateEmployee(EmployeeIO emp) {
emp.setEmpNo(empNo);
int result = employeeDbio.updateEmployee(emp);
return result;
}
3. Rollback 옵션
기본적으로 @Transactional 어노테이션은 RuntimeException이 발생할 경우에만 Rollback을 수행한다.
RuntimeException 외에 CheckedException에서도 Rollback을 수행하려면 rollbackFor 속성을 사용하면 된다.
@Transactional(rollbackFor = Exception.class)
public void deleteEmployee(EmployeeIO emp) throws Exception {
emp.setEmpNo(empNo);
employeeDbio.deleteEmployee(emp);
}
4. 분산 트랜잭션
클라우드 환경에서는 하나의 트랜잭션이 분산되어 있는 여러 마이크로 서비스간의 원격 호출을 통해 완료되므로 기존의 트랜잭션 관리 기법으로는 데이터의 정합성을 보장해 주기 어렵다.
프레임워크에서는 이러한 데이터의 정합성을 보장해 주기 위한여 LRA(Long Running Actions)를 제공해 주고 있으며, 개발 도구에서 쉽게 LRA를 개발 할 수 있도록 Template을 제공해주고 있다.
LRA 컨트롤러 생성
LRA 컨트롤러는 [New –> 컨트롤러] 메뉴를 선택한 후 타입 이름과 논리 이름, RequestMapping URL정보를 입력하고, 템플릿을 Long Running Action을 선택하여 생성한다.
다음 화면에서 LRA를 처리하기 위한 추가 정보를 입력할 수 있다.
해당 위자드에서는 LRA 참여(시작), 완료, 취소처리 대한 정보를 입력 할 수 있다.
-
LRA: LRA 참여(시작)하기 위한 정보를 입력하는 영역이다.
-
Complete: LRA가 정상적으로 처리가 완료가 되었을 때 호출 되는 정보를 입력하는 영역이다.
-
Compensate: LRA수행시 예외가 발생항 경우 호출 되는 정보를 입력하는 영역이다.
위자드에 정상적으로 기입한 후 Finish 버튼을 클릭하면 아래와 같은 LRA를 수행하기 위한 기본적인 소스코드가 아래와 같이 생성된다
/**
*
*
* @author sysadmin
*/
@RestController
@RequestMapping("/employee-lra")
@BxmCategory(logicalName="Emp LRA 컨트롤러", description="")
public class EmployeeLraController {
private Logger logger= LoggerFactory.getLogger(getClass());
@RequestMapping(
method= RequestMethod.GET
, path= "/updateEmployee"
)
/*(1)*/@LRA(value= LRA.Type.REQUIRED, timeLimit= 10, timeUnit= ChronoUnit.SECONDS)
public ResponseEntity<String> updateEmployee(
/*(2)*/@RequestHeader( name= LRA_HTTP_CONTEXT_HEADER, required= true) String lraId
)
{
Map<Object, Object> terminationData= new HashMap<>();
//terminationData.put( "key", "value");
/*(3)*/LRAContext.storeTerminationData( terminationData);
// TODO 트랜잭션 내에서 수행할 내용과 결과를 반환 하는 코드를 작성 하십시오.
return ResponseEntity.ok("completed");
}
@RequestMapping(
method= RequestMethod.PUT
, path= "/completeEmployee"
)
/*(4)*/@Complete
public ResponseEntity<String> completeEmployee(
@RequestHeader( name= LRA_HTTP_CONTEXT_HEADER, required= true) String lraId
/*(5)*/, @RequestBody Map<Object, Object> terminationData
)
{
// TODO 트랜잭션이 성공한 경우 수행할 코드를 작성 하십시오.
return ResponseEntity.ok( ParticipantStatus.Completed.name());
}
@RequestMapping(
method= RequestMethod.PUT
, path= "/cancelEmployee"
)
/*(6)*/@Compensate
public ResponseEntity<String> cancelEmployee(
@RequestHeader( name= LRA_HTTP_CONTEXT_HEADER, required= true) String lraId
, @RequestBody Map<Object, Object> terminationData
)
{
// TODO 트랜잭션이 실패한 경우 수행할 코드를 작성 하십시오.
return ResponseEntity.ok( ParticipantStatus.Compensated.name());
}
}
(1) LRA 트랜잭션에 참여하기 위한 속성이 설정된다. 참고로 LRA의 타입 설정은 트랜잭션의 Propagation 의 속성 설정과 거의 동일하다.
LRA.Type.REQUIRED : LRA에 트랜잭션이 수행중이면, 해당 트랜잭션에 참여하고, 트랜잭션이 없으면 새로운 LRA 트랜잭션을 생성한다.
LRA.Type.REQUIRES_NEW : 항상 새로운 LRA 트랜잭션을 생성한다.
LRA.Type.MANDATORY : 이미 시작된 LRA 트랜잭션이 있으면 참여하고, 그렇지 않으면 예외를 발생시킨다. (412 Precondition Failed)
LRA.Type.SUPPORTS : LRA 트랜잭션이 있으면 해당 트랜잭션에 참여하고, 없으면 LRA 트랜잭션 없이 수행한다.
LRA.Type.NOT_SUPPORTS : 항상 LRA 트랜잭션 참여 없이 수행한다.
LRA.Type.NEVER : LRA 트랜잭션을 사용하지 않도록 한다. 이미 진행 중인 LRA트랜잭션이 있는 경우 예외를 발생시킨다. (412 Precondition Failed)
(2) LRA 트랜잭션에 참여가 시작한 경우에는 해당 트랜잭션에 대한 Unique한 ID값을 발행하며, 컨트롤러에서는 해당 값을 가져올 수 있다.
(3) LRA에 참여 시 생성되는 공유 Context로 변경된 데이터를 확정하거나, 복원하기위해 사용된다.
(4) @Complete 어노테이션은 LRA가 정상적으로 수행이 완료가 되었을 때 호출 되는 메소드이다. 해당 메소드에서는 변경 사항에 대해서 확정(완료) 처리를 수행하면 된다.
(5) (3) 에서 생성된 공유 Context를 파라미터로 전달 받아, 변경 사항에 대하여 확정(완료)처리를 하면 된다.
(6) @Compensate 어노테이션은 LRA가 실패하였을 경우에 호출 되는 메소드이다. 해당 메소드에서는 변경 사항에 대해서 취소 처리를 수행하면 된다.