我对Spring Data Mongo和Mongo Transactions有疑问。
我已经成功实现了事务,并已通过Spring @Transactional注释验证了提交和回滚工作是否如预期。
但是,我很难让事务按我在Spring Data环境中期望的方式工作。
Spring数据执行Mongo-> Java对象映射。因此,更新内容的典型模式是从数据库中获取内容,然后进行修改,然后将其保存回数据库。在实施事务之前,我们一直在使用Spring的Optimistic Locking来解决获取和更新之间记录发生更新的可能性。
我希望一旦我们能够使用事务,就不会对我的所有更新都包括开放式锁定基础结构。因此,我希望在事务的上下文中,访存会创建一个锁,这样我就可以进行更新并保存,并且我将与外界隔离,以便没人能像以前那样大胆地进行更改。
但是,根据我所看到的,访存不会创建任何类型的锁,因此没有什么可以阻止任何其他连接更新记录,这意味着,尽管有以下操作,但我似乎仍必须维护所有乐观锁代码本地mongodb事务支持。
我知道我可以使用mongodb findAndUpdate方法进行更新,并且不允许进行临时修改,但这与Spring Data的标准模式相反,Spring Data将数据加载到Java Object中。因此,我不仅要操纵Java对象,还必须在应用程序中散布mongo特定的代码,或者为我要进行的每种特定类型的更新创建Repository方法。
在维护仅使用Java对象的Spring Data范例的同时,有人对如何干净地处理这种情况有任何建议吗?
谢谢!
答案 0 :(得分:0)
我找不到任何在Spring / MongoDB事务中进行“读取”锁定的方法。
但是,为了能够继续使用以下模式:
我最终创建了一个执行findAndModify的方法,以便在获取过程中“锁定”记录,然后我可以进行更改并进行保存,所有这些都发生在同一事务中。如果另一个进程/线程在交易过程中尝试更新“锁定”记录,则会阻止该记录,直到我的交易完成为止。
对于lockForUpdate方法,我利用了Spring已经用于乐观锁定的version字段,仅仅是因为它很方便并且可以为简单的锁定操作轻松修改。
我还将我的实现添加到基本存储库实现中,以在所有存储库上启用“ lockForUpdate”。
这是我的解决方案的要点,其中删除了一些特定于域的复杂性:
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleMongoRepository<T, ID>
implements BaseRepository<T, ID> {
private final MongoEntityInformation<T, ID> entityInformation;
private final MongoOperations mongoOperations;
public BaseRepositoryImpl(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
this.entityInformation = metadata;
this.mongoOperations = mongoOperations;
}
public T lockForUpdate(ID id) {
// Verify the class has a version before trying to increment the version in order to lock a record
try {
getEntityClass().getMethod("getVersion");
} catch (NoSuchMethodException e) {
throw new InvalidConfigurationException("Unable to lock record without a version field", e);
}
return mongoOperations.findAndModify(query(where("_id").is(id)),
new Update().inc("version", 1L), new FindAndModifyOptions().returnNew(true), getEntityClass());
}
private Class<T> getEntityClass() {
return entityInformation.getJavaType();
}
}
然后,在事务上下文中,您可以按照以下方式进行调用:
Record record = recordRepository.lockForUpdate(recordId);
...make changes to record...
recordRepository.save();