3

編集:エラーの原因と私がどのように見つけたかについては、この質問の最後を見てください。

Oracleデータベースにデータをバッチ挿入するアプリを実行すると、Hibernateから非常に奇妙な例外がスローされます。エラーは、OracleデータベースORA-00001から発生します。

「重複(一意)キーを持つレコードを挿入しようとしたことを意味します。このエラーは、既存のレコードが重複(一意)キーを生成するように更新された場合にも生成されます。」

別のマシンで同じテーブル(まったく同じ定義)を作成したため、アプリで同じテーブルを使用しても同じエラーが発生しないため、エラーは奇妙です。また、すべてのデータがデータベースに挿入されるため、実際に拒否されるものはありません。

2つの設定の間で何かが異なる必要がありますが、私が見ることができる唯一の違いは、発行時に取得するバナー出力です。

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)を持つ4列のテーブルです。

何が間違っている可能性があるかについてのアイデアはありますか?私は何度かクリーンを開始し、両方のテーブルを削除して同じ理由で開始しましたが、それでもデータベースからエラーが発生します。

出力のいくつか:

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>5秒ごとにバッチジョブを実行していました。他のOracleサーバーでは1分に1回で十分であると考え、クォーツスケジューラを* * / 1 * * *?で設定しました。これは間違っていることが判明し、毎分実行する代わりに、毎秒実行しました!

各ジョブには約1.5〜2秒かかり、2つ以上のジョブが同時に実行されていたため、サーバーで同時に挿入が発生していました。したがって、529個の要素を挿入する代わりに、1000から2000個の挿入を取得していました。crontrigger式を他の式と同じに変更し、5秒ごとに実行すると、問題が修正されました。

何が悪かったのかを見つけるために、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;
4

3 に答える 3

4

一意の複合制約があります。ORA-00001 は、ServiceID、Date、Value1、Value2 の値が重複している行が 2 つ以上あることを意味します。入力は両方のデータベースで同じであると言います。したがって、次のいずれかです。

  • プログラムがORA-00001を投げていると想像しています
  • 両方の実行で入力が同じであると誤解しています。

より可能性が高いのは 2 番目の説明です。キー列の 1 つ以上が外部ソースまたはデフォルト値によって入力されています (例: ServiceId のコード テーブルまたは日付列の SYSDATE)。失敗したデータベースでは、この自動作成は一意の値を提供できません。使用しているメカニズムに応じて、これがそうなる理由はいくつもあります。一意の複合キーでは NULL エントリがカウントされることに注意してください。つまり、任意の数のレコード (NULL,NULL.NULL,NULL) を持つことができますが、(42,NULL,NULL,NULL) のレコードは 1 つだけです。

実際の問題が何であるかを推測することは、私たちには困難であり、あなたにとってもほぼ同じくらい困難です (ただし、あなたにはコードの作成者であるという利点があり、洞察が得られるはずです)。必要なのは、いくつかのトレース ステートメントです。私の好ましい解決策は、一括 DML 例外処理を使用することですが、私は 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

洞察には情報が必要です: コードを計測します。

于 2011-05-17T11:50:29.310 に答える
1

問題があるのは EE で、もう 1 つは SE データベースのように見えます。最初はより高速なハードウェア上にあると思います。その場合、日付列が SYSDATE を使用して入力されている場合、時間分解能が十分でない可能性があります。重複した日付値を取得すること。データの他の列も一意でない場合、ORA-00001 が返されます。

遠回りですが、一見するとこの方向に目を向けます。

例外テーブルを使用してデータを識別できますか? レポート制約の例外を参照してください

于 2011-05-16T17:32:15.457 に答える
1

私の推測では、サービスIDになります。「新鮮な」挿入に hibernate が使用している service_id は、すでに使用されています。

あるデータベースではテーブルが空であるが、別のデータベースではデータが取り込まれている可能性があります

service_id が生成されたシーケンスであり、シーケンス番号がデータの内容と同期していないことに賭けています。したがって、テーブルには同じ1000行がありますが、

SELECT service_id_seq.nextval FROM DUAL

あるデータベースでは、他のデータベースよりも低い数値が得られます。これは、シーケンスが作成され (たとえば、ソース管理から)、データが別のデータベースからテーブルにインポートされた場合によく見られます。

于 2011-05-17T03:43:39.970 に答える