如何通过iBatis防止UPDATE中的并发问题

时间:2012-09-27 03:22:38

标签: spring concurrency struts2 ibatis

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

流程 :JSP --->操作---> ServiceIMpl ---> DaoImpl ---->通过'IBatis调用更新查询

注意 :行动,服务& DAO类使用Spring 2.5的依赖注入技术进行实例化。我们使用Struts2。

问题 :2个并发用户搜索相同的记录,User1更新Attribute1,而User2更新Attribute2。 User1首先点击“保存”,然后User2点击保存(它们相互跟随,差几秒)。当我们在数据库中看到数据时,只存在User1的更新。审核列还仅显示User1的更新。 User2对Attribute2的更新未完成,同时不会抛出异常。

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

我们是否应该在更新期间执行任何特殊/不同的处理并发操作?像iBatis这样的ORM不会自己处理吗?

其他信息 :进一步调查显示以下信息。

当User1& User2点击一条记录,获取完整的数据供查看。

现在当User1和& User2改变了一些属性并点击保存(同时),比如说第一个User1的数据被更新,然后当User2的数据被更新时,User1的已经更新的数据被覆盖&丢失。

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

2 个答案:

答案 0 :(得分:9)

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

在网络应用程序中,您应该使用optimistic locking。基本上,您的表中有一个“版本”字段,其初始值为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,则应表示并发错误。

  6. 一个例子:

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

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

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

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

    4. User2原子地:

      • 检查版本是否= 17
      • 由于版本确实为18,表示并发错误并中止操作,通知用户由于其他人修改相同数据而无法完成操作。
    5. 大多数当前的持久性框架都支持开箱即用,并且主要是自动支持(例如,请参阅JPA @Version annotation)。 iBatis似乎没有它,但由于它非常接近SQL,你自己实现它不应该有很多问题。请参阅此博客条目some details about optimistic locking with iBatis

      通过这个方案,你不会得到丢失的更新,唯一的问题是一些特定的情况会使用户烦恼很多:想象你花了5分钟完成一些表格只是为了在最后被告知你的操作不能完成后你需要从一开始就重试!!在这几种情况下,您应该在乐观锁定之上实现更复杂的东西。我会使用某种远程资源租赁(链接??):

      1. 客户端向服务器请求租约以获取数据。

      2. 如果数据已经租给另一个客户端,则表示发生并发错误。

      3. 否则将租约授予客户有限时间

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

      5. 租约到期(例如,这可以通过服务器上的计划任务完成)。当租约到期时,数据应被视为“未锁定”,服务器应拒绝访问客户端,除非客户获得新租约。

      6. 通过这种方式,您的用户可以被告知他们甚至在开始他们试图完成的任何冗长操作之前修改相同的数据

答案 1 :(得分:1)

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