【问题标题】:How to save the order of a table and prevent concurrency issues如何保存表的顺序并防止并发问题
【发布时间】:2013-03-25 09:38:39
【问题描述】:

我有以下问题我必须将某些元素的顺序保存在表格中。这是通过订单号完成的。所以第一个元素有 ordering 1,第十个有 ordering 10 等等。我还保存了一个品牌价值,这意味着订购只在一个品牌的所有行内。 我的配置是 Spring 3.1 和 Hibernate 4.1.6 和 Spring Data,我也使用 JPA 接口,底层是一个 Mysql 数据库。

我现在有一个保存方法,它通过命名查询获取最大值。然后将该值加一。现在使用对象设置器设置值。然后保存。

    if(tag.getOrdering()==0) {
        int newOrderingNumber = tagRepository.getMaximumOrderingNumber();
        newOrderingNumber++;
        if(LOGGER.isDebugEnabled()) {
            LOGGER.debug("Next Ordering Number for Tag is " + newOrderingNumber);
        }
        tag.setOrdering(newOrderingNumber);
    }

我有一个问题,如果两个线程同时执行此操作会发生什么。 他们都会保存相同的价值。但是必须增加两次。 我的问题是如何防止这种情况发生。 首先,我尝试对 spring 事务的隔离级别做一些事情,但是 JPA 不知道事务级别以及我发现如何扩展 jpadialect 的方法,例如这里 (http://amitstechblog.wordpress.com/2011/05/31/supporting-custom-isolation-levels-with-jpa/) 但这没有用,我的 Connection 始终是只读的, readOnly=false 标志设置不正确,我没有找出问题所在。另外我认为这不会解决我的问题。

然后我尝试使用 @Version 属性,但这不起作用,因为我不在一个对象实例上工作。这两个线程在两个不同的行上工作,所以版本控制并不能解决问题。

所以现在我看到了四种不同的方法。

首先:我可以在我的代码中使用同步锁对象,让一个线程等待另一个线程释放对象上的锁

第二:我可以获得一个全表锁,我不知道如何使用hibernate,而且它似乎也太大了以阻塞整个表。

第三:当我没有其中的排序可能相同的品牌列时,我可以添加一个唯一约束,该约束会抛出一个异常,我可以使用它再次增加它,直到它起作用。

第四:现在我找到了另一种可行的方法。我不保存订购号。相反,我添加一列,其中包含按顺序排列的下一个元素的 id。这意味着每个请求中都有一个带有订单的 Join,但我知道这个表没有很多行,所以性能并不重要。此外,订单更改时的更新也会容易得多。

那么解决这个问题的正确方法是什么。当更改顺序时必须更新所有对象时,我还必须解决该问题。

【问题讨论】:

  • 需要数据库事务,其他的都是不好的解决方法。我建议将您的问题重新表述为如何在您的环境中进行事务处理。
  • 我使用 mysql,但据我了解,提供的隔离级别(未提交读取、可重复读取、可序列化、已提交读取)始终是行锁定。当我在两个不同的行上工作并通过从表中选择 max(ordering) 获得最大值时,即使隔离级别也不会阻止两个并发读取,因为读取不在特定行上,正如我所看到的那样(但我对现在也很穷)

标签: java database spring hibernate jpa


【解决方案1】:

使用 mysql 触发器应该可以让您直接检查其他 SO question

【讨论】:

  • 我正在使用 mysql。 (我忘了在我的帖子中提到这一点,我现在添加了它)并且 mysql 不支持像 oracle 这样的序列生成器。此外,我需要一张桌子上的多个序列,因为我在一张桌子上拥有不同品牌的所有元素,并且每个品牌的顺序都不同。这意味着我可以在同一张表中为品牌 1 下订单 1、2、4、5、6、7、8,然后再为品牌 2 下订单
  • 啊,好吧,是的,我明白这让事情变得更加困难,但是那么使用触发器呢?这会是一个选择吗?
  • 效果很好。首先我遇到了问题,因为触发器没有做任何事情。这是因为我的域类中仍然有 Version 属性。此版本标记使休眠执行插入,然后进行更新以更新版本,但它在那里设置旧值,在我的情况下为 null。所以我在插入触发器中的更改被覆盖了。现在我有一个更新触发器,检查空值。这很完美。谢谢。我还添加了另一个解决方案
【解决方案2】:

你试过@OrderColumn吗?在有序列表的情况下如何处理并发是 JPA 提供者的业务(我正在考虑一些限制)。 Here 有序树结构示例。

您也可以尝试使用JPA locks。查看PESSIMISTIC

【讨论】:

  • 阅读@ordercolumn 的文档在我看来,这只能用于另一个域对象内的列表,而不是域对象本身。 jpa 锁使恕我直言行锁定,我在两个不同的行上工作,并从表中读取 select max(value)。我需要 jpa lock 不提供的表锁定
【解决方案3】:

我的问题的另一个解决方案是使 order 列和 brand 列都 UNIQUE ( ADD CONSTRAINT UNIQUE(order,brand) )。那么在一个特定品牌中可能只有一个订单号。然后我必须对 ConstraintViolationException 做出反应,并用新值重新插入。但我感谢它不是很好,因为您不确切知道 constraintViolationException 是否真的是由于 Unique 约束或其他一些 Constraint Violation

【讨论】:

    猜你喜欢
    • 2022-10-26
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 2016-02-23
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多