我的情况:公共(public)方法一开始尝试更新实体,最后尝试查询更新的实体并通过 SNS 发送消息。我遇到了两个问题。
第一个问题是,如果我调用DeviceDao.save()
,我可以获得更新的实体,但如果我调用自定义更新方法updateAsset
,则无法获取更新的实体UpdateClass
中的 code>。
第二个问题是代码仍然会传递到 messagePublisher,即使 UpdateClass.doSth()
抛出 org.hibernate,我也可以在“MessagePublisher.doRealthing()”中获取更新的实体。异常.ConstraintViolationException(我对 Assets ID有唯一的约束),但最终,更新被回滚。
我的问题是,为什么我会遇到这两个问题?对于第二个问题,除了提前查询 Assets Id,如何避免?
这是我的代码。
public interface ExampleDeviceDao extends JpaRepository<Device, UUID>, JpaSpecificationExecutor<Device> {
@Modifying
@Query("UPDATE device a SET a.asset = ?1 WHERE a.device = ?2")
int updateAsset(UUID asset, UUID device);
}
我的公共(public)服务和方法:
@Component
public class Service {
@Autowired
UpdateClass updateClass;
@Autowired
MiddleClass middleClass;
@Autowired
MessagePublisher messagePublisher;
@org.springframework.transaction.annotation.Transactional
public void updateAsset(UUID deviceId, UUID assetId) {
updateClass.doSth(deviceId, assetId);
middleClass.doSth(deviceId, assetId);
messagePublisher.doSth(deviceId, assetId);
}
}
public abstract class AbstractClass {
protected abstract void doRealThing(UUID deviceId, UUID assetId);
public void doSth(UUID deviceId, UUID assetId) {
doRealThing(deviceId, assetId);
}
}
@Component
public class UpdateClass extends AbstractClass{
@Autowired
ExampleDeviceDao deviceDao;
protected void doRealThing(UUID deviceId, UUID assetId) {
Optional<Device> device = deviceDao.findById(deviceId);
if (device.isPresent()) {
device.get().setAsset(assetId);
/** approach 1:*/
deviceDao.save(device.get());
/**
* approach 2:
* deviceDao.updateAsset(assetId, deviceId);
*/
}
}
}
@Component
public class MiddleClass extends AbstractClass{
protected void doRealThing(UUID deviceId, UUID assetId) {
//do other things, not db update or query.
}
}
@Component
public class MessagePublisher extends AbstractClass{
@Autowired
ExampleDeviceDao deviceDao;
@Autowired
SNSMessagePublisher snsMessagePublisher;
protected void doRealThing(UUID deviceId, UUID assetId) {
Optional<Device> device = deviceDao.findById(deviceId);
if (device.isPresent()) {
snsMessagePublisher.publishMessage(device.get());
}
}
}
最佳答案
问题可能是您的第一级缓存(也称为持久性上下文)包含您正在执行 DML 语句(更新设备...
)所针对的实体。但 Hibernate 并不知道您的 DML 语句将更改持久性上下文中实体的状态,并且对此也无法执行任何明智的操作。因此,更改是在数据库上执行的,但持久性上下文中的实体仍然具有旧状态。您可以做的唯一明智的事情是刷新实体(如果需要),或者通过调用 EntityManager.detach() 将其从持久化上下文中删除。
据我了解您的第二个问题,即使数据库事务失败,您的发布者代码也会发布 SNS 消息。在这种情况下,您始终会遇到可能的问题,因为 SNS 客户端不是交易的一部分,也不可能成为交易的一部分。
您可以在交易完成后发送消息
- 在
@Transactional
方法返回后调用发布者 - 注册事务提交后回调,请参阅 https://stackoverflow.com/a/15026797/412446
如果事务完成后 JVM 关闭但尚未发送 SNS 消息,或者 SNS 消息由于某种原因发送失败,您可能会错过发送一些消息。
为了解决此类问题,您通常会使用某种具有重试机制的作业队列。如果您对此问题的解决方案感兴趣,可以查看 Blaze-Notify这是一个可以用来有效实现这一点的工具包。您无需立即发送 SNS 消息,而是持久保存一个表示该消息的实体,稍后将对该消息进行异步处理并通过重试机制发送。
关于spring - 无法使用 spring jpa 获取更新的实体并且唯一约束不会停止更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73299764/