Как принудительно зафиксировать транзакцию Spring — hibernate безопасно

#java #database #spring #hibernate #transactions

#java #База данных #spring #переход в спящий режим #транзакции

Вопрос:

Мы используем spring и hibernate для веб-приложения: В приложении есть корзина, в которую пользователь может размещать товары. для хранения элементов для просмотра между разными входами в систему значения элементов в корзине покупок хранятся в таблицах. при отправке корзины товары будут сохранены в другой таблице, если нам нужно сгенерировать номер заказа.

Когда мы вставляем значения в таблицу, чтобы получить номер заказа, мы используем для получения максимального номера заказа и добавляем к нему 1. мы используем spring transaction manager и hibernate, в потоке кода мы получаем номер заказа и обновляем объект hibernate, чтобы сохранить значение order num. при отладке я заметил, что только при выдаче полной транзакции вставляется компонент объекта с номером заказа.

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

я заметил, что при отладке постоянный уровень не вставляется в базу данных даже после сброса сеанса session.flush()

это просто обновление памяти и вставка данных в БД только в конце транзакции spring. я попытался явно выполнить фиксацию транзакции

session.getTransaction().commit();

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

Любая помощь высоко ценится.

Добавлено: база данных Oracle, которую я использовал. Существует порядковый номер, который является уникальным для этой таблицы, а также номер заказа, сопоставленный с ним.

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

1. Какую базу данных вы используете? Разве он не поддерживает последовательности? Все ваши проблемы будут решены.

2. каков тип распространения вашей транзакции?

3. @MartinFrey используя базу данных oracle, проблема в том, что номер заказа должен начинаться со 100000 и заканчиваться на 999999 в соответствии с бизнес-правилом.. я согласен, если это порядковый номер, это не будет проблемой.

4. @RafikBeldi Для метода не установлен конкретный параметр распространения, мы используем управление транзакциями на уровне контекста приложения, как указано ниже HibernateTransactionManager»> <имя свойства=»SessionFactory» ref=»SessionFactory» /> </bean>

5. @RafikBeldi я использую тип Propagation_required по умолчанию.

Ответ №1:

выполните следующие действия:- , 1) Создайте метод службы с распространением REQUIRE_NEW в другом классе службы. 2) Переместите свой код (любой код, который вы хотите сбросить в БД) в этом новом методе. 3) Вызовите этот метод из существующего api (из-за прокси-сервера в spring мы должны вызвать этот новый сервисный метод из другого класса, иначе REQUIRE_NEW не будет работать, что гарантирует, что ваши данные сбрасываются).

Ответ №2:

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

После сохранения корзины покупок, чтобы увидеть обновленное количество заказов, вам нужно будет позвонить:

 session.refresh(cart);
  

Счетчик не должен управляться Hibernate ( insertable/updatable = false или @Transient ).

Ответ №3:

Ваша первая проблема заключается в последовательном доступе к генерации чисел, когда несколько потоков выполняют одну и ту же логику. Если бы вы могли использовать последовательности Oracle, об этом автоматически позаботились бы на уровне базы данных, поскольку последовательности гарантированно возвращают уникальные значения любое количество раз, когда они вызываются. Однако, поскольку теперь этим необходимо управлять на стороне сервера, вам необходимо будет использовать механизм синхронизации вокруг вашей логики генерации чисел (выберите max и увеличьте на единицу) через границу транзакции. Вы можете синхронизировать метод Service (ваш класс service будет одноэлементным и управляемым Spring) и объявить границу транзакции вокруг него. Однако, пожалуйста, обратите внимание, что это будет иметь последствия для производительности и, как правило, плохо сказывается на масштабируемости.

Другим вариантом может быть вариант этого — сохранить идентификатор, который будет выделен в отдельной таблице с одним столбцом «currentVal», и использовать пессимистическую блокировку для получения следующего номера. Таким образом, у основной таблицы не будет большой блокировки. Таким образом, блокировка будет удерживаться для кода генератора последовательности на время завершения транзакции создания основного объекта. Основная идея, лежащая в основе этих методов, заключается в сериализации доступа к генератору последовательности и удержании блокировки до фиксации транзакции основного объекта. Также отложите генератор чисел как можно позже.

Решение, предложенное @Vlad, является хорошим, если использование триггеров подходит для вашего дизайна.

Что касается вашего вопроса о поведении flush, SQL отправляется в базу данных при вызове flush, однако данные не фиксируются до тех пор, пока транзакция не будет зафиксирована декларативно или не будет вызвана ручная фиксация. Однако транзакция может видеть данные, которые она намеревается изменить, но не другие транзакции, в зависимости от характера изоляции транзакции.

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

1. Я использовал подход oracle pessimistic lock из-за меньшего изменения дизайна в существующем коде. Спасибо, это помогло.