JPA атомарный запрос / сохранение для многопоточного приложения - PullRequest
8 голосов
/ 07 июня 2010

Я нахожусь в процессе изменения своего кода 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 ().

Если не считать добавления в синхронизацию, есть ли атомарный способ получить / сохранить-если-не-значение с помощью JPA или я должен переосмыслить свой подход

EDIT:

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

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

Я добавил синхронизацию ... МАССИВНЫЙ удар по производительности (до такой степени, что его нельзя использовать). Собираем все данные обратно в основной поток и затем создаем их в этом потоке.

Ответы [ 4 ]

4 голосов
/ 08 июня 2010

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

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

3 голосов
/ 08 июня 2010

Некоторые "взломать", чтобы рассмотреть:

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

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

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

2 голосов
/ 07 июня 2010

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

0 голосов
/ 08 июня 2010

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

private Object customerLock = new Object[0];

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

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

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...