java - 使用 Spring Data JPA 时如何处理 ObjectOptimisticLockingFailureException 和 StaleObjectStateException?
问题描述
我正在开发一个小型 RESTful 服务,它使用定义为和扩展的域对象的spring-boot-starter-data-jpa
依赖关系。该服务使客户能够重置他们忘记的密码。作为第一部分,从客户端接收电子邮件的方法检查具有此类电子邮件的用户是否存在且未被禁用,如果存在 - 为该用户创建一个临时重置令牌,更新它并将带有令牌的特殊链接发送到客户:User
@Entity
UsersRepository
CrudRepository
@RestController
@RequestMapping
@Validated
public class ResetTokenController {
private final UsersRepository usersRepo;
private final JavaMailSender mailSender;
@Autowired
public ResetTokenController(UsersRepository usersRepo, JavaMailSender mailSender) {
this.usersRepo = usersRepo;
this.mailSender = mailSender;
}
@GetMapping(path = "/auth/reset-token", produces = "application/json")
@ResponseStatus(HttpStatus.OK)
public Map<String, String> sendMeResetToken(@RequestParam
@NotBlank(message = "Parameter `email` must not be empty")
@Email(message = "Parameter `email` is not valid")
String email) {
// step #1: fetch user with given e-mail
User user = usersRepo.findByEmail(email)
.filter(User::isEnabled)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"User with given e-mail not found or currently disabled"));
// step #2: create a reset-token for user
String token = UUID.randomUUID().toString();
user.setResetToken(token);
user.setExpBefore(LocalDateTime.now().plusHours(24));
// step #3: save user with his new token and send instructions
usersRepo.save(user);
MimeMessage message = constructHtmlMessage(token, email);
mailSender.send(message);
return Collections.singletonMap("message", "The tokenized link for resetting your password " +
"was sent to the provided e-mail and will be active for the next 24 hours.");
}
// a helper method to construct the mime-message
private MimeMessage constructHtmlMessage(String token, String email) {
// here, we build a link based on the token that's been just created
// and include it in the message along with the instructions details
return mimeMessage;
}
}
然而,上面的代码有一个潜在的缺陷。假设有人(例如,我们服务的管理员)意外地从另一个线程进行干预,并在步骤 #1 和步骤 #3 之间的中间位置删除了正在从数据库中更新的用户。为了避免这种情况下可能出现的不一致,我首先尝试用 包装整个方法@Transactional(isolation = Isolation.REPEATABLE_READ)
,但很快发现这样做没有效果,因为在这种情况下无论如何都会抛出ObjectOptimisticLockingFailureException
with 嵌套,无论方法是否注释。StaleObjectStateException
@Transactional
现在,问题是:如何处理这种异常?就个人而言,我不希望将其翻译给客户端,到目前为止,我做出的唯一决定是将方法的内部结构放入 while 循环中,如下所示:
public Map<String, String> sendMeResetToken(...) {
while (true) {
try {
// #1: fetch user with given e-mail
// #2: create a reset-token for user
// #3: update user and send token
// leave the loop on successful update
return Collections.singletonMap("message", "The tokenized link for resetting your password " +
"was sent to the provided e-mail and will be active for the next 24 hours.");
} catch (ObjectOptimisticLockingFailureException ignore) {
// continue the loop and make another attempt from the start
}
}
}
是否有任何解决方案(也许,更优雅的不使用 while 循环)来处理这种情况,并且在更一般的意义上,在生产中通常如何处理这些异常(我的意思是,ObjectOptimisticLockingFailureException
和)?StaleObjectStateException
解决方案
推荐阅读
- oracle - 从包 A 过程调用时,表插入在包 B 过程中不起作用
- javascript - 在 Quasar 页面消费 Api
- python - PyLint W0108: Lambda may not be > necessary (unnecessary-lambda)
- python - 获取缩写和定义的程序 - 获取所有小写缩写的麻烦
- javascript - 我的sidenav颜色没有随按钮改变(onclick)
- postgresql - Postgres 错误 08001(SQLCLIENT 无法建立 SQLCONNECTION)和 FOC1394 错误
- python - 无论如何使用python将图像导入Visio文档?
- sql - 如何在 knex.js 中正确传递 orderBy 函数的数学函数?
- flutter - 如何在颤动中每 x 秒从 api 获取数据?
- html - 使用反应组件内的标签进行锚导航