2016-08-27 1 views
1

У меня возникла проблема с откатом транзакций в Spring + Hibernate + SQLite.Слишком сложная транзакция Spring + Hibernate + SQLite

Вот основные классы моего примера

Смотрит на метод TestBean.testMethod()

public void testMethod() { 
    entityManager.persist(createOrder("1")); 
    entityManager.persist(createOrder("2")); 
    entityManager.persist(createOrder("3")); 
    entityManager.createQuery("from Order").getResultList(); 
    entityManager.persist(createOrder("4")); 
    throw new RuntimeException("Managed"); 
} 

Он создает и сохраняется ряд объектов Order. Между некоторыми творениями он считывает некоторые данные из базы данных. В конце он бросает RuntimeException для имитации сбоя во время вызова метода и принудительной транзакции Spring.

После выполнения я открываю базу данных и вижу, что она завершена откат только последней созданной сущности (createOrder («4»)) и все остальные (с именами «1», «2» и «3»), все еще присутствующие в базы данных. Но так как TestBean отмечен как @Transactional, тогда он должен удалить все четыре объекта, созданные в одном транзакционном методе testMethod().

Я пробовал все journal_mode в SQLIte, но rollback все еще не работал.

Любые идеи?

+0

Кажется, что Hibernate вызывает ** JdbcIsolationDelegate # delegateWork ** для каждого ** вызова em.persist() **. В то же время метод ** JdbcIsolationDelegate # delegateWork ** вызывает ** Connection # commit **, который блокирует все транзакционное взаимодействие в рамках JPA/Hibernate – Joltd

ответ

0

После долгого времени я нашел истинную причину проблемы - это тип стратегии @GeneratedValue аннотация всех объектов в приложении.

По умолчанию в стратегии аннотации указано значение AUTO, а это означает, что Hibernate будет использовать свою собственную таблицу в целевой базе данных (hibernate_sequence) для генерации значений идентификатора. Hibernate вызывает запрос в отдельной транзакции (выбор текущего значения из hibernate_sequence) - и в этом случае мы столкнулись с проблемой.

Если вы используете общий источник данных с несколькими соединениями, чем Hibernate пытается получить новое соединение, но SQLite может иметь только одно активное соединение - мы получаем ошибку «файл базы данных заблокирован». Если вы используете Spring SingleConnectionDataSource, который предотвращает соединение, то Hibernate будет повторно использовать существующее соединение, но создаст новую отдельную транзакцию. Новая транзакция будет завершена успешно, но старая транзакция, запрашивающая новый идентификатор, будет сбой.

Так решение очень простое, просто убедитесь, что все ваши лица аннотированный как этот

@GeneratedValue(strategy = GenerationType.IDENTITY) 

Таким образом, в этом случае Hibernate будет делегировать генерацию значений ID в основной базе данных.