несколько потоков hibernate предотвращают несколько save (), JTA необходимо? - PullRequest
0 голосов
/ 09 февраля 2012

Я использую сеанс гибернации для каждой модели запроса для своего веб-приложения.Моя транзакция jdbc начинается в начале каждого веб-запроса и фиксируется в конце.

// Идиома неуправляемой среды

Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();

    // do some work
    ...

    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e; // or display error message
}
finally {
    sess.close();
}

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

public synchronized myMethod(param1, param2) {
    MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
    if (entity == null) {
        entity = .../create entity
        MyEntityADAO.save(entity);
    }
}

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

Я пытался выполнить код сохранения в своем новом сеансе и транзакции:

public synchronized myMethod(param1, param2) {
    MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
    if (entity == null) {
        entity = .../create entity
        Session session = HibernateUtil.createSession();
        MyEntityADAO.save(entity);
        Transaction t = session.beginTransaction();

   }
}

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

Должен ли я заключать каждый вызов DAO в свою собственную транзакцию и использовать распространение транзакций с JTA?Есть ли способ избежать JTA?Можно ли зафиксировать транзакцию, связанную с основным сеансом, после вызова MyEntityADAO.save () и вызвать beginTransaction в основном сеансе сразу после завершения транзакции, а транзакция зафиксирована в конце запроса, как сейчас?

Ответы [ 2 ]

0 голосов
/ 15 февраля 2012

Решение, которое работало для меня и моих конкретных требований к приложениям, пытаясь избежать JTA и вложенных транзакций:

Использование ManagedSessionContext, потому что org.hibernate.context.ThreadLocalSessionContext закроется и создаст новый сеанс для каждой транзакции. Вы столкнетесь с проблемами с сущностями, с которыми связаны коллекции, если вы загрузите эти сущности в нескольких открытых сеансах (когда вы создадите несколько транзакций для одного запроса).

  • Я открываю сеанс гибернации и привязываю его к контексту в начале моего веб-запроса
  • Любой метод уровня обслуживания, который нуждается в проверке существования перед вставкой, помечается как синхронизированный, глобальная транзакция фиксируется с помощью оператора вставки и запускается новая транзакция
  • В конце запроса фиксируется транзакция, привязанная к сеансу

    публичный синхронизированный myMethod (param1, param2) {

     MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
     if (entity == null) {
          entity = .../create entity
    
          MyEntityADAO.save(entity);
          HibernateUtil.getCurrentSession().getTransaction().commit();
          HibernateUtil.getCurrentSession().getTransaction().begin();
    
    
     }
    

    }

Я знаю, что это некрасиво и не будет работать для всех в каждом сценарии, но после очень интенсивного поиска по управлению транзакциями, уровням изоляции, блокировкам, версиям - это единственное решение, которое я нашел, которое сработало для меня. Я не использую Spring, и я не использую контейнер Java EE, используя Tomcat 6.

0 голосов
/ 09 февраля 2012

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

Что вам нужно сделать, это наложить уникальное ограничениев базе данных на [param1 - param2].Это приведет к откату одной из двух транзакций при наличии условия гонки.

Если вы решите по-прежнему изолировать код чека / вставки в своей собственной транзакции (потому что это не проблема, если это удается, а внешняя транзакция завершается неудачей), я не вижу, как JTA будет проблемой.Предположим, что вы используете EJB или Spring, просто поместите этот метод в его собственный EJB / bean-компонент и отметьте метод как транзакционный с распространением REQUIRES_NEW.

Таким образом, код будет выглядеть так:

// some code
Long id = myBean.checkIfExistOrCreate(param1, param2); // this methos call starts a new transaction
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);

Если вы не можете синхронизировать checkIfExistOrCreate, попробуйте вызвать его, отловить любое исключение, которое он может вызвать, и повторить вызов:

Long id = null;
try {
    id = myBean.checkIfExistOrCreate(param1, param2);
}
catch (Exception e) { // a well-defined exception would be better
    // the transaction roled back: retry
    id = myBean.checkIfExistOrCreate(param1, param2);
}
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...