#spring #hibernate #spring-mvc
#весна #впасть в спящий режим #spring-mvc #спящий режим
Вопрос:
транзакция была инициирована с использованием следующих кодов в файле контекста приложения:
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*InOwnTransaction" propagation="REQUIRES_NEW"
rollback-for="com.dummy.common.exception.DummyException" />
<tx:method name="*" propagation="REQUIRED"
rollback-for="com.dummy.common.exception.DummyException" />
</tx:attributes>
</tx:advice>
В Service impl функция для обновления объекта
public Offering selectiveUpdateInOwnTransaction(Map<String, String> offeringData) throws DummyException
{
// search data from DB
AbstractJpaEntity priceObj = selectEntityByCriteria(Price.class, paramMap);
// now "priceObj" contain requied data;
..
priceObj.setPublishedStatus(true);
// some businesslogic which is throwing exception <-- Point 1 : This is throwing exception
priceObj.setPrice(23);
..
updateEntity(priceObj);
}
Две функции, вызывающие ту же вышеупомянутую функцию «selectiveUpdateInOwnTransaction»
public boolean updateOffering(Offering offering) // In same Service Impl
public boolean updateOfferingInOwnTransaction(....) throws DummyException // In different Service Impl
В обеих этих функциях вызов «selectiveUpdateInOwnTransaction» выглядит следующим образом:
try
{
selectiveUpdateInOwnTransaction(dataMap);
}
catch (DummyException e)
{
....
}
Проблема:
Пункт 1 вызывает исключение, и «priceObj» не должен обновляться в БД, но когда вызывается «selectiveUpdateInOwnTransaction» из «updateOffering», он сохраняется в БД.
Также только содержимое, которое сохраняется в БД, — это содержимое, которое обновляется в объекте перед выбросом исключения.
Вызов из «updateOfferingInOwnTransaction» не показывает такой ошибки.
Я не могу понять, почему «updateOffering» не работает в соответствии с ожиданиями.
В качестве быстрого решения я внес следующие изменения в «selectiveUpdateInOwnTransaction», и после этого он работал нормально (объект не сохранялся в БД при выбросе исключения, как и ожидалось):
Price priceObj1 = new Price();
BeanUtils.copyProperties(priceObj, priceObj1);
updateEntity(priceObj1);
Но и в этом я не понимаю, почему это работает?
Другие сведения о конфигурации
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<aop:config>
<aop:pointcut id="serviceOperation"
expression="execution(* com.dummy.offering.db.service..*Service.*(..)) || execution(* com.dummy.common.db.service..*Service.*(..))" />
<aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice" />
</aop:config>
Комментарии:
1. Можете ли вы опубликовать остальную часть конфигурации транзакции? Для каких классов применим этот совет?
2. Привет @AndreiStefan, я добавил другую (может быть) соответствующую конфигурацию в main post. контекст: component-scan содержит все pkg, которые содержат вышеупомянутые методы
3. Одна вещь: если вы полагаетесь на
name="*InOwnTransaction" propagation="REQUIRES_NEW"
то, что будете работать для вызова методаupdateOffering
, тогда этого не произойдет. Вы выполняете внутренний вызов (самостоятельный вызов) для проксируемого класса: внутренний вызовselectiveUpdateInOwnTransaction
fromupdateOffering
будет вызовом обычного (не проксируемого) метода. Предполагая, что ServiceImpl реализует интерфейс, который, возможно, вызываетсяService
,updateOffering
указан ли метод в интерфейсе?4. привет @AndreiStefan, updateOffering также записывается в интерфейсе. Эта функция вызывается из контроллера, который автоматически подключает этот интерфейс (сервис)
5. Я также попытался переименовать «updateOffering» в «updateOfferingInOwnTransaction», но ничего не изменил. Чего я не пробовал, так это добавления «throw DummyEcxeption» в объявление функции «updateOffering». (поскольку нет необходимости выбрасывать его из него)
Ответ №1:
Если вы полагаетесь на name="*InOwnTransaction" propagation="REQUIRES_NEW"
то, что будете работать для вызова метода updateOffering
, то этого не произойдет. Вы выполняете внутренний вызов (самостоятельный вызов) для проксируемого класса: внутренний вызов selectiveUpdateInOwnTransaction
from updateOffering
будет вызовом обычного (не проксируемого) метода.
Я настоятельно рекомендую внимательно прочитать этот раздел документации. Чтобы напрямую применить то, что у вас есть в вашем коде, к образцу в документации: SimplePojo
ваш ServiceImpl
, foo()
ваш updateOffering
и bar()
ваш selectiveUpdateInOwnTransaction
. Подумайте о прокси как о совершенно новом классе, который перехватывает вызовы ваших собственных методов и классов.
- Итак, по сути, когда вы вызываете
updateOffering
со своего контроллера, вы вызываетеupdateOffering
экземпляр другого класса (которого нетServiceImpl
). - Этот новый класс применяет транзакционное поведение (запуск новой транзакции, связывание транзакционных ресурсов с текущим потоком и т. Д.), А Затем вызывает real
updateOffering
из вашего собственногоServiceImpl
. updateOffering
затем вызываетselectiveUpdateInOwnTransaction
, но поскольку этот вызов похожthis.selectiveUpdateInOwnTransaction
на then , вызов будет на вашемServiceImpl
, а не на вновь созданном классе, который действует как прокси. Из-за этого Spring обрабатывает вашselectiveUpdateInOwnTransaction
как обычный, ничего особенного.
С другой стороны, если selectiveUpdateInOwnTransaction
вызывается из другого класса, этот другой класс вызовет этот метод на прокси-сервере, и именно поэтому он работает, если вы вызываете его из другого ServiceImpl
.
В этом разделе документации есть уродливое решение этого ограничения
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
но реальным приемлемым подходом было бы немного изменить дизайн ваших классов: перейти updateOffering
в другой класс.
Комментарии:
1. спасибо @Andrey. Хотя я не использую «AopContext». У меня появилась идея. Я повторно извлек компонент из контекста приложения и вызвал функцию selectiveUpdate, используя этот объект компонента. Он работает.
2. я хорошая ссылка для прокси-сервера forum.spring.io/forum/spring-projects/aop /. … Просто для информации для новичков, таких как я