我们的Java EE web应用程序使用iBatis(ORM)执行数据库操作。数据库操作流程如下
流程 :JSP-->操作--->ServiceIMpl--->DaoImpl---->通过'IBatis 调用更新查询
注意 :操作、服务和;DAO类是使用Spring2.5的依赖注入技术实例化的。我们使用Struts2。
问题 :两个并发用户搜索同一记录,User1更新Attribute1,而User2更新Attribute2。User1首先单击"保存",然后User2单击保存(它们相互跟随几秒钟)。当我们在数据库中看到数据时,只显示User1的更新。Audit列也只显示User1的更新。User2对Attribute2的更新没有完成,同时没有引发异常。
我们尝试在iBatis中使用begin事务和commit事务来调用sqlmap.xml中定义的更新查询。同样的问题依然存在。我们还尝试删除那些事务命令,但问题仍然存在。
在更新过程中,我们应该做一些特殊/不同的事情来处理并发性吗?像iBatis这样的ORM难道不能自己处理吗?
其他信息
:进一步调查揭示了以下信息
当用户1&用户2点击一条记录,它的完整数据就会被提取出来查看。
现在当User1&User2更改了一些属性并单击保存(同时),例如首先更新User1的数据,然后在更新User2的数据时,重写User1的已经更新的数据&迷路的
What approach is usually followed in such screens to handle such scenarios ?
您的问题不在于事务,而是在修改某个内容之前,您必须确保自上次读取以来没有人修改过它。
在web应用程序中,应该使用乐观锁定。基本上,您的表中有一个"version"字段,其初始值为insert
(提示为1),并随着每个update
而递增。程序是:
-
从表中读取数据,包括版本字段
-
向客户端发送数据,包括版本字段
-
从客户端接收修改后的数据,包括版本字段未更改
-
更新表并增加版本,,但前提是行中的版本字段与从客户端收到的原始字段相同。也就是说,你有
update ... where id = :id_from_client
,现在你应该做
update ..., version = version + 1 where id = :id_from_client and version = :version_from_client
-
获取更新的行数(通常可以从持久性框架的更新操作中获得这些信息)。如果更新的行数为1,则操作成功。如果更新的行数为0,则应该发出并发错误的信号。
一个例子:
-
User1获取数据,版本=17
-
User2获取数据,版本=17
-
User1原子性地(原子性来自于单个SQL语句):
- 检查版本是否为17
- 更新数据
将版本设置为18
-
User2原子:
- 检查版本是否为17
- 由于版本实际上是18,发出并发错误信号并中止操作,通知用户由于其他人修改了相同的数据而无法完成操作
大多数当前的持久性框架都支持开箱即用,而且大多是自动的(例如,请参阅JPA@Version注释)。iBatis似乎没有,但由于它非常接近SQL,您自己实现它应该不会有太多问题。有关iBatis的乐观锁定的一些详细信息,请参阅此博客条目。
有了这个方案,你就不会丢失更新,唯一的问题是,某些特定情况会让用户非常恼火:想象一下,你花了5分钟填写一些表格,但最后却被告知你的操作无法完成,你需要从一开始就重试!!在这几种情况下,您应该在乐观锁定的基础上实现一些更复杂的东西。我会使用某种远程资源租赁(链接??):
-
客户端向服务器请求数据的租约。
-
如果数据已租给另一个客户端,则发出并发错误信号。
-
否则,将租约授予客户有限的时间。
-
在租约到期之前,服务器授予客户端对数据的独占访问权限。客户端还可以请求续订租约(例如,通过AJAX)。服务器可以接受或拒绝续订。(租用时间和续订策略应由服务器根据正在处理的特定用例来决定)。
-
租约到期(例如,这可以通过服务器上的计划任务来完成)。租约到期后,数据应被视为"未锁定",服务器应拒绝访问客户端,除非客户端被授予新租约。
通过这种方式,您的用户甚至可以在开始他们试图完成的任何冗长操作之前就被告知有人正在修改相同的数据。
Ibatis不执行锁定。看到您的场景乐观锁定可以解决问题。要实现乐观锁定,您可以选择。1.使用支持事务管理的框架,如org.springframework.jdbc.datasource。DataSourceTransactionManager。它们提供传播行为和隔离级别。但是,您正在使用的数据源必须支持传播级别和隔离级别。Spring提供以下隔离级别。.sisolation_DEFAULT.sisolation_READ_UNCOMMITTED.sisolation_READ_COMMITTED.隔离_可重复读取.sisolation_SERIALIZABLE。2.接下来,您可以提供乐观锁定的实现。