因此,我们正在开发一个高度并发的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)实际使用这种方法的人能说出他得到的一些规则\实现吗?谢谢!
答案 0 :(得分:6)
首先,我想提一下,你的老式风格不应该使用save()
。附加实体自动持久化。 save()
电话无效。对附加实体所做的每个更改都将在提交时(或更早)保留。
我还想指出,与使用更新查询的方法相比,此方法不再存在并发问题。相反:如果使用乐观锁定(使用@Version),那么老式的执行方式比更新查询方法(它只是更新状态而不管某些并发事务是否也更新它)的并发性问题更少。 p>
关于你的不同观点:
不常见,因为这比仅修改实体并让JPA处理更新查询要麻烦得多。处理复杂的用例也要复杂得多,并使代码看起来像JDBC代码,JPA首先允许这样做。
如果您使用更新查询,则会绕过乐观锁定,就像第一级缓存一样。因此,如果您已加载实体并使用更新查询来更新其状态,则加载的实体仍将保持旧状态。
您可以同时使用这两种方法。请注意我在第2点提到的陷阱。
更新查询有意义,特别是如果目标是一次更新多个实体。如果您已经证明存在性能问题,我只会降级到这种方法,并且可以通过使用此方法解决此性能问题。否则,它只是过早优化,具有很高的生产率和稳健性成本。
答案 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
让更新增加版本。