Идентичные настройки БД Oracle: исключение только для одного из них
редактировать: посмотрите в конце этого вопроса, что вызвало ошибку и как я узнал.
Мне было очень странное исключение из 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;
3 ответа
У вас есть уникальное сложное ограничение. 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
(вам может понадобиться администратор БД для получения доступа к этому каталогу).
ТЛ; др
понимание требует информации: инструмент вашего кода.
Тот, у которого есть проблемы, это EE, а другой выглядит как база данных SE. Я ожидаю, что первое будет на более быстром оборудовании. Если это так, и ваш столбец даты заполнен с использованием SYSDATE, вполне может быть, что временного разрешения недостаточно; что вы получаете повторяющиеся значения даты. Если другие столбцы ваших данных также не являются уникальными, вы получаете ORA-00001.
Это длинный выстрел, но на первый взгляд я бы посмотрел в этом направлении.
Можете ли вы использовать таблицу исключений для идентификации данных? См. Отчет об исключениях из ограничений
Мое предположение было бы идентификатором сервиса. Какой бы сервисный идентификатор hibernate не использовал для "свежей" вставки, он уже был использован.
Возможно, таблица пуста в одной базе данных, но заполнена в другой
Я держу пари, что service_id генерируется в последовательности, а порядковый номер не синхронизирован с содержимым данных. Итак, у вас есть те же 1000 строк в таблице, но вы делаете
SELECT service_id_seq.nextval FROM DUAL
в одной базе данных дает меньшее число, чем в другой. Я часто вижу это, когда последовательность была создана (например, из-под контроля исходного кода) и данные были импортированы в таблицу из другой базы данных.