Как заставить совершить транзакцию Spring-hibernate безопасно


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

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

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

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

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

session.getTransaction().commit();

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

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

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

3   2   2014-06-26 20:43:46

3 ответа:

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

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

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

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

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

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

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

session.refresh(cart);

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