Ошибка JPA: транзакция в данный момент активна

#java #spring #jpa

#java #весна #jpa

Вопрос:

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

 @Test
public void testSave()
 {
EntityManagerHelper emh = new EntityManagerHelper();
LevelDAO dao = new LevelDAO();
Level l = new Level();
l.setName("aname");
emh.beginTransaction();
dao.save(l);
emh.commit();
}
  

затем запуск этого отдельного теста ниже без проблем

 @Test
public void testUpdate()
 {
EntityManagerHelper emh = new EntityManagerHelper();
LevelDAO dao = new LevelDAO();
Level l = new Level();
l.setName("bname");
l.setLevelid(1);
emh.beginTransaction();
dao.update(l);
emh.commit();
}
  

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

Обновить

EntityManagerHelper получает доступ к контексту сохранения следующим образом

 emf = Persistence.createEntityManagerFactory("bw_beta");        
threadLocal = new ThreadLocal<EntityManager>();
  

что выглядит как проблема

Таким образом, хакерским обходным путем было использование определения локально ie.

     EntityManagerFactory factory = Persistence.createEntityManagerFactory("bw_beta");
    EntityManager entityManager = factory.createEntityManager();
    entityManager.getTransaction().begin();
    dao.save(l);
    entityManager.persist(l);
    entityManager.getTransaction().commit();
  

Уверен, что есть лучший способ — возможно, с помощью Spring?

Комментарии:

1. Что происходит внутри EntityManagerHelper ?

2. @DataNucleus: он не говорит, что хочет запускать их параллельно.

3. И какова трассировка стека исключения?

Ответ №1:

  • Уверен, что есть лучший способ — возможно, с помощью Spring?

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

С Spring ваши тесты будут выглядеть примерно так:

 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:META-INF/conf/spring/application-context.xml",
                        "classpath:META-INF/conf/spring/test-datasource-spring-config.xml" })
@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
public class LevelDaoTest {

    @Resource( name="levelDao" )
    LevelDao levelDao;

    @Test
    public void shouldSaveNewLevels() {

        Level l = new Level();
        l.setName("aname");
        levelDao.save(l);
        // assert
    }

    @Test
    public void shouldUpdateExistingLevels() {

        Level l = new Level(); // or I would assume, you'd read this level back from DB, or set a proper ID, so the DAO will know to update it.. But that is besides the point
        l.setName("bname");
        levelDao.update(l);
        // assert
    }
}
  

Взгляните на документацию Spring в разделе Тестирование => Управление транзакциями, чтобы получить более подробную информацию.

PS Из вашего примера:

 dao.save(l);
entityManager.persist(l);
  

Выглядит действительно странно, так как обычно вы инкапсулируете entityManager в DAO, поэтому все, что вам нужно сделать, это dao.save(l)

Ответ №2:

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

 if(!entityManager.getTransaction().isActive())
    entityManager.getTransaction().begin();
dao.save(l);
entityManager.persist(l);
entityManager.getTransaction().commit();
  

Я реализовал одноэлементный подход для ее обработки.