Столбец @Version обновляется, хотя persist никогда не вызывался - PullRequest
0 голосов
/ 10 апреля 2011

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

@Entity @Table public class A {
   @Id public String id;
   @Version public int version;
   @OneToMany(targetEntity = B.class,
              mappedBy = "a",
              cascade = CascadeType.ALL,
              fetch = FetchType.LAZY)
   public Collection<B> bs;

   ...
}

@Entity @Table public class B {
   @Id public String id;
   @Version public int version;
   @ManyToOne(targetEntity = A.class,
              cascade = CascadeType.ALL,
              fetch = FetchType.LAZY)
   public A a;

   ...
}

У меня есть промежуточная структура данных, которая содержит информацию о взаимосвязи между A и B, которую я повторяю перед сохранением своих данных в активной транзакции, проверяяесли A или B уже существует в базе данных или не использует EntityManager.find(Class<T>, Object):

// get an entity manager and begin transaction
for (C c : cs) {
    A a = em.find(A.class, c.getAId()); // an `A` doesn't exist in the database
    if (null == a) {                    // on first iteration so a new one will
        a = new A();                    // be created
        a.id = c.getAId();              // however in a 100 `C` there are 4-5
    }                                   // unique `A` exists

    B b = em.find(B.class, c.getBId()); // a `B` generally doesn't exist in
    if (null == b) {                    // the database, but the opposite
        b = new B();                    // could happen often
        b.id = c.getBId();
        b.a = a;
        a.bs.add(b);
    }       

    if (!em.contains(b))                // if a `B` was found in the database
      em.persist(b);                    // persist won't execute here
}
// commit transaction

Это прекрасно работает: новые данные сохраняются, уже существующие вещи не.

Однако! Столбец версии в моей базе данных всегда обновляется независимо от того, что происходит в цикле for-each.Я взял набор образцов данных, сохранил их в своих пустых таблицах (версии были на 0);Я взял этот же набор образцов данных и сохранил его снова (EntityManager.persist(Object) никогда не вызывался), а поля версии (в A) были обновлены, но я не могу себе представить, почему.

Мне известен тот факт, что, хотя EntityManager.persist(Object) никогда не вызывали, мой поставщик сохраняемости (Hibernate) генерировал операторы обновления для увеличения столбца версии.

Вопросы: почему?Как я могу предотвратить это?Что я должен изменить в моем методе сохранения моих данных, чтобы избежать этого?Спасибо.

Ответы [ 2 ]

3 голосов
/ 10 апреля 2011

Я думаю, что здесь происходит то, что у вас режим блокировки установлен на OPTIMISTIC_FORCE_INCREMENT. Если вы установите для него значение OPTIMISTIC, вы не увидите обновления. Javadoc объяснение поведения является точным, но довольно плотным; есть также сообщение в блоге , которое может помочь.

Я думаю, что смысл OPTIMISTIC_FORCE_INCREMENT в том, что он защищает согласованность в базе данных. Представьте, что у вас есть два As, a1 и a2, каждый с одним B. Два потока загружают их обоих. Поток 1 устанавливает a1, чтобы иметь два B, и a2, чтобы иметь один B (статус-кво). Поток 2 устанавливает a1, чтобы иметь один B (статус-кво) и a2, чтобы иметь два B. Поток 1 фиксируется перед потоком 2. С OPTIMISTIC_FORCE_INCREMENT поток 2 получит исключение, потому что объекты уже были изменены потоком 1. С OPTIMISTIC поток 2 будет успешным, потому что каждый поток будет только повышать версию на объекте, который он добавляет от B до Если поток 2 завершается успешно, база данных теперь представляет состояние, в котором ни один из потоков не зафиксирован - a1 и a2 оба имеют два B.

Теперь, возможно, это несоответствие - своего рода неявное слияние состояний - приемлемо и действительно именно то, что вы хотите. Но JPA не может предположить, что в общем, поэтому OPTIMISTIC_FORCE_INCREMENT доступен. Я не знаю, по умолчанию ли это (я не могу найти ни одной документации, в которой говорится об этом, но это было бы разумно), или вы устанавливаете ее явно где-то еще.

Что касается решений, то в случае, когда вообще ничего не изменяется, вы можете просто не фиксировать сеанс. Если вы имеете дело со случаями, когда некоторые объекты изменены, а некоторые нет, вы можете обработать каждый объект A в своем собственном сеансе. Или вы можете просто использовать OPTIMISTIC блокировку. Если я прав, то это!

0 голосов
/ 10 апреля 2011
 B b = em.find(B.class, c.getBId()); // a `B` generally doesn't exist in      
 if (null == a) {  

Полагаю, вы допустили небольшую ошибку при публикации этих двух строк кода: переменная в условии if должна быть b вместо a. Если это так, строка ниже вызывает обновление поля версии.

 a.bs.add(b); 
...