Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the connection inside of a Spring transaction?

Imagine this code:

foo() {
     Connection conn = ...;
}

foo() has been called from a method that has the annotation @Transactional. How do I get the current JDBC connection? Note that foo() is in a bean (so it can have @Autowired fields) but foo() can't have parameters (so I can't pass the connection in from somewhere).

[EDIT] I'm using jOOQ which needs either a data source or a connection. My problem: I don't know which transaction manager is configured. It could be anything; Java EE, DataSource based, something which gets the data source via JNDI. My code isn't an application, it's a library. I need to swallow what others put on my plate. Along the same lines, I can't request a Hibernate session factory because the application using me might not use Hibernate.

But I know that other code, like the Spring Hibernate integration, somehow can get the current connection from the transaction manager. I mean, Hibernate doesn't support Spring's transaction manager, so the glue code must adapt the Spring API to what Hibernate expects. I need to do the same thing but I couldn't figure out how it works.

[EDIT2] I know that there is an active transaction (i.e. Spring has a Connection instance somewhere or at least a transaction manager which can create one) but my method isn't @Transactional. I need to call a constructor which takes java.sql.Connection as parameter. What should I do?

like image 571
Aaron Digulla Avatar asked Aug 02 '12 14:08

Aaron Digulla


People also ask

How do Spring transactions work internally?

Transactions and Proxies. At a high level, Spring creates proxies for all the classes annotated with @Transactional, either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method, mainly for starting and committing the transaction.

What does @transactional annotation mean?

The @Transactional annotation is metadata that specifies that an interface, class, or method must have transactional semantics; for example, "start a brand new read-only transaction when this method is invoked, suspending any existing transaction".

Which interface manages the Spring core transaction?

Spring offers you a PlatformTransactionManager / TransactionManager interface, which, by default, comes with a couple of handy implementations. One of them is the datasource transaction manager. You create a database-specific or connection-pool specific datasource here. MySQL is being used for this example.


3 Answers

You can probably try DataSourceUtils.getConnection(dataSource), per the API it should return you the current connection for the datasource.

Update: Based on your comments and the source code for org.springframework.transaction.support.TransactionSynchronizationManager :

Like I said, the key to getting the connection is the datasource name, if this cannot be obtained, one way out by looking at the source code is to try this:

TransactionSynchronizationManager.getResourceMap() will return a map of datasource to ConnectionHolder in the current thread, assuming that you have only 1 resource involved in transaction, you can probably do a map.values().get(0) to get the first ConnectionHolder, from which you can get a connection by calling .getConnection()

So essentially calling the following:

TransactionSynchronizationManager.getResourceMap().values().get(0).getConnection()

There probably has to be better way though :-)

like image 123
Biju Kunjummen Avatar answered Sep 19 '22 11:09

Biju Kunjummen


I assume you are using Plain Jdbc, you need to do is :

BaseDao {
    @Autowired
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Connection getConnection() {
        // ....use dataSource to create connection
        return DataSourceUtils.getConnection(dataSource);
    }
}

FooDao extends BaseDao {
    // your foo() method
    void foo() {
       Connection conn = getConnection();
       //....
    }
}
like image 24
Nandkumar Tekale Avatar answered Sep 20 '22 11:09

Nandkumar Tekale


(completely rewritten based on comment thread; not sure why my original answer was focused on Hibernate, other than that's what I'm working with right now)

The transaction manager is completely orthogonal to data sources. Some transaction managers interact directly with data sources, some interact through an intermediate layer (eg, Hibernate), and some interact through services provided by the container (eg, JTA).

When you mark a method as @Transactional, all that means is that Spring will generate a proxy when it loads your bean, and that proxy will be handed to any other class that wants to use your bean. When the proxy's method is invoked, it (the proxy) asks the transaction manager to either give it an outstanding transaction or create a new one. Then it calls your actual bean method. When your bean method returns, the proxy interacts with the transaction manager again, to either say "I can commit" or "I must rollback". There are twists to this process; for example, a transactional method can call another transactional method and share the same transaction.

While the transaction manager interacts with the DataSource, it does not own the DataSource. You cannot ask the transaction manager to give you a connection. Instead, you must inject a frame-specific object that will return connections (such as the Hibernate SessionFactory). Alternatively, you can use the static transaction-aware utility classes, but these again are tied to a specific framework.

like image 41
parsifal Avatar answered Sep 19 '22 11:09

parsifal