[英]@Transactional on Controller Method does not roll back transactions, but on Service Methods does
Disclaimer: Yes, I know that @Transactional
should only be put on Service methods, ideally.免责声明:是的,我知道
@Transactional
应该只放在服务方法上,理想情况下。
I am supporting an old and poorly-designed application where all the business logic is cluttered in Controller methods, and those methods aren't transactional, so errors don't trigger rollback and the data is corrupted.我正在支持一个旧的和设计糟糕的应用程序,其中所有业务逻辑都在 Controller 方法中混乱,并且这些方法不是事务性的,因此错误不会触发回滚并且数据已损坏。
Our team needs a quick fix to enable error rollback for this legacy application, so it was decided to put @Transactional
on Controller methods (rather than refactor the application which would be a major effort).我们的团队需要一个快速修复来为这个遗留应用程序启用错误回滚,因此决定将
@Transactional
放在控制器方法上(而不是重构应用程序,这将是一项重大工作)。
However, when I tried the following on a Controller method, it didn't roll back my exception:但是,当我在 Controller 方法上尝试以下操作时,它没有回滚我的异常:
@Controller
public class ReviewDetailsController extends BaseController{
@RequestMapping(value={"/testException"}, method = RequestMethod.POST)
@Transactional(readOnly = false, rollbackFor = Exception.class)
public void testException(@RequestParam(required = true) String planId) throws Exception {
// Simulate some simple DB change: we'll change a Date to a dummy value, 03:33:33
Plans plan = planService.findById(new Integer(planId));
PlanWorkflow pw = processService.getLatestWorkflow(plan);
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
Date d = sdf.parse("12/06/2019 03:33:33");
pw.setCreatedDate(d);
// Save it
processService.savePlanWorkflow(pw);
// Exception test
throw new Exception("EXCEPTION TEST");
}
On the other hand, when put the exact same logic inside a sample Service class method, it did roll back successfully.另一方面,当将完全相同的逻辑放入示例服务类方法中时,它确实成功回滚。
Controller now calls Service method, the Service method has @Transactional Controller 现在调用 Service 方法,Service 方法有 @Transactional
@RequestMapping(value={"/testException"}, method = RequestMethod.POST)
@Transactional(readOnly = false, rollbackFor = Exception.class)
public void testException(@RequestParam(required = true) String planId) throws Exception {
// Call a Service method with the exact same logic from the Controller
processService.testException(planId);
}
Service服务
@Component
public class ProcessServiceImpl implements ProcessService {
@Override
@Transactional(readOnly = false, rollbackFor = Exception.class)
public void testException(@RequestParam(required = true) String planId) throws Exception {
// Simulate some simple DB change: we'll change a Date to a dummy value, 03:33:33
Plans plan = planService.findById(new Integer(planId));
PlanWorkflow pw = processService.getLatestWorkflow(plan);
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
Date d = sdf.parse("12/06/2019 03:33:33");
pw.setCreatedDate(d);
// Save it
processService.savePlanWorkflow(pw);
// Exception test
throw new Exception("EXCEPTION TEST");
}
In theory, everyone says I should be able to attach @Transactional
to Controller methods (even though it's not recommended -- but I still should be able to do it).理论上,每个人都说我应该能够将
@Transactional
附加到 Controller 方法(即使不推荐这样做——但我仍然应该能够做到)。 So why don't the Controller methods pick it up or honor it?那么为什么 Controller 方法不接受或尊重它呢?
applicationContext:应用上下文:
<context:component-scan base-package="mypackage.myapp">
</context:component-scan>
<jpa:repositories base-package="mypackage.myapp.dao"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="/META-INF/persistence.xml"/>
</bean>
<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cache-manager-ref="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:config-location="/WEB-INF/ehcache.xml"/>
implements
ControllerInterface
where the method signatures have been extracted (Source -> Refactor -> Extract Interface in Eclipse).implements
ControllerInterface
,其中方法签名已被提取(源 -> 重构 -> Eclipse 中的提取接口)。<aop:aspectj-autoproxy proxy-target-class="true" />
to enable "CGLIB Proxy".<aop:aspectj-autoproxy proxy-target-class="true" />
以启用“CGLIB 代理”。 Related threads where I got these ideas from: 1 , 2 .我从中获得这些想法的相关主题: 1 , 2 。
But none of this helped.但这些都没有帮助。 Data still isn't getting rolled back from the Controller.
数据仍未从控制器回滚。
The only solution we found to this is to implement a new series of @Component
classes (which we call "Delegates"
, but that's our term) which "mirror" the Controllers and contain the business logic that gets called from the Controller.我们找到的唯一解决方案是实现一系列新的
@Component
类(我们称之为"Delegates"
,但这是我们的术语),它们“镜像”控制器并包含从控制器调用的业务逻辑。 So eg所以例如
ReviewController
Each Method -> calls ReviewDelegate
Each Method, and that's where the business logic resides for each method. ReviewController
Each Method -> 调用ReviewDelegate
Each Method,这就是每个方法的业务逻辑所在的位置。
All these "Delegate" classes will observe Transactions (with rollback) because they're @Component
, which is impossible directly with a Controller.所有这些“委托”类都将观察事务(
@Component
),因为它们是@Component
,这对于控制器是不可能的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.