Подтвердить что ты не робот

JPA/Hibernate - нежелательный частичный откат и обработка сеанса

Я использую класс без сохранения состояния EJB для обновления объекта сохранения, находящегося в базе данных. Метод в EJB вызывает класс реализации, где выполняется работа. Я считаю, что причиной этой проблемы является то, что объект с именем Foo имеет отношение oneToMany с сущностью Bar. Делаются, и сеанс обновляется с помощью Foo, который "каскадирует" до Bar. Когда происходит событие StaleObjectStateException, транзакция не полностью откатывается, что вызывает очевидные причины.

EJB

private Session getSession() throws BusinessException {

    if( this.sess == null ) {
            ServiceLocator locator = new ServiceLocator();
            SessionFactory sf = locator.getHibernateSessionFactory();
            this.sess = sf.openSession();
    }
    return this.sess;

}

private ProductionOrderImpl getImpl() throws BusinessException {

    if( this.impl == null ) {
        this.impl = new ProductionOrderImpl( getSession() );
    }
    return this.impl;

}

public void cutoffOrders(  ) throws Exception {

    Transaction tx = null;
    try {
        tx = getSession().beginTransaction();
        getImpl().cutOffFoos(fooTime);
        tx.commit();
    } catch (StaleObjectStateException e1){
        if (tx != null) tx.rollback();
        logger.error( "Failed to cutoff order : " + e1 );
        throw new Exception( LocaleMgr.getMessage());
    } 
      finally {
        // reset implementation object, close session,
        // and reset session object
        impl = null;
        sess.close();
        sess = null;
    }   
}

Реализация

public ProductionOrderImpl(Session sess) {
    this.sess = sess;
}

public void cutoffFoos(  Timestamp fooTime) throws Exception {
    ... Code that gets fooList ...
    if( fooList != null ) {
        for( Foo foo: fooList ) {
            for( Bar bar : foo.getBarList() ) {
                 ... Code that does things with existing Barlist ...
                 if( ... ) {
                     ... Code that makes new Bar object ...
                     foo.getBarList().add(bar2);
                 }
            }
            sess.update( foo );
        }
    }
}

Релевантный код Foo:

@OneToMany(cascade=CascadeType.ALL, mappedBy="foo")
@OrderBy("startTime DESC")
Set<Bar> barList;

Итак, в основном, когда транзакция пытается откат, части, которые были изменены, возвращаются, но новые записи Bar (bar2 в коде) остаются.

Любое руководство будет оценено по достоинству. Как я уже сказал, я считаю, что ошибка здесь связана с sess.update(foo); возможно, что-то делать с autocommit, но по умолчанию оно должно быть выключено.

Я считаю, что происходит то, что Session.Update(foo) в свою очередь создает две отдельные транзакции. В частности, обновляется Foo (SQL UPDATE), но сохраняется Bar (SQL INSERT). Так как контекст транзакции будет реально видеть SQL UPDATE, это все, что он меняет. Вам придется подробнее изучить это.

Я попытался изменить Session.FlushMode на COMMIT, но он по-прежнему не исправляет проблему. Однако он частично устраняет проблему. Он будет откатывать записи должным образом, за исключением конкретной записи, которая вызывает исключение StaleObjectStateException. Эта конкретная запись фактически удаляется прямо из базы данных...

4b9b3361

Ответ 1

Ну, мне удалось исправить мою проблему.. Я буду ждать, чтобы принять ее, если кто-то еще что-то поставит, что-то еще... щедрость достойная.

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

Я изменил свой EJB, чтобы иметь следующее:

public void cutoffOrders(  ) throws Exception {
  Transaction tx = null;
  try {
      tx = getSession().beginTransaction();
      getSession().setFlushMode(FlushMode.MANUAL);
      getImpl().cutOffFoos(fooTime);
      getSession().flush();
      tx.commit();
  } catch (StaleObjectStateException e1){
      if (tx != null) tx.rollback();
      logger.error( "Failed to cutoff order : " + e1 );
      throw new Exception( LocaleMgr.getMessage());
  } 
    finally {
      // reset implementation object, close session,
      // and reset session object
      impl = null;
      sess.close();
      sess = null;
  }   
}

Затем код реализации должен иметь следующее:

public void cutoffFoos(  Timestamp fooTime) throws Exception {
  ... Code that gets fooList ...
  if( fooList != null ) {
      for( Foo foo: fooList ) {
          for( Bar bar : foo.getBarList() ) {
               ... Code that does things with existing Barlist ...
               sess.flush();
               if( ... ) {
                   ... Code that makes new Bar object ...
                   foo.getBarList().add(bar2);
               }
          }
          sess.flush();
          sess.update( foo );
      }
  }
}

Ответ 2

Хорошо, вот мои два цента на этом, так как это также относится к JPA:

В Spring Data JPA вы можете просто использовать следующее:

1. @Транзакционная аннотация перед вызовом репозитория (обрабатывает откат)

2. Используйте метод saveAndFlush репозитория JPA, т.е.

@Service
public class ProductionOrderServiceImpl extends ProductionOrderService{

    @Autowired
    ProductionOrderRepository jpaRepository;

    @Transactional
    public void cutoffOrders( Timestamp fooTime ){

    ... Code that gets fooList ...
      if( fooList != null ) {
          for( Foo foo: fooList ) {
              for( Bar bar : foo.getBarList() ) {
                   ... Code that does things with existing Barlist ...
                   {Call another similar method with @transactional..}//saveAndFlush(BarList);
                   if( ... ) {
                       ... Code that makes new Bar object ...
                       foo.getBarList().add(bar2);
                   }
              }
              jpaRepository.saveAndFlush(foo);
          }
      }

    }

}

Что делает внутреннее сохранение и скрытие:

/*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
     */
@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

а затем em.flush();

И в случае, если после этого при запуске проблемы с @Audited версией, где удаленная запись не отображается, установите org.hibernate.envers.store_data_at_delete = true.

Надеюсь, что добавит перспективу в решение.