Атомарные JPA запрашивают/сохраняют для многопоточного приложения

Я посреди того, чтобы менять мой код JPA для использования потоков. У меня есть отдельный менеджер по объекту и транзакция для каждого потока.

То, что я раньше имел (для единственной потоковой среды) было кодом как:

// get object from the entity manager
X x = getObjectX(jpaQuery);

if(x == null)
{
    x = new X();
    x.setVariable(foo);
    entityManager.persist(x);
}

С тем кодом в многопоточной среде я добираюсь, делают дубликаты ключа с тех пор, я принимаю, getObjectX пустой указатель возвратов для потока, затем тот поток выгружается, следующий поток называет getObjextX, также становясь пустым, и затем оба потока создадут и сохранят новое X().

За исключением добавления в синхронизации, там атомарный путь к get/save-if-doesn't-exist значение с JPA или если я заново продумал свой подход

Править:

Я использую последний Eclipselink и MySql 5.1

РЕДАКТИРОВАНИЕ 2:

Я добавил синхронизацию... КРУПНЫЙ хит производительности (до такой степени, что это не может использоваться). Попытка собрать все данные назад по основному потоку и затем сделать создания на том потоке.

8
задан TofuBeer 7 June 2010 в 20:21
поделиться

4 ответа

Короткий печальный ответ: нет, JPA API не может сделать это за вас. Весь API более или менее построен на оптимистическом принципе предположения, что все будет работать, и выдачи исключений в случае одновременной модификации.

Если это происходит часто, вероятно, есть какой-то другой компонент (что бы ни генерировало foo?), Которому было бы полезно сделать потокобезопасным, как, возможно, альтернативу синхронизации по запросу + create.

4
ответ дан 5 December 2019 в 18:57
поделиться

Некоторые хитрости, которые следует рассмотреть:

  • реализовать hashCode () и equals () на основе бизнес-ключа объектов (не сгенерированный идентификатор)
  • синхронизировать:

     (obj.getClass (). getName () + String.valueOf (hashCode ())). intern ()
    

Таким образом, вы получите блокировки только в соответствующих случаях.

3
ответ дан 5 December 2019 в 18:57
поделиться

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

2
ответ дан 5 December 2019 в 18:57
поделиться

Вы уверены, что вам нужно несколько entitymanagers? В аналогичной ситуации я просто использую один entitymanager и простые объекты блокировки для каждого метода:

private Object customerLock = new Object[0];

public Customer createCustomer(){
    Customer customer = new Customer();
    synchronized(customerLock){
        entityManager.persist(customer);
    }
    return customer;
}

Edit: OK, не могу ничего сказать о производительности, кроме того, что в моих приложениях это работает нормально, но для уникальности используйте что-то вроде этого:

public Customer getOrCreateCustomer(String firstName, String lastName){
    synchronized(customerLock){
        List<Customer> customers = 
            entityManager.createQuery(
                "select c from Customer c where c.firstName = :firstName"
                + " and c.lastName = :lastName"
            )
            .setParam("firstName", firstName)
            .setParam("lastName", lastName)
            .setMaxResults(1)
            .getResultList();
        if(customers.isEmpty()){
            Customer customer = new Customer(firstName, lastName);
            entityManager.persist(customer);
        }else{
            customer = customers.get(0);
        }
    }
    return customer;
}
0
ответ дан 5 December 2019 в 18:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: