【问题标题】:Duplicate inserts重复插入
【发布时间】:2012-07-13 22:45:00
【问题描述】:

在并发请求的情况下,我遇到了重复插入的以下问题。

我有两张桌子:

create table tbl (
  val varchar2(1000)
);

create table tbl2 (
  val varchar2(1000)
);

我需要在表tbl2 中插入一些值,以防表tbl 为空。因此,在我的 Java 代码中,我使用隔离级别 = READ COMMITED 进行事务处理:

//start transaction
int result = jdbcTemplate.queryForInt("select count(*) from tbl");
if (result == 0) {
  int update = jdbcTemplate.update("insert into tbl2(val) values(?)", "di" + UUID.randomUUID().toString());
}
//end transaction

这里的问题是:实际上有人可以在if (result == 0) 和我的更新语句之间插入数据。所以我会有重复的条目。

我的例子过于简单,但我的实际情况要复杂得多,但基本思想是一样的:我需要在插入之前从 Javacode 中进行几次选择。

那么,如何避免这种情况(我对 db 端解决方案和 java 端解决方案感兴趣)?

附:数据库是 Oracle。

【问题讨论】:

  • 实际上我使用 2 个不同的应用程序来模拟这种情况:来自 IDE 的 java 代码,我将断点放在“if (result == 0)”行和 SQL 开发人员插入新行并执行提交

标签: java sql transactions transactionscope


【解决方案1】:

我认为处理这种情况的最佳方法可能是在数据库级别。您可以使用如下查询:

insert into tbl2 values(xx) where not exists (select 1 from tbl1)

所以你的查询可能会变成这样

int update = jdbcTemplate.update("insert into tbl2(val) values(?) where not exists (select 1 from tbl1)", "di" + UUID.randomUUID().toString());

【讨论】:

  • 好点。但不幸的是,我的真实代码结合使用了 Hibernate 和 spring jdbc 模板。
  • 好吧,我已经尝试过您的解决方案。但我仍然能够重现我的情况
【解决方案2】:

不太清楚你为什么要做你正在做的事情,但你可以先锁定桌子。

LOCK TABLE tbl IN SHARE MODE;

记得提交;释放锁。

【讨论】:

  • 附注谨慎选择您的锁定模式:docs.oracle.com/cd/B19306_01/server.102/b14200/…
  • 是否有其他好的替代方法可以在不锁定表的情况下执行此操作?
  • P.S.我想这样做,因为例如我只想在有足够的指定类型的项目时才创建新订单...
  • 如果你想阻止某人插入一行,这是唯一的方法,虽然我不推荐它。
  • 另一种方法可能是将 BEFORE INSERT 触发器添加到 tbl2 以检查是否允许插入。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2015-08-07
  • 1970-01-01
相关资源
最近更新 更多