相同的Oracle数据库设置:仅对其中一个设置异常

时间:2011-05-16 16:44:54

标签: oracle primary-key quartz-scheduler ora-00001

编辑:请查看此问题的结尾,了解导致错误的原因以及我的发现方式。

当我运行一个将数据批量插入到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;

3 个答案:

答案 0 :(得分:4)

您有一个独特的复合约束。 ORA-00001表示您有两行或多行在ServiceID,Date,Value1和/或Value2中具有重复值。你说两个数据库的输入是相同的。所以:

  • 您在想象您的计划正在向ORA-00001投掷
  • 你错了两次运行中的输入相同。

更可能的解释是第二个:您的一个或多个键列由外部源或默认值填充(例如,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
一个数据库中的

给出的数字低于另一个数据库。我在序列创建的地方看到了很多(例如,源代码控制之外),并且数据已从另一个数据库导入到表中。