高并发Web应用程序上的Hibernate \ JPA用法

时间:2014-02-09 09:53:07

标签: java spring hibernate jpa concurrency

因此,我们正在开发一个高度并发的Web应用程序,这是一个非常重要的多人游戏。

我们的服务器使用spring MVC,spring数据,MySQL和JPA。

我们现在讨论我们应该在并发方面使用的方法。

其中一条建议让我走出了我的舒适区:

我习惯使用老式的orm风格:

 @Transactional
 @public void doSomeMagic() {
    User user = userDao.findById(id);
    user.setStuff(stuff);
    ...       
 }

现在,提出的建议如下:

由于上述方法容易出现并发错误(并且可能通过锁定机制得到缓解),为什么我们不使用SQL \ JPQL更新呢?如果我们唯一的目标是更新其字段,为什么甚至打扰取用用户?

我们可以这样做:

 userDao.updatePlayer(stuffParameters);   // "update Player p set stuffParameters...."

现在,我对这种方法有一些疑问:

1)使用更新查询而不是仅通过ORM进行更新的做法有多常见?

2)这种方法的缺陷究竟是什么?

3)有没有混合解决方案?或其他我没有谈过的替代方案?

4)实际使用这种方法的人能说出他得到的一些规则\实现吗?

谢谢!

2 个答案:

答案 0 :(得分:6)

首先,我想提一下,你的老式风格不应该使用save()。附加实体自动持久化。 save()电话无效。对附加实体所做的每个更改都将在提交时(或更早)保留。

我还想指出,与使用更新查询的方法相比,此方法不再存在并发问题。相反:如果使用乐观锁定(使用@Version),那么老式的执行方式比更新查询方法(它只是更新状态而不管某些并发事务是否也更新它)的并发性问题更少。 p>

关于你的不同观点:

  1. 不常见,因为这比仅修改实体并让JPA处理更新查询要麻烦得多。处理复杂的用例也要复杂得多,并使代码看起来像JDBC代码,JPA首先允许这样做。

  2. 如果您使用更新查询,则会绕过乐观锁定,就像第一级缓存一样。因此,如果您已加载实体并使用更新查询来更新其状态,则加载的实体仍将保持旧状态。

  3. 您可以同时使用这两种方法。请注意我在第2点提到的陷阱。

  4. 更新查询有意义,特别是如果目标是一次更新多个实体。如果您已经证明存在性能问题,我只会降级到这种方法,并且可以通过使用此方法解决此性能问题。否则,它只是过早优化,具有很高的生产率和稳健性成本。

答案 1 :(得分:0)

Hibernate通过HQL / JPSQL支持批量更新,请参见example

String jpqlUpdate =
        "update Customer c " +
        "set c.name = :newName " +
        "where c.name = :oldName";
int updatedEntities = entityManager.createQuery( jpqlUpdate )
        .setString( "newName", newName )
        .setString( "oldName", oldName )
        .executeUpdate();

这在批处理的情况下是常见的,当您需要发出修改数据库中的大量实体的修改查询时,但是不希望产生加载内存中所有实体的开销,脏检查它们和刷新数据库的更改。

缺陷是批处理查询可以修改数据库中已经在会话中的对象,在这种情况下,会话中的数据将是陈旧的。

可以使用versioned前面的关键字update让更新增加版本。