2014-09-11 4 views
1

В соответствии с Hibernate documentation небезопасно использовать сеанс после того, как исключение вызывается Hibernate.Использование текущего Sesssion после исключения выбрано

Если сеанс вызывает исключение, включая любое SQLException, немедленно откат транзакции базы данных, вызовите Session.close() и отмените экземпляр сеанса. Некоторые методы сеанса не оставляют сеанс в согласованном состоянии. Никакое исключение, вызванное Hibernate, не может рассматриваться как подлежащее восстановлению. Убедитесь, что сессия будет закрыта вызовом функции close() в блоке finally.

В моем коде я делаю пакетную вставку. Я использую метод sessionFactory.getCurrentSession() для получения сеанса. Мой код такой.

try { 
    //some code here.... 
    .... 
    Table1Entity table1Entity = ....... 
    List<Table2Entity> table2Entities = ....... 
    Session currentSession = sessionFactory.getCurrentSession(); 
    for (int i = 0; i < table2Entities; i++) { 
     ............. 
     currentSession.save(table2Entities.get(i)); 
     if(i % batchSize == 0 || i + 1 == table2Entities) { 
     currentSession.flush(); 
     currentSession.clear(); 
    } 
    } 
} catch (Exception e) { 
    currentSession.getTransaction().rollback(); 
    //currentSession.close(); //According to documentation Session should be closed here 
    table1Entity.setError(true); 
    currentSession.save(table1Entity);//According to documentation Session should not be used here 
    ....... 
} 

Как указано в документации, сеанс не должен использоваться после исключения исключения. Моя проблема в том, что я использую currentSession, как я могу сохранить table1Entity в блоке catch? Должен ли я открыть новый сеанс с использованием метода openSession() или любым другим способом?


Edit: Чтобы быть более ясным, просто то, что я спрашиваю, могу ли я получить новый currentSession после существующего currentSession закрыт.

+0

делает это [это] (http://stackoverflow.com/questions/25738883/spring-transactional-annotation-when-using-try-catch-block/25739582#25739582) помогает вам где-то –

+0

@ ankur-singhal Спасибо за связь. Но это не решает мою проблему. В этом ответе он откатывает всю транзакцию. В моем случае мне нужно сохранить другой объект после исключения. – prageeth

ответ

0

Это единый блок try..catch. Как вы можете быть уверены, что исключение Hibernate, безусловно, не будет выбрано из любой обработки, выполняемой в таблице1Entity? Если исключение возникает при обработке table1Entity, то попытка сохранить его в текущем сеансе может создать большие проблемы.

Мой совет заключается в том, что вы разделяете обработку таблиц на отдельные блоки try..catch и вызываете сохранение отдельно в каждом блоке. После того, как выбрано исключение, попытка сохранить транзакцию в том же сеансе в блоке catch небезопасна и если предположить, что в таблице нет проблемы, то иногда может быть встречной интуитивностью.

+0

Спасибо за ответ. Выше код не из моего фактического класса. В любом случае, как я вижу, вы не поняли мой вопрос. Как указано в документации, если Hibernate выбрал исключение при сохранении table2Entities, сеанс больше не должен использоваться для будущих транзакций. Вызывается Session.close() и экземпляр сеанса должен быть отброшен. Каков мой вопрос после закрытия сеанса, как я могу получить новый сеанс для сохранения table1Entity? – prageeth

+0

Почему бы вам просто не сохранить table1Entity перед тем, как перейти к table2Entities? Потому что из того, что понимается, вы хотите, чтобы table1Entity сохранялась, даже если что-то не справляется с обработкой table2Entities. Это то, что я и предложил в своем ответе. – CodeNewbie

+0

Нет, актуально Я хочу вызвать метод setError() таблицы1Entity и сохранить его только в случае, если при сохранении table2Entities возникает исключение. См. Мой код выше. – prageeth

0

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

В таких ситуациях вы должны проявлять большую осторожность, чтобы ваша партия не выбрала исключение, несмотря ни на что. Таким образом, если исключение происходит, вы можете предположить, что база данных недоступна или в любом случае не будет принимать никаких попыток. Запишите ошибку в другое место (файлы журнала оказались полезными для этого).

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

Вместо этого напишите свою партию так, чтобы те, которые не будут работать как предпочтительные, вместо этого вернут правильный результат, который говорит «Я не работал», и обрабатываю его соответствующим образом.

Так что в вашем случае:

try { 
    //some code here.... 
    .... 
    Table1Entity table1Entity = ....... 
    List<Table2Entity> table2Entities = ....... 
    Session currentSession = sessionFactory.getCurrentSession(); 
    for (int i = 0; i < table2Entities; i++) { 
     ............. 

     // HERE you need to make sure that this call would succeed 
     currentSession.save(table2Entities.get(i)); 
     if(i % batchSize == 0 || i + 1 == table2Entities) { 
     currentSession.flush(); 
     currentSession.clear(); 
    } 
    } 
    currentSession.save(table1Entity); 
    currentSession.getTransaction().commit(); 
} catch (Exception e) { 
    currentSession.getTransaction().rollback(); 
    log.warn("Could not do what I wanted", e); 
} 

Это либо работает целиком или не на всех, и дает правильный трассировки стека с сообщениями и все в том месте, где он принадлежит (регистрационного журнала).

Возможно, вы хотите проверить, будет ли вызов успешным, прежде чем делать это.

Если вы считаете, что должны делать то, что хотите, прочитайте комментарии CodeNewbie под его ответом: Создайте таблицу1Entry с setGoingToWriteBatch(true), а затем в конце партии setBatchWritten(true). Таким образом, вы можете найти те, где первая истинна, а вторая - нет.

+0

Я ценю ваше предложение. В основном моя партия не работает, если в базе данных обнаружено дублирующееся значение идентификатора. В таком случае я хочу отбросить все вставки таблицы2Entities и заполнить столбец «error» таблицы1 (table1Entity) «true». Можете ли вы предложить мне, как я могу установить это свойство table1Entity в true после сбоя пакета и сохранить его в БД. Благодарю. – prageeth

+0

На самом деле я не верю, что предложение CodeNewbies о вставке и удалении записи Table1 является хорошим. Например, когда я использую последовательности, он вызывает nextVal(), затем присваивает идентификатор, а затем исключает ли исключение этот идентификатор. Таким образом, идентификатор никогда нельзя использовать. С другой стороны, я использую большую партию, которая занимает некоторое время, чтобы встать. Поэтому, пока моя партия все еще вставлена, если кто-либо запросил таблицу 1, он видит, что в столбце «Ошибка» установлено значение «истина». Это вводит в заблуждение. Вместо использования этого решения я предпочитаю избегать использования getCurrentSession() и вместо этого использовать openSession(). – prageeth

+0

@prageeth, как это сделать очень сильно зависит от того, как вы это делаете сейчас. Обычно я использую SQL для такого рода вещей, поскольку он дает вам более прямой контроль (и быстрее) по сравнению с тем, что происходит, вместо того, чтобы использовать Object-ориентированную обертку вокруг него. Но если вы хотите придерживаться подхода OO, просто сделайте выбор перед вставкой. –

Смежные вопросы