Как предотвратить неповторяющиеся результаты запроса, используя постоянный API в Java SE? - PullRequest
3 голосов
/ 15 июля 2010

Я использую Java SE и изучаю использование постоянного API (toplink-essentials) для управления объектами в БД Derby.Примечание: это (дистанционное обучение) работа в университете, но это не «домашняя работа», эта проблема возникает в материалах курса.

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

например, из одного потока выполняется эта операция:

static void updatePrices(EntityManager manager, double percentage) {
    EntityTransaction transaction = manager.getTransaction();

    transaction.begin();
    Query query = manager.createQuery("SELECT i FROM Instrument i where i.sold = 'no'");
    List<Instrument> results = (List<Instrument>) query.getResultList();

    // force thread interruption here (testing non-repeatable read)
    try { Thread.sleep(2000); } catch (Exception e) { }

    for (Instrument i : results) {
        i.updatePrice(percentage);
    }
    transaction.commit();
    System.out.println("Price update commited");
}

И если он прерывается из другого потока с помощью этого метода:

private static void sellInstrument(EntityManager manager, int id)
{
    EntityTransaction transaction = manager.getTransaction();
    transaction.begin();
    Instrument instrument = manager.find(Instrument.class, id);
    System.out.println("Selling: " + instrument.toFullString());
    instrument.setSold(true);
    transaction.commit();
    System.out.println("Instrument sale commited");
}

Что может произойти, если поток внутриupdatePrices() возобновляет свой результат запроса. Набор недействителен, и цена проданного предмета обновляется до цены, отличной от той, по которой он был продан.(Магазин хочет вести учет предметов, которые были проданы в БД).Поскольку происходят параллельные транзакции, я использую разные EntityManager для каждого потока (от одной и той же фабрики).

Возможно ли (с помощью блокировки или какого-либо распространения контекста) предотвратить результаты запросастановится «недействительным» во время (прерванной) транзакции?У меня есть идея, что именно такой сценарий предназначен для Java EE, но я хочу знать, возможно ли его реализовать в Java SE.


Редактировать:

Взять Vineet иСовет Паскаля: использование аннотации @Version в классе сущности (с дополнительным столбцом БД) приводит к сбою большой транзакции (updatePrices()) с OptimisticLockException.Это очень дорого, если это происходит в конце большого набора результатов запроса.Есть ли способ заставить мой запрос (внутри updatePrices()) заблокировать соответствующие строки, заставляя поток внутри sellInstrument() либо блокировать, либо прерывать, генерировать исключение (а затем прерывать)?Это было бы намного дешевле.(Из того, что я понимаю, у меня нет пессимистической блокировки в Toplink Essentials).

Ответы [ 2 ]

3 голосов
/ 15 июля 2010

Резьба безопасности

У меня есть сомнения относительно того, как вы управляете своим EntityManager. В то время как EntityManagerFactory является потокобезопасным (и должен быть создан один раз при запуске приложения), EntityManager - нет, и вы обычно должны использовать один EntityManager на поток (или синхронизировать доступ к нему, но я бы использовал один для каждого нить).

параллелизм

JPA 1.0 поддерживает (только) оптимистическую блокировку (если вы используете атрибут Version) и два режима блокировки, позволяющие избежать грязного чтения и неповторяемого чтения через EntityManager.lock() API. Я рекомендую прочитать Блокировка чтения и записи и / или весь раздел 3.4 Оптимистическая блокировка и параллелизм спецификации JPA 1.0 для получения полной информации.

PS: обратите внимание, что Пессимистическая блокировка не поддерживается в JPA 1.0 или только через специальные расширения провайдера (он был добавлен в JPA 2.0, а также другие опции блокировки). На всякий случай Toplink поддерживает это с помощью подсказки eclipselink.pessimistic-lock.


Как написано в вики JPA, TopLink Essentials должен поддерживать пессимистическую блокировку в JPA 1.0 с помощью подсказки запроса:

// eclipselink.pessimistic-lock
Query Query = em.createQuery("select f from Foo f where f.bar=:bar");
query.setParameter("bar", "foobar");
query.setHint("eclipselink.pessimistic-lock", "Lock");
query.getResultList();

Я не использую TopLink, поэтому не могу подтвердить, что этот совет поддерживается во всех версиях. Если это не так, вам придется использовать собственный запрос SQL, если вы хотите сгенерировать «FOR UPDATE».

2 голосов
/ 15 июля 2010

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

Исходя из вашего описания проблемы, вы хотите заблокировать запись базы данных, как только она будет «выбрана» из базы данных.Этого можно добиться с помощью пессимистичной блокировки, которая более или менее эквивалентна инструкции SELECT ... FROM tbl FOR UPDATE.

...