JPA merge()最佳实践

时间:2013-02-13 04:42:58

标签: hibernate jpa concurrency locking pessimistic-locking

我为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多个地方。

那么有没有办法解决这种情况至少应用悲观锁定?

2 个答案:

答案 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注释包装业务逻辑方法。