我为entity object
实施了一项服务,它使用了纯jpa
,我使用了spring
,因此在spring xml config
已配置hibernate
作为jpa
impl。我正在使用spring
数据进行crud
操作。
但是在我们的系统中,我们的entity
对象被拉/更新多次,并且数据存在高度争用(concurrency
)。从许多地方的代码中,许多classes
只是inject
服务bean和调用getEntity
方法来获取实体,之后它们改变实体(根据我的理解分离,但在相同的线程,因此em对象应该与我的知识相同)所以实体需要一段时间才能更新服务,他们会调用服务的save()
方法来保存实体。 Save()
方法只是调用merge()
crud操作。它是@Transactional
注释的事务性。这是一个问题,当有人拉实体对象并在更改它时,其他人可能会拉动并更改它并将其保存回来,所以我的实体读取是脏的,如果保存它,我将覆盖已经更新的实体。问题是我们在服务之外更改实体并调用save。这里spring数据存储库类是DAO层。
Optimistic lock
是一种解决方案,但由于某些原因我们不喜欢它,所以它对我们不起作用。我正在考虑pessimistic lock
。例如,当我通过锁定获取实体进行更新时,然后将其更改为位于不同位置的其他位置并回调(entity
已被锁定以防更新!)它是否有效?我不确定它是否仍然EntityManager
用于拉动实体的对象。如果它存在,那么在更新并解锁之前需要很长时间才能通过这些“智能”逻辑。
以下是该方案的简单示例代码:
class SomeEntity {
Long id;
String field1;
String field2;
String field3;
String field4;
String field5;
String field6;
String field7;
//getters and setters, column annotations
}
class SomeEntityServiceImple implemenet SomeEntityService{
@Transactional
save(SomeEntity se){
//call to spring data's merge() method
}
get(Long id){
//call to spring data's findOne()
}
}
//this class might be spring bean, might not be. If it is not I will get SomeEntityService bean from shared appContext
class SomeCrazyClass{
@Autowired
SomeEntityService service;
someMethod(){
SomeEntity e = service.get(1L);
//do tons of logic here, change entity object, call some another methods and again, takes 3 seond
service.save(e); //Probably detached and someone updated this object, probably overriding it.
}
}
}
在这里,我无法在服务层内移动tons of logic
,它非常具体,这种不同的逻辑在100多个地方。
那么有没有办法解决这种情况至少应用悲观锁定?
答案 0 :(得分:3)
对于悲观锁定,您可以尝试以下代码
获取实体时锁定
entityManager.find(Entity.class, pk, LockModeType.PESSIMISTIC_WRITE);
之后应用锁定
entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE);
在查询中设置锁定模式
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
否则,您可以尝试在SomeEntityServiceImpl
中添加同步方法。
public synchronized void saveEntity(e){
{
Object o = get(id); //-- Fetch most recent entity
applyChanges(o, e); //-- Applying custom changes
save(o); //-- Persisting entity
}
因此,您不必将所有逻辑都移到服务中,而只需委托数据库操作。此外,它将是代码更改的常见位置,而不会影响应用程序逻辑。
答案 1 :(得分:-1)
将@Transactional注释移动到上层到“SomeCrazyClass”的“someMethod”。 使用此方法所做的更改必须是一个事务流的一部分。据我所知,这个流应该是原子的,这意味着它应该全部提交或不提交(完全回滚)。
将@Transactional注释单独放在退化的保存方法之上是缺少此注释的要点。 您仍然可以将其保留在那里(使用propagation.required默认值),但您确定需要将其添加到一级。
您应该在整个应用中遵循此建议。这意味着您应该使用Transactional注释包装业务逻辑方法。