Spring managed Transaction commits where it shouldn't

耗尽温柔 提交于 2019-12-11 11:56:18

问题


in "applicationContext-base.xml" I add below:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        >

     .....

    <!-- transaction support-->
    <!-- PlatformTransactionMnager -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- enable transaction annotation support -->
    <tx:annotation-driven transaction-manager="txManager" />

and I want to setup a transaction to a function in the "controller"

@Controller
@RequestMapping("/ywdata")
public class YwController  extends BaseController{
....
@Transactional(timeout=1)
private void submitNewSXSQ(Map map, HttpServletRequest request, HttpServletResponse response) throws Exception {

    ...//STEP1 :do some db insert and update STEP1
    if(true)
        throw new Exception("test transaction ");

     ...//STEP2: do another db insert and update

and I expected the db operation should never be commit since I throw a exception before return. but actually not.


回答1:


There are multiple issues with your code:

  • @Transactional on private methods don't work
  • @Transactional on @Controller annotated classes usually don't work
  • rollback is not performed for checked exceptions

The last issue can be easily understood. Let me explain the first two problems. AOP in Spring works like this:

  • before the application context is initialized, Spring searches for beans which require method interception
  • a special proxy bean is registered for each of these beans... the proxy is either dynamic interface implementation (JDK proxy) or a dynamic subclass (CGLIB proxy) of your target bean
  • the proxy replaces the definition of your bean... original definition is renamed and marked as not eligible for autowiring (but it is still present on application context)
  • the methods on proxy are very dumb - all they do is interception logic (i.e. calling some aspect before/after/around execution) and calling the original proxied target bean method

Why private methods are problem:

  • with JDK proxying (default):
    • @Transactional won't work if you have @Transactional on non-interface method (only interface methods are present on the proxy)
  • with CGLIB proxying:
    • @@Transactional won't work if you have @Transactional on private or final method (only non-private and non-final methods can be overridden in dynamic subclass)

And why controllers are problem:

  • Spring's RequestMappingHandlerMapping (bean responsible for mapping requests to your @Controllers) asks application context to get all beans with @Controller annotation
    • this might return your original class, not the proxy (I think there was a bug for this in Spring JIRA, so it might be already fixed)
    • in case of JDK proxying, you need to add the annotation to the interface (so that the proxy is annotated)... this means that you would need to define interfaces for your controllers

What to do:

  • I suggest you to move transaction handling to service level
  • If you want your transaction to be wrapped around the whole request, you might take your inspiration from OpenSessionInViewFilter.
  • Also I encourage you to put breakpoint in your code and check the stack trace and look for the AOP proxy.
  • If you still want to manually handle transactions in some random part of code, you can use TransactionTemplate helper class.



回答2:


From Spring documentation, it is the default behavior: Transactions are marked for rollback only for unchecked exceptions.

See section 10.5.3 of doc



来源:https://stackoverflow.com/questions/25012070/spring-managed-transaction-commits-where-it-shouldnt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!