We are evaluating a JTA transaction manager for a legacy Oracle JDBC project and have looked at Bitronix and Atomikos so far.
The java.sql.DataSource implementations of both the Bitronix and Atomikos connection pools are making heavy use of dynamic proxy objects for the JDBC interface instances they return.
The PreparedStatements of the Bitronix PoolingDataSource connections are themselfs dynamic proxy objects, casting them to oracle.jdbc.OraclePreparedStatement results in a ClassCastException.
The Atomikos dynamic connection proxy objects on the other hand return actual OraclePreparedStatement instances - casting possible.
The legacy code uses Oracle JDBC update batching and hence casts java.sql.PreparedStatements to oracle.jdbc.OraclePreparedStatement for calling OraclePreparedStatement.setExecuteBatch(batchSize).
Switching to standard JDBC batching is not an option.
How are we supposed to use special JDBC driver features that require access to the actual driver classes/interfaces in the given situation?
If the connection pools support the java.sql.Wrapper interface properly, you can get access to the wrapped object using the unwrap method.
Thanks, that indeed worked for both Bitronix 2.1.2 and Atomikos 3.7.0 - assuming the JDK6 Oracle driver of cause.
I had tested unwrap(oracle.jdbc.OraclePreparedStatement.class) before, since oracle.jdbc.OraclePreparedStatement is the interface for the driver's PreparedStatement classes. I got a "bitronix.tm.resource.jdbc.JdbcUncachedPreparedStatementHandle is not a wrapper for interface oracle.jdbc.driver.OraclePreparedStatement" SQLException though.
Looking at the source of the Bitronix proxy class (JdbcUncachedPreparedStatementHandle) I found:
public Object unwrap(Class iface) throws SQLException {
if (PreparedStatement.class.equals(iface)) {
return delegate;
}
throw new SQLException(getClass().getName() + " is not a wrapper for interface " + iface.getName());
}
Wouldn't that rather have to be
public Object unwrap(Class iface) throws SQLException {
if (iface.isAssignableFrom(delegate.getClass()) {
return delegate;
}
throw new SQLException(getClass().getName() + " is not a wrapper for interface " + iface.getName());
}
?
Anyway - when I use this code it works for both Bitronix and Atomikos - Atomikos not being an issue anyway since the returned PreparedStatement is not a proxy object as mentioned above.
((OraclePreparedStatement)pstmt.unwrap(PreparedStatement.class)).setExecuteBatch(100);
Related
I wondered how works method getConnection() at DataSource? I assume that DataSource calling DriverManager.getConnection every time when we call DataSource.getConnection with our properties that we setting in DataSource. Is that true?
The answers to your question can be deduced from the DataSource javadoc.
"The DataSource interface is implemented by a driver vendor. There are three types of implementations:
Basic implementation -- produces a standard Connection object
Connection pooling implementation -- produces a Connection object that will automatically participate in connection pooling. This
implementation works with a middle-tier connection pooling manager.
Distributed transaction implementation -- produces a Connection object that may be used for distributed transactions and almost always
participates in connection pooling. This implementation works with a
middle-tier transaction manager and almost always with a connection
pooling manager."
Thus:
I wondered how works method getConnection() at DataSource?
It is vendor specific, and depends on the type of implementation that the vendor provides.
I assume that DataSource calling DriverManager.getConnection every time when we call DataSource.getConnection with our properties that we setting in DataSource. Is that true?
Not necessarily. For example, DataSource.getConnection() could return the same Connection object each time it is called. Or more plausibly, it could return a new Connection proxy for an underlying database pool connection that has been recycled. Furthermore, that DriverManager method is not necessarily called to get the connection.
If you want to know how a specific DataSource works, you would need to look at the vendor documentation ... or alternatively its source code.
I have plain JDBC code which is doing transaction management using Connection Interface. I wanted to switch to Spring Transaction Management in small steps.
Firstly I want to provide PlatformTransactionManager for my datasource and annotate my class / methods with #Transaction and keep my other logic same ie. using connection / PreparedStatement etc.
All the examples, which I see use JdbcTemplate. I was wondering can Spring Transaction be used without JdbcTemplate?
Technically it is possible to use #Transactional without JdbcTemplate . But if you try to do it , you will sooner or later find that you are re-inventing what the things that are already done by JdbcTemplate.
What #Transactional does is that before executing a #Transactional method , it will help you to get a JDBC Connection from the DataSource , and start a transaction on this Connection .The JDBC Connection will then stored in a ThreadLocal.
That means if you do it without JdbcTemplate , you have to manually get this Connection from that ThreadLocal such that you can create a JDBC Statement from it for executing your SQL. Not to mention you have to manually release the JDBC resources such Statement , ResultSet etc. properly by yourself which all of these things are already take care by JdbcTemplate.
But if you have already implemented these JDBC codes manually and just want to let #Transactional to handle the transaction , you could try to inject the DataSource to your bean and then use the following method to get the Connection for your JDBC codes use :
Connection connection = DataSourceUtils.getConnection(dataSource);
Also checkout JdbcTemplate#execute(ConnectionCallback<T> action) , it is useful for migrating the existing JDBC codes that expects a JDBC Connection to work on to JdbcTemplate.
Yes it's possible. Adding a #Transactional annotation to your methods, as long as they follow the correct procedure should make your methods transactional. But as others have mentioned if you're in the process of updating your app you might as well ditch plain JDBC and move across to Spring JPA/JDBC (depending on which version of Spring you're using).
I want to use JOOQ to access my database from the Ninja Framework. How can I get a JDBC connection from a controller?
Here's resources I found that didn't quite work.
How to retrieve the datasource used by a persistence unit programmatically - Tedious set of steps to get the connection from an EntityManager.
http://blog.jooq.org/2015/05/26/type-safe-queries-for-jpas-native-query-api/ - works by building a query in JOOQ and passing to EntityManager.createNativeQuery. It's functional, but it's not as nice as just having the connection.
Could I inject the connection into a controller like so:
public Result myController(#DBConnection Connection connection) {
List<String> articles = DSL.using(connection).selectFrom(ARTICLE).fetch(ARTICLE.TITLE);
return Results.html().render("template", articles);
}
DropWizards has a plugin that looks like a winner: https://github.com/benjamin-bader/droptools/tree/master/dropwizard-jooq
public BlogPost getPost(#QueryParam("id") int postId, #Context DSLContext database) {
BlogPostRecord post = database
.selectFrom(POST)
.where(POST.ID.equal(postId))
.fetchOne();
// do stuff
}
Following up on #LukasEder's answer this is the approach:
HibernateEntityManagerFactory hibernateEntityManagerFactory = ((EntityManagerImpl) entityManager).getFactory();
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
C3P0ConnectionProvider c3P0ConnectionProvider = (C3P0ConnectionProvider) sessionFactoryImpl.getConnectionProvider();
Connection connection = c3P0ConnectionProvider.getConnection();
This is obviously very very strange and bad code.
A clean solution is to provide access to Connection / DataSource by Ninja directly (separating the connection pool from Hibernate or any implementation). That is not too hard and is partly done in the ebeans plugin. Let's discuss that on our mailing list if you are interested in contributing code :)
Short of any option to retrieve a JDBC Connection or DataSource from ninja framework directly, the standard approach should be to "unwrap" it from the EntityManager:
Connection connection = em.unwrap(Connection.class);
See also: How to get DataSource or Connection from JPA2 EntityManager in Java EE 6
A Hibernate-specific approach is documented here: How to retrieve the datasource used by a persistence unit programmatically
If I load a City object from DB using Hibernate session, and then using the same session which has not been used for an hour, if I do a city.getCountry() which should lazy load the Country object, I get an Exception (Connection marked as broken because of SQLSTATE(08003)). I got two general questions:
Is lazy loading just another new JDBC connection and totally
independent connection under the hood?
What kind of parameters in hibernate connection pool configuration I
need to tune to increase the timeout for these kind of situations?
I'm not using transactions.
I'm using HikariCP and my DB is Postgrsql.
I have a large amount of legacy code that relies on being able to pass around a DataSource instead of a Connection object. I can see sample code for making a connection, ie:
Class.forName("org.apache.cassandra.cql.jdbc.CassandraDriver");
con = DriverManager.getConnection("jdbc:cassandra:root/root#localhost:9160/MyKeyspace");
However I can't see from the documentation any way to create a DataSource. Am I going to have to write my own DataSource to wrap the above code?
You can use BasicDataSource class of Apache Commons DBCP http://commons.apache.org/proper/commons-dbcp/ which is a DataSource implementation that can work with any JDBC driver. See usage example here http://www.kodejava.org/how-do-i-create-a-basicdatasource-object/
You can use the CassandraDataSource class.
https://code.google.com/a/apache-extras.org/p/cassandra-jdbc/source/browse/src/main/java/org/apache/cassandra/cql/jdbc/CassandraDataSource.java