edit: посмотрите в конце этого вопроса, что вызвало ошибку и как я узнал.
У меня очень странное исключение из Hibernate, когда я запускаю приложение, которое выполняет пакетную вставку данных в базу данных Oracle. Ошибка исходит из базы данных 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
Определения таблиц, ввод и приложение, которое я написал, одинаковы для обоих. Используемая таблица представляет собой таблицу из четырех столбцов с составным идентификатором (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 в hibernate.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;