2

我们的 Java EE Web 应用程序使用 iBatis (ORM) 执行数据库操作。数据库操作流程如下

流程:JSP --->Action--->ServiceIMpl--->DaoImpl---->Calling update query thru' IBatis

注意:Action、Service 和 DAO 类是使用 Spring 2.5 的依赖注入技术实例化的。我们使用 Struts2。

问题:2 个并发用户搜索相同的记录,并且 User1 更新 Attribute1,而 User2 更新 Attribute2。用户 1 首先单击“保存”,然后用户 2 单击保存(它们彼此跟随,相差几秒钟)。当我们看到数据库中的数据时,只有 User1 的更新存在。Audit 列也只显示 User1 的更新。User2 对 Attribute2 的更新没有完成,同时没有抛出异常。

我们尝试在 iBatis 中使用开始事务和提交事务,围绕在 sqlmap xml 中定义的更新查询的调用。同样的问题仍然存在。我们还尝试删除这些事务命令,但问题仍然存在。

我们是否应该在更新期间做一些特殊/不同的事情来处理并发?像 iBatis 这样的 ORM 不会自己处理吗?

附加信息:进一步调查显示以下信息。

当用户 1 和用户 2 单击记录时,将获取完整的数据以供查看。

现在,当 User1 和 User2 都更改了一些属性并单击保存(同时)时,假设首先更新 User1 的数据,然后在更新 User2 的数据时,User1 已经更新的数据被覆盖并丢失。

What approach is usually followed in such screens to handle such scenarios ?

4

2 回答 2

9

您的问题不在于事务,而是在您修改某些内容之前,您必须确保自上次阅读以来没有人进行过修改。

在 Web 应用程序中,您应该使用乐观锁定。基本上,您的表中有一个“版本”字段,其初始值为insert(通常为 1),并随着每个update. 程序是:

  1. 从表中读取数据,包括版本字段

  2. 向客户端发送数据,包括版本字段

  3. 从客户端接收修改的数据,包括未更改的版本字段

  4. 更新表并增加版本但前提是行中的版本字段与从客户端收到的原始版本字段相同。也就是说,你在哪里

    update
    ... 
    where id = :id_from_client
    

    ,现在你应该做

    update 
    ..., 
    version = version + 1 
    where 
    id = :id_from_client and 
    version = :version_from_client
    
  5. 获取更新的行数(您通常可以直接从持久性框架的更新操作中获取此信息)。如果更新的行数为 1,则操作成功。如果更新的行数为 0,则应发出并发错误信号。

一个例子:

  1. User1 获取数据,版本 = 17

  2. User2 获取数据,版本 = 17

  3. User1 原子性(原子性来自单个 SQL 语句):

    • 检查版本是否 = 17
    • 更新数据
    • 将版本设置为 18

  4. 用户2原子:

    • 检查版本是否 = 17
    • 由于版本实际上是 18,因此发出并发错误并中止操作,通知用户由于其他人修改了相同的数据,操作无法完成。

大多数当前的持久性框架开箱即用地支持这一点,而且大多是自动的(例如,请参阅JPA @Version 注释)。iBatis 似乎没有,但由于它非常接近 SQL,因此您自己实现它应该不会有很多问题。有关使用 iBatis 进行乐观锁定的一些详细信息,请参阅此博客条目。

使用此方案,您不会丢失更新,唯一的问题是某些特定情况会使用户非常恼火:假设您花了 5 分钟完成某个表格,但最后却被告知您的操作无法完成,并且你需要从头开始重试!!在这少数情况下,您应该在乐观锁定之上实现一些更复杂的东西。我会使用某种远程资源租赁(链接??):

  1. 客户端向服务器请求数据租用

  2. 如果数据已租用给另一个客户端,则发出并发错误信号。

  3. 否则,将租约授予客户一段有限的时间

  4. 在租约到期之前,服务器授予客户端对数据的独占访问权限。客户端还可以请求续租(例如,通过 AJAX)。服务器可以接受或拒绝续订。(租用时间和续订策略应由服务器根据正在处理的特定用例决定)。

  5. 租约到期(例如,这可以通过服务器上的计划任务来完成)。当租约到期时,数据应该被认为是“解锁的”,并且服务器应该拒绝对客户端的访问,除非客户端被授予新的租约。

这样,您的用户甚至可以在他们开始尝试完成的任何冗长操作之前就被告知有人正在修改相同的数据。

于 2012-09-27T20:14:27.133 回答
1

Ibatis不执行锁定。看到你的场景乐观锁定可以解决问题。要实现乐观锁定,你可以有选择。1.使用支持事务管理的框架,如org.springframework.jdbc.datasource。DataSourceTransactionManager。它们提供传播行为和隔离级别。但是,您使用的数据源必须支持传播级别和隔离级别。Spring 提供以下隔离级别。.ISOLATION_DEFAULT .ISOLATION_READ_UNCOMMITTED .ISOLATION_READ_COMMITTED .ISOLATION_REPEATABLE_READ .ISOLATION_SERIALIZABLE。2.接下来你可以提供你自己的乐观锁实现。

于 2013-02-18T10:54:15.650 回答