{"id":95,"date":"2011-11-20T22:50:58","date_gmt":"2011-11-20T22:50:58","guid":{"rendered":"http:\/\/doanduyhai.wordpress.com\/?p=95"},"modified":"2018-09-14T13:58:54","modified_gmt":"2018-09-14T13:58:54","slug":"spring-transactional-explained","status":"publish","type":"post","link":"https:\/\/www.doanduyhai.com\/blog\/?p=95","title":{"rendered":"Spring @Transactional explained"},"content":{"rendered":"<p>Spring is a widely used framework today, bringing many powerfull features and extensions to the Java core stack. However most of people tend to use these features without understanding their underlying mechanism.<\/p>\n<p>Since there is no &#8220;magic&#8221; in real life, we are going to dig into some Spring features related to Transaction and Database in this serie of articles.<\/p>\n<p>This first article is dealing with the famous<strong> @Transactional<\/strong> annotation, saving the developers the burden of managing low level transaction code.<br \/>\n<!--more--><br \/>\nThe second article is available here:<br \/>\n<a href=\"https:\/\/www.doanduyhai.com\/blog\/?p=222\" title=\"Spring @PersistenceContext\/@PersistenceUnit explained\" target=\"_blank\">Spring @PersistenceContext\/@PersistenceUnit explained<\/a><\/p>\n<blockquote><p>Note: the following code analysis was done with <strong>Spring 3.0.5 official release<\/strong> We only focus on the <strong>J2SE environment (no EJB)<\/strong> and on the <strong>JPA API<\/strong> for database management. However, the code analyzed is generic enough to apply, to some extent, to other cases (J2EE platform, specific vendor JPA API&#8230;)<\/p><\/blockquote>\n<h1>I Usage and use cases<\/h1>\n<pre class=\"brush: java; title: ; toolbar: false; notranslate\" title=\"\">\r\n@Transactional(value = &amp;quot;myTransactionManager&amp;quot;, propagation = Propagation.REQUIRED)\r\npublic void myMethod()\r\n{\r\n ...\r\n}\r\n<\/pre>\n<p>The <strong>value<\/strong> attribute of the<strong> @Transactional<\/strong> annotation is not mandatory. If not mentionned Spring will look by default for any bean declared in the context with the name &#8220;<strong>transactionManager<\/strong>&#8221; (defaultConvention).<\/p>\n<pre class=\"brush: xml; highlight: [1]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  &lt;bean id=&quot;transactionManager&quot; class=&quot;org.springframework.orm.jpa.JpaTransactionManager&quot;&gt;\r\n    &lt;property name=&quot;entityManagerFactory&quot; ref=&quot;entityManagerFactory&quot;\/&gt;\r\n  &lt;\/bean&gt;\r\n<\/pre>\n<h1>II Registration in Spring context<\/h1>\n<pre class=\"brush: xml; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  &lt;tx:annotation-driven\/&gt;\r\n<\/pre>\n<p>To make the <strong>@Transactional<\/strong> annotation work, you should declare the <em>&lt;tx:annotation-driven&gt;<\/em> tag (tx being the shortcut of the namespace for &#8220;<em>http:\/\/www.springframework.org\/schema\/tx<\/em>&#8220;) <\/p>\n<h1>III Code analysis<\/h1>\n<h2>A Bean registration<\/h2>\n<p>&nbsp;<br \/>\nIn this chapter we will see how the<em> &lt;tx:annotation-driven&gt;<\/em> tag declaration is handled in the Spring context<\/p>\n<h5>1) org.springframework.transaction.config.<strong>AnnotationDrivenBeanDefinitionParser<\/strong><\/h5>\n<pre class=\"brush: java; highlight: [15]; title: ; toolbar: false; notranslate\" title=\"\">\r\n\/**\r\n* Parses the '&amp;lt;code&amp;gt;&amp;lt;\/code&amp;gt;' tag. Will\r\n* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}\r\n* with the container as necessary.\r\n*\/\r\npublic BeanDefinition parse(Element element, ParserContext parserContext) {\r\n  String mode = element.getAttribute(&amp;quot;mode&amp;quot;);\r\n  if (&amp;quot;aspectj&amp;quot;.equals(mode)) {\r\n       \/\/ mode=&amp;quot;aspectj&amp;quot;\r\n      registerTransactionAspect(element, parserContext);\r\n \u00a0}\r\n  else {\r\n      \/\/ mode=&amp;quot;proxy&amp;quot;\r\n      \/\/ DEFAULT MODE\r\n      AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);\r\n \u00a0}\r\n  return null;\r\n}\r\n<\/pre>\n<p>For most users, we fall into the else block (<em>mode=&#8221;proxy&#8221;<\/em>) so we&#8217;re calling <strong>AopAutoProxyConfigurer<\/strong><em>.configureAutoProxyCreator()<\/em><\/p>\n<pre class=\"brush: java; highlight: [18,19,24,28,38,39,47]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n\r\nprivate static class AopAutoProxyConfigurer {\r\n\r\n  public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {\r\n    AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);\r\n\r\n     if (!parserContext.getRegistry().containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) {\r\n       Object eleSource = parserContext.extractSource(element);\r\n\r\n       \/\/ Create the TransactionAttributeSource definition.\r\n       RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);\r\n       sourceDef.setSource(eleSource);\r\n       sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\r\n\r\n       \/\/ The bean AnnotationTransactionAttributeSource is created and registed dynamically here\r\n       String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);\r\n\r\n       \/\/ Create the TransactionInterceptor definition.\r\n       \/\/ Point A\r\n       RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);\r\n       interceptorDef.setSource(eleSource);\r\n       interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\r\n\r\n       \/\/ Set the declared transaction manager in\u00a0  to the transactionInterceptor, when available\r\n       registerTransactionManager(element, interceptorDef);\r\n       interceptorDef.getPropertyValues().add(&amp;quot;transactionAttributeSource&amp;quot;, new RuntimeBeanReference(sourceName));\r\n\r\n       \/\/\u00a0 The bean TransactionInterceptor is created and registed dynamically here\r\n       String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);\r\n\r\n       \/\/ Create the TransactionAttributeSourceAdvisor definition.\r\n\r\n       \/\/ This bean is an AOP definition\r\n       RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);\r\n       advisorDef.setSource(eleSource);\r\n       advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\r\n\r\n       \/\/ Inject the bean AnnotationTransactionAttributeSource into the AOP definition\r\n       \/\/ Point B\r\n       advisorDef.getPropertyValues().add(&amp;quot;transactionAttributeSource&amp;quot;, new RuntimeBeanReference(sourceName));\r\n\r\n       \/\/ Definition of advice bean = TransactionInterceptor previously declared\r\n       advisorDef.getPropertyValues().add(&amp;quot;adviceBeanName&amp;quot;, interceptorName);\r\n       if (element.hasAttribute(&amp;quot;order&amp;quot;)) {\r\n          advisorDef.getPropertyValues().add(&amp;quot;order&amp;quot;, element.getAttribute(&amp;quot;order&amp;quot;));\r\n       }\r\n       \/\/\u00a0 The bean BeanFactoryTransactionAttributeSourceAdvisor is created and registed dynamically here\r\n       parserContext.getRegistry().registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);\r\n\r\n       CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);\r\n       compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));\r\n       compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));\r\n       compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME));\r\n       parserContext.registerComponent(compositeDef);\r\n    }\r\n  }\r\n\r\n  \/\/ Retrieve the transactionManager attribute defined on  when available\r\n  \/\/ Example\r\n  private static void registerTransactionManager(Element element, BeanDefinition def) {\r\n        def.getPropertyValues().add(&amp;quot;transactionManagerBeanName&amp;quot;,\r\n                TxNamespaceHandler.getTransactionManagerName(element));\r\n  }\r\n}\r\n<\/pre>\n<p>The transaction interceptor class is created at <strong>line 19<\/strong><\/p>\n<p>Then the declared transaction manager in <em>&lt;tx:annotation-driven&gt;<\/em> is searched in the Spring context and attached to this transaction interceptor, <strong>line 24<\/strong><\/p>\n<p>Finally the bean is registered in the Spring context at<strong> line 28<\/strong><\/p>\n<p>From\u00a0 <strong>line 32<\/strong> to <strong>line 47<\/strong>, Spring declares an\u00a0 <strong>TransactionAttributeSourceAdvisor<\/strong> bean and registers it into the context.<br \/>\n&nbsp;<\/p>\n<h2>B @Transactional parsing<\/h2>\n<p>&nbsp;<\/p>\n<h5>1) org.springframework.transaction.interceptor.<strong>BeanFactoryTransactionAttributeSourceAdvisor<\/strong> extends <strong>AbstractBeanFactoryPointcutAdvisor<\/strong><\/h5>\n<p>&nbsp;<br \/>\nIn this chapter, we&#8217;ll see how the <strong>@Transactional<\/strong> annotation is parsed during runtime by Spring to retrieve transaction-related properties<\/p>\n<pre class=\"brush: java; highlight: [1]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n   \/\/ Injected at Point B\r\n   private TransactionAttributeSource transactionAttributeSource;\r\n\r\n   \/\/ Define a pointcut here for AOP, injecting the transactionAttributeSource that was\r\n   \/\/ set in AopAutoProxyConfigurer\r\n   \/\/ see Point B\r\n   private final TransactionAttributeSourcePointcut pointcut =\r\n      new TransactionAttributeSourcePointcut() {\r\n         @Override\r\n         protected TransactionAttributeSource getTransactionAttributeSource() {\r\n           return transactionAttributeSource;\r\n         }\r\n      };\r\n<\/pre>\n<p>Here Spring just defines a poincut advisor. It is composed of a poincut handled by the class <strong>TransactionAttributeSourcePointcut<\/strong>. Here the <em>transactionAttributeSource<\/em> is passed to the anonymous class dynamically within the overriden <em>getTransactionAttributeSource()<\/em> method<\/p>\n<h5>2) abstract class org.springframework.transaction.interceptor.<strong>TransactionAttributeSourcePointcut<\/strong><\/h5>\n<pre class=\"brush: java; highlight: [12]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\nabstract class org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut\r\n\r\n  \/\/ Determine whether a call on a particular method matches the poincut\r\n  \/\/ If it matches then the advice bean will be called\r\n  \/\/ The advice bean that has been registered for this pointcut is the\r\n  \/\/ TransactionInterceptor class (see Point A)\r\n  public boolean matches(Method method, Class targetClass) {\r\n  TransactionAttributeSource tas = getTransactionAttributeSource();\r\n\r\n  \/\/ Call getTransactionAttribute of the injected transactionAttributeSoirce\r\n  \/\/ (see Point C)\r\n  return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);\r\n}\r\n<\/pre>\n<p>This abstract class only defines the default bahavior of the <em>maches()<\/em> method to check whether the join point matches this poincut<\/p>\n<h5>3) org.springframework.transaction.annotation.<strong>AnnotationTransactionAttributeSource<\/strong> extends <strong>AbstractFallbackTransactionAttributeSource<\/strong><\/h5>\n<pre class=\"brush: java; highlight: [1,2,3,17]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Should be false in a context of J2SE\r\n  private static final boolean ejb3Present = ClassUtils.isPresent(&amp;quot;javax.ejb.TransactionAttribute&amp;quot;,\r\n                                    AnnotationTransactionAttributeSource.class.getClassLoader());\r\n\r\n  \/\/ Default constructor\r\n  public AnnotationTransactionAttributeSource() {\r\n    this(true);\r\n  }\r\n\r\n  \/\/ publicMethodsOnly = true because this bean has been registered dynamically\r\n  \/\/ by AopAutoProxyConfigurer with no argument so the default constructor above applies\r\n  \/\/\r\n  \/\/ ejb3Present = false\r\n  public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {\r\n        this.publicMethodsOnly = publicMethodsOnly;\r\n        this.annotationParsers = new LinkedHashSet(2);\r\n        this.annotationParsers.add(new SpringTransactionAnnotationParser());\r\n        if (ejb3Present) {\r\n           this.annotationParsers.add(new Ejb3TransactionAnnotationParser());\r\n        }\r\n    }\r\n<\/pre>\n<p>The second constructor of <strong>AnnotationTransactionAttributeSource<\/strong> registers the <strong>SpringTransactionAnnotationParser<\/strong> as default parser for the <strong>@Transactional<\/strong> annotation<\/p>\n<h5>4) org.springframework.transaction.interceptor.<strong>AbstractFallbackTransactionAttributeSource<\/strong><\/h5>\n<p><strong>Point C &amp; Point D <\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,13,14,30,49,50]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point C\r\n  public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {\r\n        \/\/ First, see if we have a cached value.\r\n        Object cacheKey = getCacheKey(method, targetClass);\r\n        Object cached = this.attributeCache.get(cacheKey);\r\n        if (cached != null) {\r\n          ....\r\n          ....\r\n          \/\/ Not interesting code\r\n        }\r\n        else {\r\n          \/\/ We need to work it out.\r\n          \/\/ (see Point D below)\r\n          TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);\r\n          \/\/ Put it in the cache.\r\n          if (txAtt == null) {\r\n            this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);\r\n          }\r\n          else {\r\n            if (logger.isDebugEnabled()) {\r\n               logger.debug(&amp;quot;Adding transactional method '&amp;quot; + method.getName()\r\n               + &amp;quot;' with attribute: &amp;quot; + txAtt);\r\n            }\r\n            this.attributeCache.put(cacheKey, txAtt);\r\n          }\r\n          return txAtt;\r\n        }\r\n    }\r\n\r\n  \/\/ Point D\r\n  private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {\r\n       \/\/ Don't allow no-public methods as required.\r\n       \/\/ Here allowPublicMethodsOnly() will return true because we set the attribute\r\n       \/\/ publicMethodOnly = true in the constructor of AnnotationTransactionAttribute\r\n       if (allowPublicMethodsOnly() &amp;amp;&amp;amp; !Modifier.isPublic(method.getModifiers())) {\r\n          return null;\r\n       }\r\n\r\n       \/\/ Ignore CGLIB subclasses - introspect the actual user class.\r\n       Class userClass = ClassUtils.getUserClass(targetClass);\r\n\r\n       \/\/ The method may be on an interface, but we need attributes from the target class.\r\n       \/\/ If the target class is null, the method will be unchanged.\r\n       Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);\r\n\r\n       \/\/ If we are dealing with method with generic parameters, find the original method.\r\n       specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);\r\n\r\n       \/\/ Find the @Transactional attributes of the method in the target class.\r\n       \/\/ (see Point E)\r\n       TransactionAttribute txAtt = findTransactionAttribute(specificMethod);\r\n       if (txAtt != null) {\r\n          return txAtt;\r\n       }\r\n       ...\r\n       \/\/ Not interesting code\r\n   }\r\n<\/pre>\n<p>The <em>getTransactionAttribute()<\/em> of the abstract class ultimately delegates the work to the <em>computeTransactionAttribute()<\/em> method.<\/p>\n<p>First it determines the target class (in case the annotation being put on an interface method) then calls the method <em>findTransactionAttribute()<\/em><\/p>\n<h5>5) org.springframework.transaction.annotation.<strong>AnnotationTransactionAttributeSource<\/strong> extends <strong>AbstractFallbackTransactionAttributeSource<\/strong><\/h5>\n<p><strong>Point E<\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,4,9,10]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point E\r\n  protected TransactionAttribute findTransactionAttribute(Method method) {\r\n        \/\/ See below\r\n        return determineTransactionAttribute(method);\r\n  }\r\n\r\n  protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {\r\n        for (TransactionAnnotationParser annotationParser : this.annotationParsers) {\r\n            \/\/ (see Point F)\r\n            TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);\r\n            if (attr != null) {\r\n                return attr;\r\n            }\r\n        }\r\n        return null;\r\n  }\r\n<\/pre>\n<p>Again the real job is not done here but is delegated to the annotation parser class <strong>SpringTransactionAnnotationParser<\/strong> registered previously in the constructor of <strong>AnnotationTransactionAttributeSource<\/strong><\/p>\n<h5>6) org.springframework.transaction.annotation.<strong>SpringTransactionAnnotationParser<\/strong><\/h5>\n<p><strong>Point F<\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,24,25,26,27,36]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point F\r\n  public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {\r\n        Transactional ann = ae.getAnnotation(Transactional.class);\r\n        if (ann == null) {\r\n            for (Annotation metaAnn : ae.getAnnotations()) {\r\n                \/\/ @Transactional annotation\r\n                ann = metaAnn.annotationType().getAnnotation(Transactional.class);\r\n                if (ann != null) {\r\n                    break;\r\n                }\r\n            }\r\n        }\r\n        if (ann != null) {\r\n            \/\/See below\r\n            return parseTransactionAnnotation(ann);\r\n        }\r\n        else {\r\n            return null;\r\n        }\r\n    }\r\n\r\n    public TransactionAttribute parseTransactionAnnotation(Transactional ann) {\r\n        RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();\r\n        rbta.setPropagationBehavior(ann.propagation().value());\r\n        rbta.setIsolationLevel(ann.isolation().value());\r\n        rbta.setTimeout(ann.timeout());\r\n        rbta.setReadOnly(ann.readOnly());\r\n\r\n        \/* Set qualifier name in the case multiple transaction managers are used\r\n         * bean id=&amp;quot;myTransactionManager&amp;quot; class=&amp;quot;org.springframework.orm.jpa.JpaTransactionManager&amp;quot;\r\n         *    property name=&amp;quot;entityManagerFactory&amp;quot; ref=&amp;quot;myEntityManagerFactory&amp;quot; \/\r\n         * \/bean\r\n         *\r\n         * @Transactional(value = &amp;quot;myTransactionManager&amp;quot;)\r\n         *\/\r\n        rbta.setQualifier(ann.value());\r\n        ArrayList rollBackRules = new ArrayList();\r\n        ...\r\n        \/\/ Not interesting code\r\n        return rbta;\r\n    }\r\n\r\n<\/pre>\n<p>The parser will retrieve all attributes of the <strong>@Transactional<\/strong> annotation, among which:<\/p>\n<ul>\n<li>propagation behavior<\/li>\n<li>isolation level<\/li>\n<li>timeout value for the transaction<\/li>\n<li><strong>readOnly<\/strong> flag<\/li>\n<li>and the most important attribute of all: <strong>value<\/strong>, which corresponds to the bean name of the transactionManager declared in the Spring context and responsible for the current transaction.<\/li>\n<\/ul>\n<p>\nIf omitted, the <strong>value<\/strong> attribute defaults to <strong>&#8220;transactionManager&#8221;<\/strong>. When dealing with multiple databases or multiple datasources applications, more than one transactionManager are defined in the Spring context so the <strong>value<\/strong> is important to help  Spring choosing the right one.<br \/>\n&nbsp;<\/p>\n<h2>C Transactional interceptor invocation<\/h2>\n<p><\/p>\n<p>In this chapter we&#8217;ll look under the hood to see how Spring achieves transaction demarcations<\/p>\n<h5>\n1) org.springframework.transaction.interceptor.<strong>TransactionInterceptor<\/strong> extends <strong>TransactionAspectSupport<\/strong><br \/>\n<\/h5>\n<pre class=\"brush: java; highlight: [1,11,12,13,14,16,17,25,26,33,44,45]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ The real job is done here\r\n  public Object invoke(final MethodInvocation invocation) throws Throwable {\r\n\r\n        \/\/ Work out the target class: may be &amp;lt;code&amp;gt;null&amp;lt;\/code&amp;gt;.\r\n        \/\/ The TransactionAttributeSource should be passed the target class\r\n        \/\/ as well as the method, which may be from an interface.\r\n        Class&amp;lt;?&amp;gt; targetClass = (invocation.getThis() != null ?\r\n                              AopUtils.getTargetClass(invocation.getThis()) : null);\r\n\r\n        \/\/ If the transaction attribute is null, the method is non-transactional.\r\n        \/\/Similar to Point C\r\n        final TransactionAttribute txAttr =\r\n                getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(),\r\n                                                                          targetClass); \r\n\r\n         \/\/ (see Point G)\r\n        final PlatformTransactionManager tm = determineTransactionManager(txAttr); \r\n        final String joinpointIdentification = methodIdentification(invocation.getMethod(), targetClass);\r\n\r\n        \/\/ The txAttr is not null but the transactionManager is NOT an instance \r\n        \/\/ of CallbackPreferringPlatformTransactionManager so we still enter the if block\r\n        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {\r\n\r\n            \/\/ Standard transaction demarcation with getTransaction() and commit\/rollback calls.\r\n            \/\/ (see Point H) \r\n            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); \r\n\r\n            \r\n            Object retVal = null;\r\n            try {\r\n                \/\/ This is an around advice: Invoke the next interceptor in the chain.\r\n                \/\/ This will normally result in a target object being invoked.\r\n                retVal = invocation.proceed();\r\n            }\r\n            catch (Throwable ex) {\r\n                \/\/ target invocation exception\r\n                completeTransactionAfterThrowing(txInfo, ex);\r\n                throw ex;\r\n            }\r\n            finally {\r\n                cleanupTransactionInfo(txInfo);\r\n            }\r\n            \/\/ Commit after the method call returns\r\n            \/\/ (see Point O)\r\n            commitTransactionAfterReturning(txInfo); \r\n            return retVal;\r\n        }\r\n<\/pre>\n<ul>\n<li>First Spring retrieves the transaction attributes (<strong>line 12, 13 &amp; 14<\/strong>)<\/li>\n<li>Then it gets the transaction manager from the Spring context and transaction attributes (<strong>line 17<\/strong>)<\/li>\n<li>A transaction is created by the underlying entity manager (<strong>line 26<\/strong>)<\/li>\n<li>The target method is invoked (<strong>line 33<\/strong>)<\/li>\n<li>After returning from the method invocation, the transaction is committed (<strong>line 45<\/strong>)<\/li>\n<\/ul>\n<h5>2) public abstract class org.springframework.transaction.interceptor.<strong>TransactionAspectSupport<\/strong><\/h5>\n<p><strong>Point G &amp; Point H <\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,11,17,18,25,45,46]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/Point G\r\n  protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {\r\n        if (this.transactionManager != null || this.beanFactory == null || txAttr == null) {\r\n            return this.transactionManager;\r\n        }\r\n        String qualifier = txAttr.getQualifier();\r\n      \r\n        \/\/ Case when the transaction manager has been declared directly in the @Transactional annotation\r\n        \/\/  Example @Transactional(value = &amp;quot;myTransactionManager&amp;quot;)\r\n        if (StringUtils.hasLength(qualifier)) {\r\n            return TransactionAspectUtils.getTransactionManager(this.beanFactory, qualifier);\r\n        }\r\n\r\n        \/\/ Case when the transaction manager has been declared in the tx:annotation-driven tag\r\n        \/\/  Example tx:annotation driven transaction-manager=&amp;quot;myTransactionManager&amp;quot;\r\n        else if (this.transactionManagerBeanName != null) {\r\n            return this.beanFactory.getBean(this.transactionManagerBeanName,\r\n                                             PlatformTransactionManager.class);\r\n        }\r\n       ...\r\n       \/\/ Not interesting code\r\n       \r\n    }\r\n\r\n  \/\/ Point H\r\n  protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, \r\n                                                        TransactionAttribute txAttr, \r\n                                                        final String joinpointIdentification) \r\n  {\r\n        \/\/ If no name specified, apply method identification as transaction name.\r\n        \/\/ This is the default case\r\n        if (txAttr != null &amp;amp;&amp;amp; txAttr.getName() == null) {\r\n            txAttr = new DelegatingTransactionAttribute(txAttr) {\r\n                @Override\r\n                public String getName() {\r\n                    return joinpointIdentification;\r\n                }\r\n            };\r\n        }\r\n\r\n        TransactionStatus status = null;\r\n        if (txAttr != null) {\r\n            if (tm != null) {\r\n                \/\/ Call to the AbstractPlatFormTransactionManager to start a transaction\r\n                \/\/ (see Point I)\r\n                status = tm.getTransaction(txAttr); \r\n            }\r\n            else {\r\n                if (logger.isDebugEnabled()) {\r\n                    logger.debug(&amp;quot;Skipping transactional joinpoint [&amp;quot; + joinpointIdentification +\r\n                            &amp;quot;] because no transaction manager has been configured&amp;quot;);\r\n                }\r\n            }\r\n        }\r\n        return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);\r\n    }\r\n<\/pre>\n<p>This class is doing 2 main tasks:<\/p>\n<ul>\n<li>determine the transaction manager to manage the current transaction, either using the <strong>value<\/strong> attribute of the <strong>@Transactional<\/strong> annotation or using the <strong>transaction-manager<\/strong> attribute of the the <em>tx:annotation-driven<\/em> tag <\/li>\n<li>delegates the creation of the transaction to the <strong>AbstractPlatFormTransactionManager<\/strong> class<\/li>\n<\/ul>\n<h5>\n3) abstract class org.springframework.transaction.support.<strong>AbstractPlatformTransactionManager<\/strong><br \/>\n<\/h5>\n<p><strong>Point I<\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,4,5,6,28,29,30,31,51,52,53,72,73]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/Point I\r\n  public final TransactionStatus getTransaction(TransactionDefinition definition) throws\r\n  TransactionException {\r\n        \/\/ Retrieve the transaction from JpaTransactionManager.doGetTransaction()\r\n        \/\/ (see Point J)\r\n        Object transaction = doGetTransaction();\r\n\r\n        \/\/ Cache debug flag to avoid repeated checks.\r\n        boolean debugEnabled = logger.isDebugEnabled();\r\n\r\n        if (definition == null) {\r\n            \/\/ Use defaults if no transaction definition given.\r\n            definition = new DefaultTransactionDefinition();\r\n        }\r\n\r\n        if (isExistingTransaction(transaction)) {\r\n            \/\/ Existing transaction found -&amp;gt; check propagation behavior to find out how to behave.\r\n            return handleExistingTransaction(definition, transaction, debugEnabled);\r\n        }\r\n\r\n        ...\r\n\r\n        \/\/ No existing transaction found -&amp;gt; check propagation behavior to find out how to proceed.\r\n        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {\r\n            throw new IllegalTransactionStateException(\r\n                    &amp;quot;No existing transaction found for transaction marked with propagation 'mandatory'&amp;quot;);\r\n        }\r\n        \/\/ Our case\r\n        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||\r\n                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||\r\n            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {\r\n            SuspendedResourcesHolder suspendedResources = suspend(null);\r\n            if (debugEnabled) {\r\n                logger.debug(&amp;quot;Creating new transaction with name [&amp;quot; + definition.getName() \r\n                             + &amp;quot;]: &amp;quot; + definition);\r\n            }\r\n            try {\r\n                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);\r\n                \/*\r\n                * Return a new DefaultTransactionStatus(transaction,             \r\n                *                                       newTransaction = true,\r\n                *                                       newSynchronization = true,\r\n                *                                       definition.isReadOnly(),debugEnabled,\r\n                *                                       suspendedResources)\r\n                *  for a new transaction\r\n                *\/\r\n                DefaultTransactionStatus status = newTransactionStatus(\r\n                        definition, transaction, true, newSynchronization, \r\n                        debugEnabled, suspendedResources);\r\n               \r\n                \/\/ Real job here, delegates call to JpaTransactionManager.doBegin()\r\n                \/\/ (see Point K)\r\n                doBegin(transaction, definition); \r\n               \r\n                \/\/ Set some synchronization flags to the TransactionSynchronizationManager thread local\r\n                prepareSynchronization(status, definition);\r\n                return status;\r\n            }\r\n            catch (RuntimeException ex) {\r\n                resume(null, suspendedResources);\r\n                throw ex;\r\n            }\r\n            catch (Error err) {\r\n                resume(null, suspendedResources);\r\n                throw err;\r\n            }\r\n        }\r\n        else {\r\n            \/\/ TransactionDefinition = PROPAGATION_SUPPORTS or PROPAGATION_NOT_SUPPORTED\r\n            \/\/                         or PROPAGATION_NEVER\r\n            \/\/ Create &amp;quot;empty&amp;quot; transaction: no actual transaction, but potentially synchronization.\r\n            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);\r\n            return prepareTransactionStatus(definition, null, true, newSynchronization, \r\n                                            debugEnabled, null);\r\n        }\r\n    }\r\n<\/pre>\n<p>The <em>getTransaction()<\/em> delegates the creation and the start of the transaction itself to the underlying <strong>JpaTransactionManager<\/strong>.<\/p>\n<p>We can see here how Spring manages different types of Propagation behavior.<\/p>\n<h5>\n4) org.springframework.orm.jpa.<strong>JpaTransactionManager<\/strong><\/h5>\n<p><strong>Point J &amp; Point K <\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,16,17,18,26,41,42,43,44,45,46,51,64,72,90,91,92,93,94,95,96,121,124,139,140]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/Point J\r\n  protected Object doGetTransaction() {\r\n        JpaTransactionObject txObject = new JpaTransactionObject();\r\n        txObject.setSavepointAllowed(isNestedTransactionAllowed());\r\n\r\n        \/\/ Try to retrieve an EntityManagerHolder from the thread local map of \r\n        \/\/ TransactionSynchronizationManager using the EntityManagerFactory as search key\r\n        \/\/ The EntityManagerFactory was injected in the JpaTransactionManager in the XML config file\r\n        \/\/\r\n        \/\/ bean id=&amp;quot;myTransactionManager&amp;quot; class=&amp;quot;org.springframework.orm.jpa.JpaTransactionManager&amp;quot;\r\n        \/\/     property name=&amp;quot;entityManagerFactory&amp;quot; ref=&amp;quot;myEntityManagerFactory&amp;quot; \r\n        \/\/ bean\r\n        \/\/\r\n        \/\/\r\n       \r\n        \/\/ this EntityManagerHolder might be null when called the first time\r\n        EntityManagerHolder emHolder = (EntityManagerHolder)\r\n                TransactionSynchronizationManager.getResource(getEntityManagerFactory());\r\n        if (emHolder != null) {\r\n            if (logger.isDebugEnabled()) {\r\n                logger.debug(&amp;quot;Found thread-bound EntityManager [&amp;quot; +\r\n                        emHolder.getEntityManager() + &amp;quot;] for JPA transaction&amp;quot;);\r\n            }\r\n            \/\/ attach the EntityManagerHolder to the JpaTransactionObject\r\n            \/\/ the flag false is set to the property newEntityManagerHolder\r\n            txObject.setEntityManagerHolder(emHolder, false);\r\n        }\r\n\r\n        \/\/ The datasource is injected directly into the JpaTransactionManager \r\n        \/\/ after bean initialization (afterPropertySet())\r\n        \/\/ by inspecting the injected EntityManagerFactory\r\n        \/\/\r\n        \/\/  bean id=&amp;quot;myEntityManagerFactory&amp;quot; \r\n        \/\/       class=&amp;quot;org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean&amp;quot;\r\n        \/\/       property name=&amp;quot;dataSource&amp;quot; ref=&amp;quot;myDataSource&amp;quot;\r\n        \/\/       property name=&amp;quot;persistenceUnitName&amp;quot; value=&amp;quot;myPersistenceUnit&amp;quot;\r\n        \/\/  bean\r\n        \/\/\r\n        \/\/\r\n \r\n        \/\/ this test always evaluates to true\r\n        if (getDataSource() != null) {\r\n            ConnectionHolder conHolder = (ConnectionHolder)\r\n                    TransactionSynchronizationManager.getResource(getDataSource());\r\n             \/\/ attach a connectionHolder to the JpaTransactionObject (to start JDBC transaction probably)\r\n            txObject.setConnectionHolder(conHolder);\r\n        }\r\n        return txObject;\r\n    }\r\n\r\n  \/\/Point K\r\n  protected void doBegin(Object transaction, TransactionDefinition definition) {\r\n        JpaTransactionObject txObject = (JpaTransactionObject) transaction;\r\n\r\n        ...\r\n\r\n        try {\r\n           \r\n            \/\/ The EntityManagerHolder can be null if not registered already in the \r\n            \/\/ thread local map of TransactionSynchronizationManager\r\n            if (txObject.getEntityManagerHolder() == null ||\r\n                    txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) {\r\n                \/\/ Create a new EntityManager from the EntityManagerFactory \r\n                EntityManager newEm = createEntityManagerForTransaction();\r\n                if (logger.isDebugEnabled()) {\r\n                    logger.debug(&amp;quot;Opened new EntityManager [&amp;quot; + newEm + &amp;quot;] for JPA transaction&amp;quot;);\r\n                }\r\n\r\n                \/\/ attach the EntityManagerHolder to the JpaTransactionObject\r\n                \/\/ newEntityManagerHolder = true\r\n                \/\/ because the EntityManager has just been created from scratch\r\n                txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true);\r\n            }\r\n\r\n            EntityManager em = txObject.getEntityManagerHolder().getEntityManager();\r\n            final int timeoutToUse = determineTimeout(definition);\r\n\r\n           \/* Delegate to JpaDialect for actual transaction begin, passing the EntityManager\r\n            *  \r\n            *   META-INF|persistence.xml\r\n            *\r\n            *   persistence-unit name=&amp;quot;myPersistenceUnit&amp;quot; transaction-type=&amp;quot;RESOURCE_LOCAL&amp;quot;\r\n            *     provider \r\n            *         org.hibernate.ejb.HibernatePersistence \r\n            *     provider\r\n            *     properties\r\n            *         property name=&amp;quot;hibernate.dialect&amp;quot; value=&amp;quot;org.hibernate.dialect.SQLServerDialect&amp;quot;\r\n            *     ... \r\n            *\/\r\n\r\n            \/\/ (see Point L for HibernateJpaDialect)\r\n            Object transactionData = getJpaDialect().beginTransaction(em,\r\n                    new DelegatingTransactionDefinition(definition) {\r\n                        @Override\r\n                        public int getTimeout() {\r\n                            return timeoutToUse;\r\n                        }\r\n                    });\r\n\r\n            \/\/ Set transaction data to the JpaTransactionObject\r\n            txObject.setTransactionData(transactionData);\r\n\r\n            \/\/ Register transaction timeout.\r\n            if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {\r\n                txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse);\r\n            }\r\n\r\n            \/\/ Register the JPA EntityManager's JDBC Connection for the DataSource, if set.\r\n            if (getDataSource() != null) {\r\n                \/\/ Retrieve the underlying JDBC connection by calling the JPA Dialect class   \r\n                ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly());\r\n                if (conHandle != null) {\r\n                    ConnectionHolder conHolder = new ConnectionHolder(conHandle);\r\n                    if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {\r\n                        conHolder.setTimeoutInSeconds(timeoutToUse);\r\n                    }\r\n                    if (logger.isDebugEnabled()) {\r\n                        logger.debug(&amp;quot;Exposing JPA transaction as JDBC transaction [&amp;quot; + conHolder.getConnectionHandle() + &amp;quot;]&amp;quot;);\r\n                    }\r\n\r\n                    \/\/ Set the JDBC connection to the current Threadlocal resources map, the \r\n                    \/\/ datasource being the key                    \r\n                    TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);\r\n                   \r\n                    \/\/ Set JDBC connection holder to the JpaTransactionObject\r\n                    txObject.setConnectionHolder(conHolder);\r\n                }\r\n                else {\r\n                    if (logger.isDebugEnabled()) {\r\n                        logger.debug(&amp;quot;Not exposing JPA transaction [&amp;quot; + em \r\n                                     + &amp;quot;] as JDBC transaction because JpaDialect [&amp;quot; +\r\n                                getJpaDialect() + &amp;quot;] does not support JDBC Connection retrieval&amp;quot;);\r\n                    }\r\n                }\r\n            }\r\n\r\n            \/\/ If the EntityManager has been created from scratch (see Point L)\r\n            if (txObject.isNewEntityManagerHolder()) {\r\n\r\n                 \/\/ register the EntityManagerHolder to the current Threadlocal resources map, the EntityManagerFactory being the key\r\n                TransactionSynchronizationManager.bindResource(\r\n                        getEntityManagerFactory(), txObject.getEntityManagerHolder());\r\n            }\r\n            txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true);\r\n        }\r\n\r\n        catch (TransactionException ex) {\r\n            closeEntityManagerAfterFailedBegin(txObject);\r\n            throw ex;\r\n        }\r\n        catch (Exception ex) {\r\n            closeEntityManagerAfterFailedBegin(txObject);\r\n            throw new CannotCreateTransactionException(&amp;quot;Could not open JPA EntityManager for transaction&amp;quot;, ex);\r\n        }\r\n    }\r\n<\/pre>\n<p>Most of the important jobs are done in this class.<\/p>\n<p><strong>Point J : doGetTransaction()<\/strong><br \/>\n<\/p>\n<ul>\n<li>\n  first Spring tries to look in the <strong>TransactionSynchronizationManager<\/strong> ThreadLocal map to see if there is an existing entity manager using the entity manager factory as search key (<strong>lines 17 &amp; 18<\/strong>)<\/p>\n<ul>\n<li>\n   The entity manager factory was injected into the Jpa transaction manager in the Spring XML definition.<br \/>\nIf this is not done explicitely, Spring will do the job for you during initialization of the transaction manager by looking for a bean named <em>&#8220;entityManagerFactory&#8221;<\/em> (default name by convention) in the context.<\/p>\n<ul>\n<li>\n     If an entity manager is found in the ThreadLocal map, Spring wraps it around an <strong>EntityManagerHolder<\/strong> object with a boolean flag <em>isNew = false<\/em> since this entity manager has been created before hand somewhere in the code. (<strong>line 26<\/strong>)\n     <\/li>\n<li>\n     Otherwise the <strong>EntityManagerHolder<\/strong> of the <strong>JpaTransactionObject<\/strong> will be <strong>null<\/strong>\n     <\/li>\n<\/ul>\n<\/li>\n<li>\n   Spring also retrieves the <strong>dataSource<\/strong> declared for this transaction manager and stores it in the <strong>JpaTransactionObject<\/strong> (<strong>line 46<\/strong>)\n   <\/li>\n<\/ul>\n<\/li>\n<li>\n<\/li>\n<\/ul>\n<p><strong>Point K : doBegin()<\/strong><\/p>\n<ul>\n<li>Spring checks the <strong>JpaTransactionObject<\/strong> to look for an <strong>EntityManagerHolder<\/strong>.\n<ul>\n<li>If not found, Spring delegates the creation of the entity manager to the attached entity manager factory (<strong>line 65<\/strong>). Then Spring wraps an <strong>EntityManagerHolder<\/strong> object around this entity manager with the flag <em>isNew = true<\/em> to indicate that this entity manager was created in the current transaction and not before (<strong>line 73<\/strong>)\n   <\/li>\n<\/ul>\n<\/li>\n<li>Then Spring delegates the creation of a new JDBC transaction to the underlying JPA Dialect (<strong>line 91 to 97<\/strong>). This dialect is defined in the <strong>META-INF\/persistence.xml<\/strong> file for each <em>persistenceUnit<\/em>\n <\/li>\n<li>Spring registers the current JDBC connection to the <strong>TransactionSynchronizationManager<\/strong> ThreadLocal map using the dataSource as key (<strong>line 125<\/strong>)\n <\/li>\n<li>If the flag <em>isNew = true<\/em> is set on the <strong>JpaTransactionObject<\/strong>, Spring will also register the newly created entity manager to the <strong>TransactionSynchronizationManager<\/strong> ThreadLocal map using the entity manager factory as key.\n <\/li>\n<\/ul>\n<h5>\n5) org.springframework.orm.jpa.<strong>HibernateJpaDialect<\/strong> extends <strong>DefaultJpaDialect<\/strong><br \/>\n<\/h5>\n<p><strong>Point L <\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,9,12]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point L\r\n  public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)\r\n\t\t\tthrows PersistenceException, SQLException, TransactionException {\r\n\r\n\tif (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {\r\n\t\tgetSession(entityManager).getTransaction().setTimeout(definition.getTimeout());\r\n\t}\r\n        \/\/ (see Point M)\r\n\tsuper.beginTransaction(entityManager, definition);\r\n\r\n        \/\/ (see Point N)\r\n\treturn prepareTransaction(entityManager, definition.isReadOnly(), definition.getName());\r\n}   \r\n<\/pre>\n<p>Let&#8217;s consider the <strong>HibernateJpaDialect<\/strong> as default Jpa dialect. We can see that this class is calling the superclass <strong>DefaultJpaDialect<\/strong> to start the transaction (<strong>line 9<\/strong>)<\/p>\n<p>Then it calls the internal method <em>prepareTransaction()<\/em>  (<strong>line 12<\/strong>)<\/p>\n<h5>\n6) org.springframework.orm.jpa.<strong>DefaultJpaDialect<\/strong><br \/>\n<\/h5>\n<p><strong>Point M<\/strong><\/p>\n<pre class=\"brush: java; highlight: [10]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point M\r\n  public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition) \r\n        throws PersistenceException, SQLException, TransactionException {\r\n\t\r\n        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {\r\n\t\tthrow new InvalidIsolationLevelException(\r\n                &amp;quot;Standard JPA does not support custom isolation levels - &amp;quot; \r\n                +&amp;quot;use a special JpaDialect for your JPA implementation&amp;quot;);\r\n\t}\r\n\tentityManager.getTransaction().begin();\r\n\treturn null;\r\n} \r\n<\/pre>\n<p>The transaction is started by the entity manager. We can clearly see that only the default ISOLATION level is supported by vanilla <strong>HibernateJpaDialect<\/strong>. Any attempt to set the isolation level to something other that ISOLATION_DEFAULT will trigger an Exception.<\/p>\n<h5>\n7) org.springframework.orm.jpa.<strong>HibernateJpaDialect<\/strong> extends <strong>DefaultJpaDialect<\/strong><br \/>\n<\/h5>\n<p><strong>Point N<\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,10,11,16,17]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/Point N\r\n  public Object prepareTransaction(EntityManager entityManager, boolean readOnly, String name)\t\r\n  throws PersistenceException {\r\n\r\n\tSession session = getSession(entityManager);\r\n\tFlushMode flushMode = session.getFlushMode();\r\n\tFlushMode previousFlushMode = null;\r\n\tif (readOnly) {\r\n\t\t\/\/ We should suppress flushing for a read-only transaction.\r\n\t\tsession.setFlushMode(FlushMode.MANUAL);\r\n\t\tpreviousFlushMode = flushMode;\r\n\t}\r\n\telse {\r\n\t\t\/\/ We need AUTO or COMMIT for a non-read-only transaction.\r\n\t\tif (flushMode.lessThan(FlushMode.COMMIT)) {\r\n\t\t\tsession.setFlushMode(FlushMode.AUTO);\r\n\t\t\tpreviousFlushMode = flushMode;\r\n\t\t}\r\n\t}\r\n\treturn new SessionTransactionData(session, previousFlushMode);\r\n  }\r\n<\/pre>\n<p>The <em>prepareTransaction()<\/em> method is setting and saving previous flush mode, nothing more that that&#8230;<\/p>\n<h5>\n8 ) org.springframework.transaction.interceptor.<strong>TransactionAspectSupport<\/strong><br \/>\n<\/h5>\n<p><strong>Point O<\/strong><\/p>\n<pre class=\"brush: java; highlight: [8,9]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n    \/\/ Point O\r\n    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {\r\n        if (txInfo != null &amp;amp;&amp;amp; txInfo.hasTransaction()) {\r\n            if (logger.isTraceEnabled()) {\r\n                logger.trace(&amp;quot;Completing transaction for [&amp;quot; + txInfo.getJoinpointIdentification() + &amp;quot;]&amp;quot;);\r\n            }\r\n            \/\/ Delegate the commit call to the underlying TransactioManager\r\n            \/\/ (see Point P)\r\n            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());\r\n        }\r\n    }\r\n<\/pre>\n<p>This method is just delegating the transaction commit to the transaction manager<\/p>\n<h5>\n9) org.springframework.transaction.support.<strong>AbstractPlatformTransactionManager<\/strong><br \/>\n<\/h5>\n<p><strong>Point P<\/strong><\/p>\n<pre class=\"brush: java; highlight: [8,21,22,34,35]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point P\r\n  public final void commit(TransactionStatus status) throws TransactionException {\r\n        if (status.isCompleted()) {\r\n            throw new IllegalTransactionStateException(Transaction is already completed - do not call commit or rollback more than once per transaction&amp;quot;);\r\n        }\r\n        ...        \r\n        \/\/ Not interesting code\r\n        processCommit(defStatus); \/\/ See below\r\n    }\r\n\r\n  private void processCommit(DefaultTransactionStatus status) throws TransactionException {\r\n        try {\r\n            boolean beforeCompletionInvoked = false;\r\n            try {\r\n                \/\/ Commit pre-processing, not always implemented by the actual TransactionManager\r\n                prepareForCommit(status);\r\n                triggerBeforeCommit(status);\r\n                triggerBeforeCompletion(status);\r\n                  \u2026\r\n                    \/\/ Delegate the real commit to the actual TransactionManager\r\n                    \/\/ (see Point Q)\r\n                    doCommit(status); \r\n                }\r\n              ...\r\n             try {\r\n                \/\/ Commit post-processing, not always implemented by the actual TransactionManager\r\n                triggerAfterCommit(status);\r\n              }\r\n            finally {\r\n                triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);\r\n            }\r\n        }\r\n\tfinally {\r\n          \/\/ (see Point M)\r\n\t  cleanupAfterCompletion(status);\r\n\t}\r\n        ...\r\n    }\r\n<\/pre>\n<p>Again, apart from calling some trigger code to prepare the commit, the real job of committing is delegated to the method <em>doCommit()<\/em><br \/>\nAfter the commit is done, <em>cleanupAfterCompletion()<\/em> is called to clean up the <strong>TransactionSynchronizationManager<\/strong> ThreadLocal map if necessary<\/p>\n<h5>\n10) org.springframework.orm.jpa.<strong>JpaTransactionManager<\/strong><br \/>\n<\/h5>\n<p><strong>Point Q<\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,10,11]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/Point Q\r\n  protected void doCommit(DefaultTransactionStatus status) {\r\n        JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();\r\n        if (status.isDebug()) {\r\n            logger.debug(&amp;quot;Committing JPA transaction on EntityManager [&amp;quot; +\r\n                    txObject.getEntityManagerHolder().getEntityManager() + &amp;quot;]&amp;quot;);\r\n        }\r\n        try {\r\n            \/\/ The real commit is done here !\r\n            EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();\r\n            tx.commit();\r\n        }\r\n        catch (RollbackException ex) {\r\n            if (ex.getCause() instanceof RuntimeException) {\r\n                DataAccessException dex = getJpaDialect().\r\n                        translateExceptionIfPossible((RuntimeException) ex.getCause());\r\n                if (dex != null) {\r\n                    throw dex;\r\n                }\r\n            }\r\n            throw new TransactionSystemException(&amp;quot;Could not commit JPA transaction&amp;quot;, ex);\r\n        }\r\n        catch (RuntimeException ex) {\r\n            \/\/ Assumably failed to flush changes to database.\r\n            throw DataAccessUtils.translateIfNecessary(ex, getJpaDialect());\r\n        }\r\n    }\r\n<\/pre>\n<p>Again, the commit is a plain call to <em>getEntityManager().getTransaction().commit()<\/em>, no magic in it.<\/p>\n<h5>\n11) org.springframework.transaction.support.<strong>AbstractPlatformTransactionManager<\/strong><br \/>\n<\/h5>\n<p><strong>Point M<\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,8,9]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point M\r\n  private void cleanupAfterCompletion(DefaultTransactionStatus status) {\r\n\tstatus.setCompleted();\r\n\tif (status.isNewSynchronization()) {\r\n\t\tTransactionSynchronizationManager.clear();\r\n\t}\r\n\tif (status.isNewTransaction()) {               \r\n                \/\/ (see Point N)\r\n\t\tdoCleanupAfterCompletion(status.getTransaction());\r\n\t}\r\n\tif (status.getSuspendedResources() != null) {\r\n\t\tif (status.isDebug()) {\r\n\t \t logger.debug(&amp;quot;Resuming suspended transaction after completion of inner transaction&amp;quot;);\r\n\t\t}\r\n\t\tresume(status.getTransaction(), (SuspendedResourcesHolder) \r\n                      status.getSuspendedResources());\r\n\t}\r\n  } \r\n<\/pre>\n<p>Just an indirection of code, the real job is done in <em>doCleanupAfterCompletion()<\/em><\/p>\n<h5>\n12) org.springframework.orm.jpa.<strong>JpaTransactionManager<\/strong><br \/>\n<\/h5>\n<p><strong>Point N<\/strong><\/p>\n<pre class=\"brush: java; highlight: [1,6,7,32,35]; title: ; toolbar: false; wrap-lines: false; notranslate\" title=\"\">\r\n  \/\/ Point N \r\n  protected void doCleanupAfterCompletion(Object transaction) {\r\n\tJpaTransactionObject txObject = (JpaTransactionObject) transaction;\r\n\r\n\t\/\/ Remove the entity manager holder from the thread.\r\n\tif (txObject.isNewEntityManagerHolder()) {\r\n\t\tTransactionSynchronizationManager.unbindResource(getEntityManagerFactory());\r\n\t}\r\n\ttxObject.getEntityManagerHolder().clear();\r\n\r\n\t\/\/ Remove the JDBC connection holder from the thread, if exposed.\r\n\tif (txObject.hasConnectionHolder()) {\r\n\t\tTransactionSynchronizationManager.unbindResource(getDataSource());\r\n\t\ttry {\r\n\t\t\tgetJpaDialect().releaseJdbcConnection(\r\n                                        txObject.getConnectionHolder().getConnectionHandle(),\r\n\t\t\t\t\ttxObject.getEntityManagerHolder().getEntityManager());\r\n\t\t}\r\n\t\tcatch (Exception ex) {\r\n\t\t\t\/\/ Just log it, to keep a transaction-related exception.\r\n\t\t\tlogger.error(&amp;quot;Could not close JDBC connection after transaction&amp;quot;, ex);\r\n\t\t}\r\n\t}\r\n\tgetJpaDialect().cleanupTransaction(txObject.getTransactionData());\r\n\r\n\t\/\/ Remove the entity manager holder from the thread.\r\n\tif (txObject.isNewEntityManagerHolder()) {\r\n\t\tEntityManager em = txObject.getEntityManagerHolder().getEntityManager();\r\n\t\tif (logger.isDebugEnabled()) {\r\n\t\t\tlogger.debug(&amp;quot;Closing JPA EntityManager [&amp;quot; + em + &amp;quot;] after transaction&amp;quot;);\r\n\t\t}\r\n\t\tEntityManagerFactoryUtils.closeEntityManager(em);\r\n\t}\r\n\telse {\r\n\t\tlogger.debug(&amp;quot;Not closing pre-bound JPA EntityManager after transaction&amp;quot;);\r\n\t}\r\n}\r\n<\/pre>\n<p>Lots of interesting pieces of code here:<\/p>\n<ul>\n<li><strong>Line 6 &amp; 7<\/strong> : if the <strong>JpaTransactionObject<\/strong> has its flag <em>isNew = true<\/em> then Spring remove its from the <strong>TransactionSynchronizationManager<\/strong> ThreadLocal map. Indeed <em>isNew = true<\/em> means that the entity manager was created from scratch for this current transaction and now since the transaction is committed there is no reason to keep it in the ThreadLocal map\n  <\/li>\n<p>  &nbsp;<\/p>\n<li>\n  Similarly, at <strong>line 32<\/strong> Spring will close gracefully the entity manager if flag <em>isNew = true<\/em>\n  <\/li>\n<p>  &nbsp;<\/p>\n<li>If the flag <em>isNew = false<\/em> meaning that the entity manager used in the current transaction has been registered in the <strong>TransactionSynchronizationManager<\/strong> ThreadLocal map before hand, nothing happens. It is not closed and still exists in the ThreadLocal map (<strong>line 35<\/strong>).\n  <\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h1>IV Summary<\/h1>\n<p>After digging into the Spring code for <strong>@Transactional<\/strong>, we can say that:<\/p>\n<ul>\n<li>There is many levels of indirection in the code. A single task like commit requires 3 method calls. I suppose it is due to the open &amp; flexible architecture of Spring which allows end-users to plug their custom implementation of each component. It can also be explained by the fact that the transactional code should be as generic as possible so it can apply not only to JDBC transaction but also to other type of transactions (JMS, Web Services &#8230;)\n <\/li>\n<p> &nbsp;<\/p>\n<li>The <strong>TransactionSynchronizationManager<\/strong> plays the key role in the transaction management. It is acting as a thread-level cache to carry the current entity manager along all layers for the current transaction\n <\/li>\n<p> &nbsp;<\/p>\n<li>The <strong>TransactionSynchronizationManager<\/strong> public methods may suggest that it can be used programmatically to gain finer control on the lifecycle of the entity manager.\n <\/li>\n<p>&nbsp;<br \/>\n Pseudo-code for <strong>@Transactional<\/strong> management:<\/p>\n<\/ul>\n<li>If <strong>TransactionSynchronizationManager.getResource(emf)<\/strong> exists, use it<\/li>\n<p>  &nbsp;<\/p>\n<li>Else, retrieve the <strong>EntityManagerFactory<\/strong> and create a new <strong>EntityManager<\/strong> instance from scratch and register it to the ThreadLocal map with <strong>TransactionSynchronizationManager.bindResource(emf,em)<\/strong>\n<ul>\n<li>Start a new DB transaction  by calling the underlying JPADialect implementation <em>getJpaDialect().beginTransaction(\u2026)<\/em><\/li>\n<p>  &nbsp;<\/p>\n<li>Commit the transaction by calling <strong>entityManager.getTransaction().commit()<\/strong><\/li>\n<p>  &nbsp;<\/p>\n<li>If the current entity manager was created from scratch, remove it from the ThreadLocal map and close it<\/li>\n<p>  &nbsp;<\/p>\n<li>Else do nothing<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Spring is a widely used framework today, bringing many powerfull features and extensions to the Java core stack. However most of people tend to use these features without understanding their underlying mechanism. Since there is no &#8220;magic&#8221; in real life,&#8230;<br \/><a class=\"read-more-button\" href=\"https:\/\/www.doanduyhai.com\/blog\/?p=95\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7,14,17,18],"tags":[47,51],"_links":{"self":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/95"}],"collection":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=95"}],"version-history":[{"count":4,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/95\/revisions"}],"predecessor-version":[{"id":13533,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/95\/revisions\/13533"}],"wp:attachment":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=95"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=95"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=95"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}