yudaocode/SpringBoot-Labs · error · RuntimeException
我就是故意抛出一个异常,测试下事务回滚
Error message
我就是故意抛出一个异常,测试下事务回滚
What it means
A RuntimeException('我就是故意抛出一个异常,测试下事务回滚' — 'intentionally throwing to test transaction rollback') thrown inside a flatMap when a newly inserted user's generated id is even. The endpoint inserts a UserDO via R2DBC and, within a reactive transaction (TransactionalOperator / @Transactional on a reactive method), the exception triggers rollback of the INSERT, demonstrating declarative transactional semantics in WebFlux + spring-data-r2dbc.
Source
Thrown at lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java:89
@Override
public Mono<Integer> apply(UserDO userDO) {
if (userDO != USER_NULL) {
// 返回 -1 表示插入失败。
// 实际上,一般是抛出 ServiceException 异常。因为这个示例项目里暂时没做全局异常的定义,所以暂时返回 -1 啦
return Mono.just(-1);
}
// 将 addDTO 转成 UserDO
userDO = new UserDO()
.setUsername(addDTO.getUsername())
.setPassword(addDTO.getPassword())
.setCreateTime(new Date());
// 插入数据库
return userRepository.save(userDO).flatMap(new Function<UserDO, Mono<Integer>>() {
@Override
public Mono<Integer> apply(UserDO userDO) {
// 如果编号为偶数,抛出异常。
if (userDO.getId() % 2 == 0) {
throw new RuntimeException("我就是故意抛出一个异常,测试下事务回滚");
}
// 返回编号
return Mono.just(userDO.getId());
}
});
}
});
}
/**
* 更新指定用户编号的用户
*
* @param updateDTO 更新用户信息 DTO
* @return 是否修改成功
*/
@PostMapping("/update")View on GitHub (pinned to 6c12efaed0)
Solutions
- Verify a ReactiveTransactionManager bean exists (e.g., R2dbcTransactionManager) and the insert path is wrapped with @Transactional or TransactionalOperator; otherwise the 'rollback' demo commits anyway.
- Ensure the exception is thrown inside the flatMap/map lambda (as shown) so it travels the same chain as the save() publisher.
- Test by inserting rows repeatedly: odd ids persist, even ids roll back — check the table to confirm.
- For production, replace the even/id gimmick with a real failure signal mapped to a business exception.
Example fix
// before: throwing inside nested flatMap — works only if tx wraps the whole chain
return userRepository.save(userDO).flatMap(saved -> {
if (saved.getId() % 2 == 0) {
throw new RuntimeException("我就是故意抛出一个异常,测试下事务回滚");
}
return Mono.just(saved.getId());
});
// after: explicit error signal, same semantics, clearer intent
return userRepository.save(userDO).flatMap(saved ->
saved.getId() % 2 == 0
? Mono.error(new RuntimeException("我就是故意抛出一个异常,测试下事务回滚"))
: Mono.just(saved.getId())); Defensive patterns
Strategy: validation
Validate before calling
// Validate before insert so the demo exception cannot fire unexpectedly:
if (addDTO.getUsername() == null || addDTO.getUsername().isEmpty()) {
return Mono.error(new IllegalArgumentException("username required"));
} Try / catch
// Consumer side, treat rollback as a business failure:
userService.add(addDTO)
.onErrorResume(ex -> {
log.warn("insert rolled back: {}", ex.getMessage());
return Mono.just(-1);
}); Prevention
- Ensure a ReactiveTransactionManager bean exists before relying on rollback.
- Throw/return errors from inside the reactive chain (flatMap/map), never from external threads.
- Verify rollback behavior with an integration test that asserts the row count is unchanged.
When it happens
Trigger: POST /user/add (the enclosing insert flow) where the DB assigns an AUTO_INCREMENT id that is even: userDO.getId() % 2 == 0 causes the throw, so roughly every other insert should roll back. Requires a live R2DBC database (MySQL/PostgreSQL) with the transaction manager configured.
Common situations: Classic reactive-transaction pitfall: the exception only rolls back if it propagates through the reactive chain that the TransactionalOperator wraps. If you throw from a thread outside the chain (e.g., inside subscribe() or an ExecutorService lambda) the rollback never happens, and the row stays committed. Also fails silently if ReactiveTransactionManager is not configured — then there is no transaction at all to roll back.
Related errors
AI-assisted analysis of yudaocode/SpringBoot-Labs@6c12efaed0 (2026-08-14).
Data as JSON: /api/errors/55b4fe079f78c7ce.
Report an issue: GitHub.