JPA Best Practice для обновления сущности с помощью коллекций - PullRequest
0 голосов
/ 06 января 2011

Я использую JPA в контейнере Glassfish.У меня есть следующая модель (не полная)

@Entity
public class Node {
    @Id
    private String serial;
    @Version
    @Column(updatable=false)
    protected Integer version;
    private String name;
    @ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
    private Set<LUN> luns = new HashSet<LUN>();

@Entity
public class LUN {
    @Id
    private String wwid;
    @Version
    @Column(updatable=false)
    protected Integer version;
    private String vendor;
    private String model;
    private Long capacity;
    @ManyToMany(mappedBy = "luns")
    private Set<Node> nodes = new HashSet<Node>();

Эта информация будет обновляться ежедневно.Теперь мой вопрос заключается в том, каков наилучший способ сделать это.

Мой первый подход заключался в том, чтобы каждый день я генерировал объекты Node на клиенте (с LUN) и объединял их с базой данных (я хотелчтобы позволить JPA выполнить работу через сервис.

Теперь я провел несколько тестов без LUN.У меня есть следующий сервис в EJB без состояния:

public void updateNode(Node node) {
    if (!nodeInDB(node)) {
        LOGGER.log(Level.INFO, "persisting node {0} the first time", node.toString());
        em.persist(node);
    } else {
        LOGGER.log(Level.INFO, "merging node {0}", node.toString());
        node = em.merge(node);
    }
}

Тест:

@Test
public void addTest() throws Exception {
    Node node = new Node();
    node.setName("hostname");
    node.setSerial("serial");
    nodeManager.updateNode(node);
    nodeManager.updateNode(node);
    node.setName("newhostname");
    nodeManager.updateNode(node);
}

Это работает без поля @Version.С полем @Version я получаю исключение OptimisticLockException.

Это неправильный подход?Должен ли я всегда выполнять em.find (...), а затем изменять управляемый объект с помощью методов получения и установки?

Любая помощь приветствуется.

BR Rene

1 Ответ

1 голос
/ 07 января 2011

Аннотация @version используется для включения оптимистической блокировки.

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

Ваша программа обновляет таблицу несколько раз после прочтения столбца версии только один раз. Поэтому при втором вызове persist () или merge () номера версий не совпадают, и ваш запрос не выполняется. Это ожидаемое поведение при использовании оптимистической блокировки: вы пытались перезаписать строку, которая была изменена с момента ее первого чтения.

Чтобы ответить на ваш последний вопрос: вам нужно читать измененную информацию @version после каждой записи в вашу базу данных. Вы можете сделать это, вызвав em.refresh ().

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

...