如何锁定事务以读取行然后插入Hibernate?

时间:2010-05-25 08:25:09

标签: mysql hibernate transactions insert locking

我有一个名称和name_count的表。因此,当我插入新记录时,我首先检查该名称的最大name_count是多少。然后我插入最大+ 1的记录。工作得很好...除了mysql 5.1和hibernate 3.5,默认情况下读取不遵守事务边界。其中2个相同名称的插入可能同时发生,并以相同的name_count结束,这完全搞砸了我的应用程序!

不幸的是,在某些特定情况下,上述情况实际上相当普遍。那我该怎么办?我假设我可以做一个悲观的锁,我读取的任何行都被锁定以进一步读取,直到我提交或回滚我的事务。或者我可以使用版本列进行乐观锁定,该列会自动继续尝试直到没有冲突?

对于我的情况,最好的方法是什么?如何在Hibernate 3.5和mysql 5.1中指定它?上表非常庞大且经常访问。

3 个答案:

答案 0 :(得分:3)

这就是大多数人使用SEQUENCE创建唯一数字的原因。也就是说,你必须做的是锁定整个表(LOCK TABLES)。问题:您必须锁定所有所需的表(即Hibernate可能触摸的任何内容),否则您将收到错误。接下来,您必须解锁表,并与事务的其余部分同步执行这两个操作。

我希望这可以让你了解为什么人们使用序列:他们有一些小的缺点,比如差距,但其他一切都更糟。

[编辑]您可以定义序列,然后使用本机SQL查询来获取下一个值。您可以尝试定义序列生成器(请参阅the docs),但可能只允许在Id字段上进行映射。

Re“2亿名”:只要数据库可以存储数字,你也可以在其上定义一个序列。

重新“基于行的锁定”:您打算锁定哪一行?最大值的那个?我不确定max()运算符会在您锁定时停止。您可以尝试的是a trigger。由于触发器是原子的,因此在运行时没有人可以插入行。但触发器有点难以维护。

答案 1 :(得分:0)

MySQL不支持序列,因此应该模拟它们。这是一个有趣的食谱:http://forums.mysql.com/read.php?61,143867,194058#msg-194058

请注意,表类型是MyISAM,不尊重事务并发性,并在每个请求中返回下一个计数器值。

答案 2 :(得分:0)

如果你想让它与数据库无关(避免序列)而不必在插入try之前运行max()函数,而不是使用guid:

/**
 * The unique id. This id is generated this object is persited. The id is a 32 character UUID.
 * This gives each entity a completely unqique identifier. This is completely unique across all
 * databases, jvms and entities.
 */
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(length = 32, name = EntityObject.Columns.ID)
@DocumentId
private String id;

这样做的好处是,您的主键在数据库中的每个表中都是唯一的,这非常适合分布式数据库。

我们已将它用于许多数据库,我们可以轻松地在它们之间移动。