hibernate вставляет пакет с разделенным postgresql
-
01-07-2019 - |
Вопрос
есть ли решение для пакетной вставки через hibernate в секционированную таблицу postgresql?в настоящее время я получаю сообщение об ошибке, подобное этому...
ERROR org.hibernate.jdbc.AbstractBatcher - Exception executing batch:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)....
я нашел эту ссылку http://lists.jboss.org/pipermail/hibernate-dev/2007-October/002771.html но я нигде не могу найти в Интернете, решена ли эта проблема или как ее можно обойти
Решение
Возможно, вы захотите попробовать использовать пользовательский пакет, установив свойство hibernate.jdbc.factory_class.Убедившись, что hibernate не будет проверять количество обновлений пакетных операций, вы можете решить вашу проблему, вы можете добиться этого, заставив свой пользовательский пакетчик расширить класс BatchingBatcher, а затем переопределив метод doExecuteBatch (...), чтобы он выглядел как:
@Override
protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException {
if ( batchSize == 0 ) {
log.debug( "no batched statements to execute" );
}
else {
if ( log.isDebugEnabled() ) {
log.debug( "Executing batch size: " + batchSize );
}
try {
// checkRowCounts( ps.executeBatch(), ps );
ps.executeBatch();
}
catch (RuntimeException re) {
log.error( "Exception executing batch: ", re );
throw re;
}
finally {
batchSize = 0;
}
}
}
Обратите внимание, что новый метод не проверяет результаты выполнения подготовленных инструкций.Имейте в виду, что внесение этого изменения может повлиять на режим гибернации каким-то неожиданным образом (а может и нет).
Другие советы
Они говорят использовать два триггера в секционированной таблице или аннотацию @SQLInsert здесь: http://www.redhat.com/f/pdf/jbw/jmlodgenski_940_scaling_hibernate.pdf страницы 21-26 (там также упоминается @SQLInsert, указывающий строковый метод).
Вот пример с триггером after для удаления дополнительной строки в главном файле: https://gist.github.com/copiousfreetime/59067
Появляется, если вы можете использовать ПРАВИЛА вместо триггеров для вставки, тогда это может вернуть правильное число, но только с одним ПРАВИЛОМ без оператора WHERE.
другим вариантом может быть создание представления, которое "оборачивает" секционированную таблицу, затем вы возвращаете НОВУЮ строку, чтобы указать на успешное обновление строки, без случайного добавления дополнительной нежелательной строки в основную таблицу.
create view tablename_view as select * from tablename; -- create trivial wrapping view
CREATE OR REPLACE FUNCTION partitioned_insert_trigger() -- partitioned insert trigger
RETURNS TRIGGER AS $$
BEGIN
IF (NEW.partition_key>= 5500000000 AND
NEW.partition_key < 6000000000) THEN
INSERT INTO tablename_55_59 VALUES (NEW.*);
ELSIF (NEW.partition_key >= 5000000000 AND
NEW.partition_key < 5500000000) THEN
INSERT INTO tablename_50_54 VALUES (NEW.*);
ELSIF (NEW.partition_key >= 500000000 AND
NEW.partition_key < 1000000000) THEN
INSERT INTO tablename_5_9 VALUES (NEW.*);
ELSIF (NEW.partition_key >= 0 AND
NEW.partition_key < 500000000) THEN
INSERT INTO tablename_0_4 VALUES (NEW.*);
ELSE
RAISE EXCEPTION 'partition key is out of range. Fix the trigger function';
END IF;
RETURN NEW; -- RETURN NEW in this case, typically you'd return NULL from this trigger, but for views we return NEW
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER insert_view_trigger
INSTEAD OF INSERT ON tablename_view
FOR EACH ROW EXECUTE PROCEDURE partitioned_insert_trigger(); -- create "INSTEAD OF" trigger
ссылка: http://www.postgresql.org/docs/9.2/static/trigger-definition.html
Если вы пошли по маршруту оболочки представления, одним из вариантов является также определение тривиальных триггеров "вместо" для удаления и обновления, тогда вы можете просто использовать имя таблицы представления вместо вашей обычной таблицы во всех транзакциях.
Другой вариант, который использует представление, - создать правило вставки, чтобы все вставки в основной таблице переходили в представление [которое использует свой триггер], например (при условии, что у вас уже есть partitioned_insert_trigger
и tablename_view и insert_view_trigger созданы, как указано выше)
create RULE use_right_inserter_tablename AS
ON INSERT TO tablename
DO INSTEAD insert into tablename_view VALUES (NEW.*);
Затем он будет использовать вашу новую вставку-оболочку рабочего вида.
спасибо!это сделало свое дело, пока никаких проблем не возникло :) .... одно но...я должен был реализовать BatcherFactory
класс и поместите его в persistence.xml
файл,
вот такой:
property name="hibernate.jdbc.factory_class" value="path.to.my.batcher.factory.implementation"
с этой фабрики я вызвал свою реализацию batcher с помощью приведенного выше кода
ps ядро гибернации 3.2.6 GA
еще раз спасибо
Я столкнулся с той же проблемой при вставке документов через hibernate после долгих поисков обнаружил, что ожидается, что обновленные строки должны быть возвращены, поэтому вместо null измените его на new в процедуре запуска, которая решит проблему, как показано ниже
ВЕРНУТЬ НОВЫЙ
Я нашел другое решение той же проблемы на этой веб-странице:
Это предполагает то же самое решение, о котором сказал @rogerdpack, изменив Возвращает значение Null Для Вернуть НОВЫЙ, и добавление нового триггера , который удаляет дублированный кортеж в главном файле вместе с запросом:
DELETE FROM ONLY master_table;