обработка ограничения базы данных в спящем режиме

В моем проекте используется спящий режим с менеджером транзакций Spring, а моя база данных - postgres (может быть, не имеет значения).

Я пытаюсь читать большие файлы xml и создавать из них объекты (объекты не большие, но количество) и вставлять их в базу данных.

Если по какой-то причине один из моих объектов нарушает ограничение базы данных, весь процесс останавливается. Как я могу пропустить те, которые нарушают ограничение базы данных? в качестве альтернативы записывать их идентификатор или что-то еще в файл журнала?

Обновление вопроса:

Я просматривал SO и обнаружил, что для пакетных вставок лучше всего использовать сеанс без сохранения состояния, но я все еще получаю ту же проблему и вставляю остановки:

May 26, 2012 4:45:47 PM org.hibernate.util.JDBCExceptionReporter logExceptions
SEVERE: ERROR: duplicate key value violates unique constraint "UN_FK"
  Detail: Key (fid)=(H1) already exists.

Вот соответствующие части моего кода для разбора xml и вставки в БД, для простоты предположим, что я вставляю фильмы:

//class field
@Autowired
private SessionFactory sessionFactory;

@Override
public void startDocument() throws SAXException {
    session = sessionFactory.getCurrentSession();
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException  {
if (qName.equalsIgnoreCase("FILM")) {
        movie.setCategory(category);
        movie.setAdded(new Date());
        session.insert(movie);
    }
}

У меня и это свойство установлено в app-ctx hibernate.jdbc.batch_size равным 100. Действительно ли необходимо делать выбор перед вставкой, чтобы избежать этого?

Обновление 2:

Если я использую StatelessSession вместо сеанса, я получаю около 20 вставок, после чего обработка останавливается на неопределенный срок без каких-либо исключений или чего-либо еще.

Я предполагаю, что число 20 связано с тем, что я объединяю соединения с tomcat и имею maxActive="20".

Обновление награды:

Мне бы очень хотелось, чтобы кто-то предложил решение (без защитного выбора, если это возможно). Использование statelessSession или просто сеанс.


person Gandalf StormCrow    schedule 25.05.2012    source источник


Ответы (6)


Большинство типов ограничений, таких как допустимость значений столбца или максимальная ширина, можно проверить с помощью Hibernate. Валидатор. Просто вручную выполните проверку объекта, прежде чем пытаться сохранить его.

Для некоторых вещей, особенно уникальных ограничений, вам нужно либо выполнить «защитный» выбор, чтобы увидеть, существует ли коллизия, либо сохранить в памяти набор уже вставленных значений.

person Affe    schedule 25.05.2012

Я думаю, что невозможно проверить что-то настолько полностью, чтобы гарантировать успешную вставку. В некоторых случаях, независимо от того, что вы делаете, кто-то другой может вставить что-то в БД между проверкой и вставкой, что приведет к нарушению ограничения.

В большинстве случаев я бы просто рекомендовал обрабатывать исключение, как и любое другое.

person Izzy    schedule 04.06.2012

Чтобы вставить большое количество объектов из файла xml, вам следует рассмотреть возможность использования весеннего пакета. Параметр skip-limit позволяет вам указать, сколько ошибочных строк может быть в вашем xml, прежде чем остановить пакетный процесс. Проверьте также skip-policy и skippable-exception; см. Настройка шага в документации Spring.

Если вы не хотите использовать весенний пакет, просто используйте простой try catch, который позволит вашему процессу продолжаться до конца.

person Sebastien Lorber    schedule 25.05.2012
comment
Простой оператор try catch не подойдет. Как только Hibernate генерирует исключение, состояние сеанса становится непоследовательным, транзакцию необходимо откатить, а сеанс закрыть. Более того, исключение будет генерироваться только во время сброса, спустя много времени после сохранения ошибочной записи. - person JB Nizet; 26.05.2012
comment
Вот почему в таких случаях рекомендуется использовать сеанс гибернации без сохранения состояния. Это предотвратит несогласованное состояние, а также уменьшит потребление памяти (или вам не придется выселять уже обработанные объекты) - person Sebastien Lorber; 26.05.2012

Как вы думаете, почему все это должно быть одной большой сделкой? Все, что вы описываете, на самом деле подразумевает для меня, что у вас действительно есть многочисленные транзакции здесь. Для объектов, которые «ошибаются», просто исключите этот объект и откатите транзакцию. Это становится немного сложнее, если элемент «ФИЛЬМ» определяет граф объектов, но идея та же.

person Steve Ebersole    schedule 01.06.2012

Я думаю, что Affe имеет правильный подход с защитным выбором перед вставкой.

Вы можете рассмотреть возможность использования точки сохранения перед каждой вставкой и отката к точке сохранения в случае возникновения исключения. Создание точки сохранения приведет к снижению производительности. Точка сохранения должна быть освобождена, когда вы закончите.

См. AbstractTransactionStatus.setSavepoint() или если у вас есть доступ к пулу соединений JDBC setSavepoint rollback(Savepoint) и releaseSavepoint(Savepoint)

person pd40    schedule 01.06.2012

Хотя это старый вопрос, недавно я столкнулся с подобной ситуацией, и вот что я сделал.

Я тоже использовал сеансы без сохранения состояния, но вот что я сделал по-другому. Я выпустил session.insert, который либо преуспевает, либо терпит неудачу из-за нарушения ограничения, при нарушении ограничения выдается ConstraintViolationException, перехватывая его, вы получаете объект, который не удалось вставить, делайте все, что вы когда-либо хотели сделать с неудавшимся объектом. Наряду с транзакциями также используются точки сохранения для каждой вставки (точки сохранения дешевле и не вызывают такого большого влияния на производительность), поэтому, когда транзакция терпит неудачу из-за какой-либо проблемы, фиксация выполняется до последней точки сохранения (в предложении catch).

мой код выглядел примерно так:

try {
  session.connection().setSavePoint("lastSaved");
  session.insert(obj);
}
catch(ConstraintViolationException) {
  log.error(obj.getUserId());
  ....
}
tx.commit();
....
catch(TransactionException e) {
  tx.rollback();
}
person Aryan    schedule 08.08.2013