Announcement Announcement Module
Collapse
No announcement yet.
Difference between having a transaction manager and not Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Difference between having a transaction manager and not

    Due to some legacy code, we are having to get our own jdbc connections. To take advantage of Spring's transaction management, I created a DAO that uses DataSourceUtils.doGetConnection(dataSource) to get the connections for that DAO. In the app I created this DAO for, I define the transaction stuff in the application context as follows:

    Code:
    <tx:annotation-driven /> 
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource"/>
    </bean>
    All the methods in the DAO have the SUPPORTS propagation, and currently there is nothing calling it within a transaction.

    This worked fine without any problems. Then, someone recently created a new app using this DAO, but they did not define a transaction manager. He found that the calls to this DAO were taking a long time. Once he added the transaction manager, it went quickly. After some testing I found that without the transaction manager, the DataSourceUtils.doGetConnection(dataSource) call would take about a tenth of a second, while with the transaction manager, it would take 1 or 2 milliseconds.

    My questions is, what exactly is the transaction manager doing that makes it get the connection so much quicker? I would understand differences in speed if everything was within a single transaction in one case and not the other. But the way it is set up, neither are running in a transaction. I tried googling around without too much success, so I'm hoping someone with an understanding of the underlying transaction manager/DataSourceUtils functionality can explain exactly what is happening within the transaction manager that makes the get of the connection so much faster. Thanks.

  • #2
    Without looking at code. I think what is happening is how Spring Transactions work with DataSources to manage them for you.

    Basically, one part is that Spring uses a technique in which a Connection is retrieved from the pool once and placed into ThreadLocal, so that all calls within that transaction reuse that one Connection. So if I have a transactional Service method that calls three Repositories, or you called them DAO. In the first repository call a Connection is retrieved from the pool. Once retrieved it is placed into ThreadLocal. the next repository gets called and instead of getting a new Connection from the datasource it sees that there is already one in ThreadLocal and uses it instead.

    So without a Transaction, it sounds like the getting a Connection from the DataSource for each call is what is slowing it down. Basically without a Transaction, each DAO call has to go to the DataSource to get a new Connection. And if your DataSource is not configured to have many Connections in the pool, then it has to go down to the database to make a new server process and create a new Connection. That is why Connections are so important to manage correctly or you can really degrade performance.

    Hope that helps clear things up for you.

    Mark

    Comment


    • #3
      I apologize if I wasn't totally clear in my original post. I do not have transactional service (no @Transactional annotation on the method calling the DAO), and all the DAO methods have @Transactional(propagation=Propagation.SUPPORTS). So, nothing is actually running within a transaction.

      The difference is that in one case, there is a transaction manager defined in the application context as seen in my original post, and in the other case, there is no transaction manager defined. In both cases, connections are retrieved by calling DataSourceUtils.doGetConnection(dataSource). Even though it's not actually running in a transaction, the first case (with transaction manager) retrieves connections much quicker than the second case (no transaction manager).

      I'm just wondering what the transaction manager is doing that allows it to get connections so much quicker. When you say "a Connection is retrieved from the pool once and placed into ThreadLocal", does that happen every time, whether or not you're actually in a transaction? So that even though all the calls I make to my DAO aren't happening within the same transaction, the transaction manager is still pulling the same connection out of ThreadLocal without having to create new ones from the dataSource?

      Thank you for any additional information you can give on this.

      Comment


      • #4
        What I have found, is that if you forget to define a transaction manager, or forget to put a @Transactional annotation, then DataSourceUtils.doGetConnection(dataSource) will always get a new connection from the Datasource. So, if you have 5 DAO calls, this will consume 5 slots in the connection pool (this is very bad). We have avoided this by implementing an AOP advice to abort calls to public @Service methods if there is no Transaction manager found.


        Code:
        	@Pointcut("@within(org.springframework.stereotype.Service) && execution(public * *(..))")
        	public void publicServiceMethods(){}
        	
        	@Around("publicServiceMethods()")
        	public Object killUnAnnotatedServiceCalls(ProceedingJoinPoint pjp) throws Throwable {
        		String methodName = pjp.getSignature().getName();
        		try {
        			TransactionAspectSupport.currentTransactionStatus();
        		} catch (NoTransactionException e){
        			throw new IllegalStateException("No transactional annotation on a service method! class=" + pjp.getTarget().getClass() + " method=" + methodName);
        		}
        		Object o = pjp.proceed();
        		return o;
        	}

        Comment


        • #5
          To answer it more directly, without a TransactionManager, there is no place for Spring to 'cache' shared connections. It doesnt blow up, but each subsequent call to DataSourceUtils.doGetConnection(dataSource) will get a new connection from the pool.

          Comment


          • #6
            I guess that's what my question is. What exactly is the transaction manager doing? Does it always keep the connection for the life of the thread so that it doesn't have to go out to the pool every time? It does this whether or not you're calling the DAO within a @Transactional annotation that requires a transaction?

            I understand the need for it to hold onto a connection when calling several methods within the same transaction, but does it just always do this, and then it knows whether or not to actually commit the transaction based on the propagation for the @Transactional annotation?

            Comment


            • #7
              Originally posted by dnc253 View Post
              I guess that's what my question is. What exactly is the transaction manager doing? Does it always keep the connection for the life of the thread so that it doesn't have to go out to the pool every time? It does this whether or not you're calling the DAO within a @Transactional annotation that requires a transaction?
              Yea that is what it is doing, the connection is for the life of the thread. Thus ThreadLocal... but, the strategy that it uses will be based upon the exact TransactionManager implementation that is chosen. It is not just so it doesn't go to the pool every time, it is also so that it can use the same Connection for which it has set autocommit = false, and commit (or rollback) as necessary on method exit. Things get more complicated with mulitple datasources (JTA / 2 phase commit, etc). Without a transaction manager, or without @Transactional, it is doing some default behavior, which appears to me to cause it to get new connections all the time. I don't know how it knows to close them up.

              Originally posted by dnc253 View Post
              I understand the need for it to hold onto a connection when calling several methods within the same transaction, but does it just always do this, and then it knows whether or not to actually commit the transaction based on the propagation for the @Transactional annotation?
              the propagation is more for if you have nested calls ie
              Code:
              Class1 {
              @Transactional
              method1(){
              ... call Class2.method2()
              }
              }
              
              public Class2{
              @Transactional
              method2(){
              
              }
              }
              what should it do in this situation? propagation is how you control this.
              Last edited by dominic_veit; Feb 14th, 2012, 03:37 PM.

              Comment


              • #8
                Thank you for your responses. I think I'm getting close to understanding all this.

                We are in the very beginning stages of using the @Transactional annotations. Most of our stuff does not have them at all. I'm just trying to understand was is going on when you start adding those annotations. So, even if all your methods were annotated with @Transactional(propagation=NEVER), the transaction manager would still bind the first retrieved connection to the current thread, and retrieve it from there after that? So even if you aren't taking advantage of having several methods all be part of the same transaction (via @Transactional(propagation=REQUIRED)), just having any kind of @Transactional annotation and a transaction manager gives you the advantage of binding a connection to a thread and not having to retrieve one from the pool every time?

                Comment


                • #9
                  i am having the same problem, after completion of my transaction in the database i m trying to trigger a mail to the user. suppose if mail server is down in that case mailsendexception will come, this time i want to prompt the user to continue without mail or roll back the whole transaction if user says yes then i want to continue or else i need to rollback the whole transaction . i am calling my mail triggering class inside the transaction as soon as my data base operation completes. please guide me how to do the same.

                  Comment


                  • #10
                    I would not do this inside the method which is annotated with @Transactional. Have your @Service class do the database operation inside a method annotated with @Transactional. Then, create a second class in front which calls the method, and then sends the email. That way, you are not sending the email, until the database transaction has been committed.

                    Comment

                    Working...
                    X