编辑:请查看此问题的结尾,了解导致错误的原因以及我的发现方式。
当我运行一个将数据批量插入到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表达式更改为与另一个表达式相同,每五秒运行一次,解决了问题。
要找出错误,我必须在hibernate.cfg.xml中设置true并禁用表上的主键约束。
-- 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;
答案 0 :(得分:4)
您有一个独特的复合约束。 ORA-00001表示您有两行或多行在ServiceID,Date,Value1和/或Value2中具有重复值。你说两个数据库的输入是相同的。所以:
更可能的解释是第二个:您的一个或多个键列由外部源或默认值填充(例如,ServiceId的代码表或日期列的SYSDATE)。在失败的数据库中,此自动填充无法提供唯一值。根据您使用的机制,可能有多种原因可能会出现这种情况。请记住,在唯一的复合键中,NULL条目计数。也就是说,你可以有任意数量的记录(NULL,NULL.NULL,NULL)但只有一个(42,NULL,NULL,NULL)。
我们很难猜出实际问题可能是什么,而且几乎和你一样难(虽然你确实有成为代码作者的优势,应该给你一些见解)。你需要的是一些跟踪声明。我首选的解决方案是使用Bulk DML Exception Handling但我是PL / SQL粉丝。 Hibernate允许你挂钩一些日志记录到你的程序:我建议你打开它。当代码具有良好的仪器时,代码很容易调试。
作为最后的手段,在运行批量插入之前禁用约束。之后重新启用它:
alter table t42
enable constraint t42_uk
exceptions into my_exceptions
/
如果你有重复的行,这将失败,但至关重要的是MY_EXCEPTIONS表将列出所有冲突的行。这至少会给你一些关于复制源的线索。如果您还没有例外表,则必须运行脚本:$ORACLE_HOME/rdbms/admin/utlexcptn.sql
(您可能需要DBA才能访问此目录)。
tl; dr
洞察力需要信息:检测您的代码。
答案 1 :(得分:1)
有问题的是EE,而另一个看起来像SE数据库。我希望第一个是更快的硬件。如果是这种情况,并且您的日期列使用SYSDATE填充,那么很可能时间分辨率不够;你得到重复的日期值。如果数据的其他列也不唯一,则会获得ORA-00001。
这是一个很长的镜头但乍一看我会朝这个方向看。
您可以使用异常表来识别数据吗?见Reporting Constraint Exceptions
答案 2 :(得分:1)
我的猜测是服务ID。无论使用什么service_id hibernate用于'新鲜'插入,都已经被使用了。
可能该表在一个数据库中为空,但在另一个数据库中填充
我打赌虽然service_id是序列生成的,但序列号与数据内容不同步。所以你在表中有相同的1000行但是
SELECT service_id_seq.nextval FROM DUAL
一个数据库中的给出的数字低于另一个数据库。我在序列创建的地方看到了很多(例如,源代码控制之外),并且数据已从另一个数据库导入到表中。