【发布时间】:2018-01-01 13:19:24
【问题描述】:
我的 Java 项目使用纯 JDBC 与 Oracle DB (v. 12) 交互。事务隔离级别为 Read Committed。
我有一个高度非规范化的表,它将一个实体存储在一组行中。我无法改变这一点。不幸的是,这张桌子必须保持这种状态,原因与我无关。
+------+------+---------+
| date | hash | ....... |
+------+------+---------+
| date | xyz | ....... |
| date | xyz | ....... |
| date | xyz | ....... |
我有两列标识一个实体 - 一个日期和一个哈希。由于每个实体都存储为多行,因此这些列并不是真正唯一的,也不是主键,而只是索引列。我仍然想强制执行一种“唯一性”,这意味着当时只存在一个实体,无论它由多少行组成。
这样的实体一天可以更新几次,产生不同的值,但也有不同的行数。
为了实现这一切,每次更新实体时,我都会在单个事务中执行两个或多个查询:
delete from "table" where "date" = ? and "hash" = ?
insert into "table" values (?, ?, .....)
insert into "table" ....
... -- as many inserts as needed to store whole entity
这适用于单个应用程序实例。不幸的是,我有 2 个实例同时工作,试图几乎同时存储 完全相同的数据(它们只是主备份实例,但备份也在持续存在 - 这我也没有影响)。
如果这是规范化的表,解决方案是使用 MERGE 语句,但在这里不起作用。
我目前的解决方案:
到目前为止,我尝试做的是再添加一列,实例的 ID 持久化,然后使用 SELECT 作为数据源执行 INSERT 语句,并将条件设置为 SELECT,该日期/哈希必须没有数据和应用 ID,否则 SELECT 不提供要插入的数据。
我认为它会起作用,但显然它不起作用。我仍然看到重复。我认为这是因为两个事务首先进行了删除,仍然看不到其他事务尚未提交的数据,因此自己执行插入。然后“提交”是执行和繁荣。两个事务都插入它们的数据。
我考虑过的其他方法:
我想乐观锁定也不起作用,因为在最终版本检查中,两个事务仍然可以认为版本没有被更改,而它们实际上同时被两个事务同时更改并且即将以这种方式提交.
我知道我可以将事务隔离切换到 SERIALIZABLE,但它也不是完美的(首先,Oracle 驱动程序不会序列化查询,但会采取乐观的方法并在并发修改的情况下失败并出现错误,我不这样做'不喜欢这样,它是一种“异常编程”范式,一种反模式,那么第二个缺点当然是性能)。
对于这样的问题还有其他解决方案吗?
【问题讨论】:
-
拥有多个应用程序实例并不一定意味着您可以(或应该)拥有多个数据库实例。我认为最简单的解决方案是只拥有一个数据库。
-
你已经有这么多反模式,声称你不能使用
SERIALIZABLE因为它是一个反模式是可笑的。 -
INSERT 语句是否总是插入具有完全相同日期和哈希值的记录,然后 DELETE 会删除这些记录?或者,DELETE 删除具有一个日期/哈希的记录(例如
2017-12-12/123),但 INSERT 在同一事务中插入其他值(例如2017-12-15/321)? -
@Kayaman:表格的设计方式不取决于我,但必须处理它的代码由我完成,这部分我想尽可能避免反模式尽可能。
-
@krokodilko:DELETE 删除与插入时相同的日期/哈希。这是一种对任意数量的记录进行 UPDATE 的方法(要求更新前后的记录数量可能会发生变化)。除非您知道更好的方法吗?
标签: java sql database oracle jdbc