edit:查看此问题的末尾,了解导致错误的原因以及我是如何发现的。
当我运行一个将数据批量插入 oracle 数据库的应用程序时,我从 Hibernate 中抛出了一个非常奇怪的异常。该错误来自 Oracle 数据库 ORA-00001,该数据库
"表示已尝试 插入包含重复项的记录 (唯一)键。此错误也将 如果现有记录是 更新以生成重复项 (唯一)钥匙。
这个错误很奇怪,因为我在另一台机器上创建了相同的表(完全相同的定义),如果我通过我的应用程序使用它,我不会得到相同的错误。 并且所有数据都入到数据库中,因此没有真正拒绝任何东西。
两种设置之间必须有所不同,但我唯一能看到的不同之处在于我在发布时获得的横幅输出
select * from v$version where banner like 'Oracle%';
给我带来麻烦的数据库: Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod
有效的那个: Oracle Database 10g Release 10.2.0.3.0 - 64bit Production
表定义、输入和我编写的应用程序对于两者是相同的。所涉及的表基本上是一个带有复合 ID (serviceid、date、value1、value2) 的四列表 - 没有什么花哨的。
关于可能出错的任何想法?我已经多次干净开始,删除两个表以在相同的基础上开始,但我仍然从数据库中收到错误。
更多输出:
Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (STATISTICS.PRIMARY_KEY_CONSTRAINT) violated
at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:367)
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:8728)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
我如何找出导致问题的原因
多亏了APC和ik_zelf我才能查明此错误的根本原因。事实证明,Quartz 调度程序为生产数据库(错误出现的地方)配置错误。对于针对非故障 oracle 服务器运行的作业,我<cronTriggerExpression>0/5 * * * * ?</cronTriggerExpression>
每五秒运行一次批处理作业。我认为一分钟一次对于另一个oracle服务器来说就足够了,并将石英调度程序设置为* */1 * * * ?。事实证明这是错误的,而不是每分钟运行一次,而是每秒运行一次!
每个作业大约需要 1.5-2 秒,因此两个或多个作业同时运行,从而导致服务器上同时插入。因此,我没有插入 529 个元素,而是插入了 1000 到 2000 个元素。将 crontrigger 表达式更改为与另一个表达式相同,每五秒运行一次,解决了问题。
为了找出问题所在,我必须在休眠中设置为 true.cfg.xml并禁用表上的主键约束。
-- To catch exceptions
-- to find the offending rows run the following query
-- SELECT * FROM uptime_statistics, EXCEPTIONS WHERE MY_TABLE.rowid = EXCEPTIONS.row_id;
create table exceptions(row_id rowid,
owner varchar2(30),
table_name varchar2(30),
constraint varchar2(30));
-- This table was set up
CREATE TABLE MY_TABLE
(
LOGDATE DATE NOT NULL,
SERVICEID VARCHAR2(255 CHAR) NOT NULL,
PROP_A NUMBER(10,0),
PROP_B NUMBER(10,0),
CONSTRAINT PK_CONSTRAINT PRIMARY KEY (LOGDATE, SERVICEID)
);
-- Removed the constraint to see what was inserted twice or more
alter table my_table
disable constraint PK_CONSTRAINT;
-- Enable this later on to find rows that offend the constraints
alter table my_table
enable constraint PK_CONSTRAINT
exceptions into exceptions;
您有一个唯一的复合约束。 ORA-00001 表示您有两行或更多行在 ServiceID、日期、值 1 和/或值 2 中具有重复值。 您说两个数据库的输入相同。 因此,请执行以下任一操作:
- 您正在想象您的程序正在投掷ORA-00001
- 您误以为两次运行中的输入都相同。
更可能的解释是第二种:一个或多个键列由外部源或默认值填充(例如,ServiceId 的代码表或日期列的 SYSDATE)。 在失败的数据库中,此自动填充无法提供唯一值。 出现这种情况的原因可能有很多,具体取决于您使用的机制。 请记住,在唯一的复合键中,NULL 条目计数。 也就是说,您可以有任意数量的记录(空,空。空,空),但只有一个 (42,空,空,空)。
我们很难猜测实际问题可能是什么,对你来说几乎同样困难(尽管你确实有作为代码作者的优势,这应该给你一些洞察力)。 您需要的是一些跟踪语句。 我的首选解决方案是使用批量DML异常处理,但我是PL/SQL的粉丝。 Hibernate允许您将一些日志记录挂接到您的程序:我建议你打开它。 当代码具有不错的检测时,调试起来要容易得多。
作为最后的手段,请在运行批处理插入之前禁用约束。 之后像这样重新启用它:
alter table t42
enable constraint t42_uk
exceptions into my_exceptions
/
如果您有重复的行,这将失败,但至关重要的是,MY_EXCEPTIONS表将列出所有冲突的行。 这至少会给你一些关于重复来源的线索。 如果您还没有异常表,则必须运行脚本:$ORACLE_HOME/rdbms/admin/utlexcptn.sql
(您可能需要DBA才能访问此目录)。
博士
见解需要信息:检测代码。
有问题的是一个EE,另一个看起来像一个SE数据库。我希望第一个是在更快的硬件上。如果是这种情况,并且您的日期列是使用 SYSDATE 填充的,则很可能是时间分辨率不够;您获得重复的日期值。如果数据的其他列也不唯一,则会得到 ORA-00001。
这是一个很长的镜头,但乍一看我会朝这个方向看。
是否可以使用异常表来标识数据?请参阅报告约束异常
我的猜测是服务 ID。 休眠service_id用于"新鲜"插入的任何内容都已被使用。
可能该表在一个数据库中为空,但在另一个数据库中填充
我敢打赌,尽管service_id是序列生成的,并且序列号与数据内容不同步。所以你在表中有相同的 1000 行,但正在
SELECT service_id_seq.nextval FROM DUAL
在一个数据库中给出的数字比另一个数据库低。我经常看到这种情况,其中已创建序列(例如出于源代码控制)并且数据已从另一个数据库导入到表中。