REQUIRED
이미 진행중인 트랜잭션이 있다면 해당 트랜잭션 속성을 따르고, 진행중이 아니라면 새로운 트랜잭션을 생성한다.
REQUIRES_NEW
항상 새로운 트랜잭션을 생성한다. 이미 진행중인 트랜잭션이 있다면 잠깐 보류하고 해당 트랜잭션 작업을 먼저 진행한다
MANDATORY
부모 트랜잭션에 합류시킨다. *없다면 Exception을 발생시킨다.*
SUPPORTS
메소드가 트랜잭션을 필요로 하지는 않지만, 진행 중인 트랜잭션이 존재하면 트랜잭션을 사용한다는 것을 의미한다. 진행 중인 트랜잭션이 존재하지 않더라도 메소드는 정상적으로 동작한다.
NEVER
메소드가 트랜잭션을 필요로 하지 않는다. 만약 진행 중인 트랜잭션이 존재하면 Exception이 발생한다.
NESTED
부모 트랜잭션이 존재하면 부모 트랜잭션에 중첩시키고, 부모 트랜잭션이 존재하지 않는다면 새로운 트랜잭션을 생성한다.
현재 트랜잭션이 있으면 중첩 트랜잭션 내에서 실행하고, 그렇지 않으면 REQUIRED 처럼 동작한다.
// TestService
@Service
@RequiredArgsConstructor
public class TestService {
private final TodoRepository todoRepository;
private final Test2Service test2Service;
@Transactional
public void test() {
Todo findTodo = todoRepository.findById(1L).orElseThrow(() -> new RuntimeException("not found todo"));
findTodo.updateTitle("수정합니다.");
test2Service.test2();
throw new RuntimeException();
}
}
// Test2Service
@Service
@RequiredArgsConstructor
public class Test2Service {
private final TodoRepository todoRepository;
@Transactional
public void test2() {
Todo findTodo = todoRepository.findById(2L).orElseThrow(() -> new RuntimeException("not found todo"));
findTodo.updateTitle("수정합니다.");
}
}
@Transactinal 의 기본값은 REQUIRED 로 이미 생성된 트랜잭션 속성을 따른다.
위와 같이 마지막에 에러를 던지면, 모두 rollback 된다.

@Service
@RequiredArgsConstructor
public class Test2Service {
private final TodoRepository todoRepository;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test2() {
Todo findTodo = todoRepository.findById(2L).orElseThrow(() -> new RuntimeException("not found todo"));
findTodo.updateTitle("수정합니다.");
}
}
Test2Service의 test2 메서드에 트랜잭션 전파 속성을 REQUIRES_NEW 로 변경하면, 트랜잭션이 새로 생기게 된다. 그러므로 test 메서드의 트랜잭션에 해당하는 부분만 rollback 된다.

// TestService
@Service
@RequiredArgsConstructor
public class TestService {
private final TodoRepository todoRepository;
private final Test2Service test2Service;
public void test() {
Todo findTodo = todoRepository.findById(1L).orElseThrow(() -> new RuntimeException("not found todo"));
findTodo.updateTitle("수정합니다.");
test2Service.test2();
throw new RuntimeException();
}
}
// Test2Service
@Service
@RequiredArgsConstructor
public class Test2Service {
private final TodoRepository todoRepository;
@Transactional(propagation = Propagation.MANDATORY)
public void test2() {
Todo findTodo = todoRepository.findById(2L).orElseThrow(() -> new RuntimeException("not found todo"));
findTodo.updateTitle("수정합니다.");
}
}
MANDATORY 는 이미 진행중인 트랜잭션이 있어야만, 작업을 수행하기 때문에 진행중인 트랜잭션이 없으므로 아래와 같이 에러를 던진다.

@Service
@RequiredArgsConstructor
public class TestService {
private final TodoRepository todoRepository;
private final Test2Service test2Service;
@Transactional
public void test() {
Todo findTodo = todoRepository.findById(1L).orElseThrow(() -> new RuntimeException("not found todo"));
findTodo.updateTitle("수정합니다.");
test2Service.test2();
throw new RuntimeException();
}
}
@Service
@RequiredArgsConstructor
public class Test2Service {
private final TodoRepository todoRepository;
@Transactional(propagation = Propagation.SUPPORTS)
public void test2() {
Todo findTodo = todoRepository.findById(2L).orElseThrow(() -> new RuntimeException("not found todo"));
findTodo.updateTitle("수정합니다.");
}
}
SUPPORTS 는 메소드가 트랜잭션을 필요로 하지는 않지만 진행중인 트랜잭션이 있으면 사용한다는걸 의미한다. 그러므로 위 코드는 모두 rollback 된다.