Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 서버구축
- 탐욕법
- 다익스트라 알고리즘
- 동적프로그래밍
- Algorithm
- 토이프로젝트
- mysql
- EC2
- Router
- Spring Boot
- 정렬
- java
- 라우터
- 스터디
- ELB
- sort
- nodejs
- 리액트
- BFS
- 완전탐색
- AWS
- spring
- EventListener
- url parsing
- react
- 백준
- 브루트포스
- 알고리즘
- 자료구조
- 백준알고리즘
Archives
- Today
- Total
공부하는 블로그
Spring | @TransactionalEventListener 본문
@TransactionalEventListener
- 이벤트 처리 로직에서 트랜잭션을 적용해야 하는 경우 사용한다.
- phase 옵션으로 어떤 식으로 트랜잭션 내에서 동작을 할 지 결정할 수 있다.
- TransactionPhase.AFTER_COMMIT
- 기본값
- 트랜잭션이 commit 되었을 때 이벤트 로직을 실행한다.
- TransactionPhase.AFTER_ROLLBACK
- 트랜잭션이 rollback 되었을 때 이벤트 로직을 실행한다.
- TransactionPhase.AFTER_COMPLETION
- 트랜잭션이 완료(commit 또는 rollback) 되었을 때 이벤트 로직을 실행한다.
- TransactionPhase.BEFORE_COMMIT
- 트랜잭션이 commit 되기 전에 이벤트를 실행한다.
- TransactionPhase.AFTER_COMMIT
Example
옵션값 별로 어떻게 동작하는지 테스트해보자. 간단하게 User를 DB에 저장하고 이벤트를 발행하는 서비스를 작성하였다. DB저장은 Spring data JPA를 이용하여 구현하였다.
User
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@ToString
public class User {
@Id
private String id;
@Column(length = 15)
private String password;
@Column
private String name;
User(String id, String password, String name) {
this.id = id;
this.password = password;
this.name = name;
}
public static User from(UserCreationRequest request) {
return new User(
request.getId(),
request.getPassword(),
request.getName()
);
}
public UserDto toDto() {
return new UserDto(this.id, this.name);
}
UserDto
@Getter
@ToString
public class UserDto {
private final String id;
private final String name;
public UserDto(String id, String name) {
this.id = id;
this.name = name;
}
}
UserCreationRequest
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserCreationRequest {
private String id;
private String password;
private String name;
public UserCreationRequest(String id, String password, String name) {
this.id = id;
this.password = password;
this.name = name;
}
}
CustomEventPublisher
@Component
@RequiredArgsConstructor
public class CustomEventPublisher {
private final ApplicationEventPublisher publisher;
public void publish(@NonNull CustomTransactionalEvent event) {
publisher.publishEvent(event);
}
}
UserService
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
private final CustomEventPublisher publisher;
private final UserRepository repository;
@Transactional
public UserDto create(UserCreationRequest request) {
repository.findById(request.getId())
.ifPresent(user -> {
throw new RuntimeException(user.getId() + " is already exist.");
});
final User user = repository.save(User.from(request));
// 이벤트 발행
publisher.publish(new CustomTransactionalEvent());
return user.toDto();
}
}
UserService는 유저 생성 요청을 인자로 받아 DB에 유저 정보를 저장하고 CustomEventPublisher를 통해 CustomTransactionEvent를 발행한다. 발행한 커스텀 이벤트는 아래에 작성한 4가지의 이벤트 리스너에서 처리된다.
@Component
@Slf4j
public class CustomEventListener {
@TransactionalEventListener(
classes = { CustomTransactionalEvent.class },
phase = TransactionPhase.AFTER_COMMIT
)
public void onListenAfterCommit(CustomTransactionalEvent event) {
log.info("##### listening after commit event. event = {}", event);
}
@TransactionalEventListener(
classes = { CustomTransactionalEvent.class },
phase = TransactionPhase.BEFORE_COMMIT
)
public void onListenBeforeCommit(CustomTransactionalEvent event) {
log.info("##### listening before commit event. event = {}", event);
}
@TransactionalEventListener(
classes = { CustomTransactionalEvent.class },
phase = TransactionPhase.AFTER_COMPLETION
)
public void onListenAfterCompletion(CustomTransactionalEvent event) {
log.info("##### listening after completion event. event = {}", event);
}
@TransactionalEventListener(
classes = { CustomTransactionalEvent.class },
phase = TransactionPhase.AFTER_ROLLBACK
)
public void onListenAfterRollback(CustomTransactionalEvent event) {
log.info("##### listening after rollback event. event = {}", event);
}
}
4가지의 리스너는 UserService에서 발행된 CustomTransactionalEvent를 각각 다른 phase 옵션으로 처리한다. 실제로 설명과 같이 동작하는지 테스트해보자! @SpringBootTest를 이용하여 애플리케이션을 실행시킨 후 로그를 확인해보았다.
@SpringBootTest
@ActiveProfiles("test")
class UserServiceTest {
@Autowired
private UserService service;
@Test
@DisplayName("Commit 이벤트 테스트")
void triggerCommitTransactionalTest() {
// given : 정상 commit 처리 되도록 작성
final UserCreationRequest request = new UserCreationRequest("user1",
"password1",
"홍길동");
// when
assertDoesNotThrow(() -> service.create(request));
}
@Test
@DisplayName("Rollback 이벤트 테스트")
void triggerRollbackTransactionalTest() {
// given : column 설정보다 긴 문자열로 일부러 Rollback 유발
final UserCreationRequest request = new UserCreationRequest("user2",
"password_is_tooooooo_long",
"홍길동");
// when
assertThrows(InvalidDataAccessResourceUsageException.class,
() -> service.create(request));
}
}
Test Result
Commit 이벤트 테스트 로그
- Spring : BEFORE_COMMIT 이벤트 리스너 동작.
- DB : Commit Initialize
- DB : Commit
- Spring : AFTER_COMMIT, AFTER_COMPLETION 이벤트 리스너 동작
Rollback 이벤트 테스트 로그
- Spring : BEFORE_COMMIT 이벤트 리스너 동작.
- DB : Commit Initialize
- DB : 오류 발생
- DB : Rollback
- Spring : AFTER_ROLLBACK, AFTER_COMPLETION 이벤트 리스너 동작
'Spring' 카테고리의 다른 글
Spring | MockServer (0) | 2023.09.24 |
---|---|
Spring | Application Event (0) | 2023.09.10 |
Spring | BDD - Given / When / Then (0) | 2023.09.03 |
Spring | Mockito Unit Test (0) | 2023.08.27 |
Spring | Annotations (0) | 2020.09.29 |
Comments