2014-06-30 1 views
2

Предположим, что у меня есть entity bean AccountBean с полем версии (javax.persistence.Version аннотация). Во время транзакции мое приложение изменяет этот объект и выполняет операции с базой данных на других объектах (вставляет и обновляет строки). Некоторые из этих объектов beans имеют поле @Version, но не все из них.Сделка не полностью откатывается после всплытия сервера OptimisticLockException

Когда один и тот же объект AccountBean изменяется одновременно на 2 потока, генерируется исключение OptimistickLockException и (по крайней мере, согласно журналу сервера) транзакция откатывается назад. Тем не менее, только изменения, внесенные в конфликтующий объект AccountBean, фактически откатываются - все остальное привязано к базе данных.

** EDIT: ** Я добавил простой исходный код, чтобы осветить проблему; Приложение является веб-службой REST; В двух тестовых потоках одновременно выполняется «обновление» с тем же идентификатором учетной записи. Опять же, OLE выбрасывается, и, тем не менее, предположительно откатанная транзакция фиксирует базу данных нового объекта AccountHistory:/ Так как транзакции управляются контейнером, транзакция запускается при вызове обновления метода и выполняется при его возврате; Это также когда бросается OLE.

//UpdateAccount.java 
@Stateless 
@Path("account") 
public class UpdateAccount { 

    @PersistenceContext 
    EntityManager em; 

    @Path("update") 
    @POST 
    @Produces(MediaType.APPLICATION_JSON) 
    @Consumes(MediaType.APPLICATION_JSON) 
    public String update(Long accountId) { 
     Account account = em.find(Account.class, accountId); 
     if(null == account) { 
      return "account not found"; 
     } else { 
      return executeUpdate(account); 
     } 
    } 

    String executeUpdate(Account account) { 
     Integer newValue = account.getValue() + 1; 

     em.persist(getAccountHistory(account, newValue)); 
     account.setValue(newValue); 

     return "ok"; 
    } 

    AccountHistory getAccountHistory(Account account, Integer newValue) { 
     AccountHistory history = new AccountHistory(); 
     history.setId(new Date().getTime()); 
     history.setAccount(account); 
     history.setValueBefore(account.getValue()); 
     history.setValueAfter(newValue); 

     return history; 
    } 
} 

//Account.java 
@Entity 
public class Account { 

    @Id 
    private Long id; 

    @Column 
    private Integer value; 

    @Version 
    private Long version; 

    (...)//getters, setters etc 
} 

//AccountHistory.java 
@Entity 
public class AccountHistory { 

    @Id 
    private Long id; 

    @Column 
    private Integer valueBefore; 

    @Column 
    private Integer valueAfter; 

    @ManyToOne 
    @JoinColumn(name = "idaccount") 
    private Account account; 

    (...)//getters, setters etc 
} 
  1. Am Я неправильно ожидать, что все изменения откатываются?
  2. Могу ли я вручную принудительно выполнить полный откат? Я попытался вручную управлять транзакцией, ловил OLE и откатывал вызов (как описано в Adam Bien's blog). Однако, когда я поймаю исключение, транзакция равна , уже отмеченной как откат.

Я развертываю приложение на jboss-eap-6.1/jboss-as-7.1.1Final с JRE 1.7 и использую Hibernate (значения по умолчанию для этих серверов). Мой файл persistence.xml так же прост, как и он. Я не задал никаких дополнительных свойств.

+1

Вы совершаете обе операции? Когда они запускаются в коде? Если бы вы могли добавить код, в котором запускаются транзакции, это поможет ответить. – andriosr

+0

Привет, спасибо за ваш ответ. Я добавил простой тестовый код, чтобы проиллюстрировать проблему. После одновременного вызова обновления дважды я заканчиваю двумя новыми строками в таблице AccountHistory ... – Nexus42

ответ

0

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

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

Итак, Hibernate не контролирует то, что получает рулон, или нет. Это основной менеджер транзакций, который делает трюк.

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