Do I need to start and commit transaction while iterate result set?
like the following:
String query = ....;
Connection con = ... // create the connection
con.setAutoCommit(false);
try (Statement stmt = con.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
//do things here
}
} catch (SQLException e) {
// roll back
}
I see that it does work without active transaction and I don't understand why.
If auto-commit is enabled, then the driver will start(*) a transaction for you when you execute a statement, and it will end as soon as execution completes (for a query, that is when you processed all rows or closed the result set, or execution resulted in an exception), or when you execute another statement.
When you disable auto-commit mode, the driver will start a transaction when it is needed (for example, when you execute a statement), but the transaction will need to be manually ended by calling Connection.commit() or Connection.rollback() (or when you enable auto-commit mode again). When you don't explicitly end a transaction, it will remain active until you close the connection (at which point most drivers will rollback, but some - for example Oracle - will commit).
In short, JDBC does not offer a way to explicitly start a transaction, it will start one when necessary. In your example, the transaction was started at stmt.executeQuery(query).
*: This is actually an implementation detail, for some databases, auto-commit mode might be handled entirely server-side
Related
Does Java Connection.close rollback into a finally block?.
I know .Net SqlConnection.close does it.
With this I could make try/finally blocks without catch...
Example:
try {
conn.setAutoCommit(false);
ResultSet rs = executeQuery(conn, ...);
....
executeNonQuery(conn, ...);
....
conn.commit();
} finally {
conn.close();
}
According to the javadoc, you should try to either commit or roll back before calling the close method. The results otherwise are implementation-defined.
In any database system I've worked with, there is no harm in doing a rollback right after the commit, so if you commit in the try block, and rollback in the finally, things get committed, whereas if an exception or early return causes the commit to be missed, the rollback will rollback the transaction. So the safe thing to do is
try {
conn.setAutoCommit(false);
ResultSet rs = executeQuery(conn, ...);
....
executeNonQuery(conn, ...);
....
conn.commit();
} finally {
conn.rollback();
conn.close();
}
Oracle's JDBC driver commits on close() by default. You should not rely on this behaviour if you intend to write multi-platform JDBC code.
The behavior is completely different between different databases. Examples:
Oracle
The transaction is committed when closing the connection with an open transaction (as #Mr. Shiny and New 安宇 stated.
SQL Server
Calling the close method in the middle of a transaction causes the
transaction to be rolled back.
close Method (SQLServerConnection)
For MySQL JDBC, the implementation rolls back the connection if closed without a call to commit or rollback methods.
It is useless to rollback in finally block. After you commit, and commit is successful, why to roll back? So if i were you, i would rollback in catch block.
In EAR application running on Websphere application server 8.5.5 we have to execute CallableStatement (call stored procedure in Oracle DB) which runs more than five minutes or more depending on input data. The operation is automatically rolled back because of transaction timeout (code WTRN0006W) which is set to 120 seconds by default in Websphere AS. We can't change this value due to customers requirements.
We can split input data to smaller chunks and execute CallableStatement several times to achieve shorter run time (30 seconds or so). Processing whole data chunks still takes more than 120 seconds (as expected). But the transaction timeout still occurs although for every statement execution with small chunk (in loop) we are getting connection from datasource configured in WAS, set autocommit to false, after statement execution doing commit and closing connection. Then again with next chunk in next loop cycle.
The whole process of statement executions is done in Stateless EJB which is called from Singleton EJB scheduled to run twice a day. We are not using JTA neither JPA, just JDBC.
Is it possible to avoid transaction timeout if we execute statement several times?
How we obtain datasource during application start:
javax.naming.Context ctx = new InitialContext();
javax.sql.Datasource ds = (javax.sql.Datasource) ctx.lookup("jndi/datasource1");
How we obtain Connection:
java.sql.Connection conn = m24sb.getConnection();
conn.setAutoCommit(false)
How we execute statement:
try (CallableStatement sta = conn.prepareCall("{ call SOME_STORED_PROC }"))) {
// ... setting statement params
sta.execute();
// ... resolving returned values
}
and then commit and closing connection.
Thanks in advance for answers!
Try marking your stateless session bean method as transaction NOT_SUPPORTED or NEVER, which will cause it to run outside of a global transaction. (Note that you would need to do this anyway for your connection.commit() call to be valid -- it likely just isn't getting that far due to the timeout).
#javax.ejb.TransactionAttribute(javax.ejb.TransactionAttributeType.NEVER)
public void yourStatelessEJBMethod() {
... code that invokes stored procedure and commits transaction
}
Hikari: 2.4.7
PostgreSQL JDBC driver: 9.4-1201-jdbc41
I'm trying to understand what must be done to a java.sql.Connection object for it to be available again in the
connection pool?
I've just introduced connection pooling to a multi threaded application that was
previously standing up / tearing down connections with each SQL statement.
What I have noticed, after introducing Hikari, is that as soon as I hit maximumPoolSize every attempt
thereafter to HikariDataSource.getConnection will fail due to connectionTimeout. So it seems like I'm not "releasing" this connection somehow.
The typical use of the Connection object is:
# omits Exception handling, parameter substitution, result evaluation.
PreparedStatement preparedStatement = hikariDataSource.getConnection().prepareStatement(sql);
preparedStatement.executeQuery();
preparedStatement.close();
Is there anything else that is expected to be done on this connection to get it eligible for reuse in the connection pool?
Autocommit is on. Connection.close(), unless doing something special when provided by Hikari, seemed like the exact thing I wanted to avoid.
I don't know Hikari specifically, but for every connection you take out of a connection pool, you have to return that connection when you are done with it.
Typically this is done using Connection.close() - the pool hands out a wrapper function where close() doesn't physically close the connection, only returns it.
So your code should look like this:
Connection con = hikariDataSource.getConnection();
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.executeQuery();
preparedStatement.close();
con.close(); // this returns the connection to the pool
Of course the two close() methods should be called in a finally block.
Got the db connection (conn) from the pool.
Assume that autocommit is TRUE on that connection.
Now conn.setautocommit(false) has set ;
then after few statement updates and finally conn.commit()/conn.rollback() has done.
Now do i need to do explicitly code setautocommit(true) to revert to the previous conn state?
OR commit()\rollback() will set setautocommit(true) inherently ?
That depends on where you got that connection from. If you created the connection yourself, there is no need to restore the state of auto commit.
If you got it from a data source, you should restore the state to what it was because the data source might keep the connections in a pool and the next piece of code might not expect what you set.
commit() doesn't influence the value of auto commit. Enabling auto commit just makes sure that the JDBC driver calls commit() after each statement that you execute. You can still call commit() as often as you like, it just won't have any effect (except that rollback() will not always do what you want).
[EDIT] How auto commit is handled depends on your connection pool. dbcp has a config option to turn auto commit off before giving you a connection, c3p0 will roll back connections when you return then to the pool. Read the documentation for your connection pool how it works.
If you don't know which pool is used, the safe solution is to set auto commit to false whenever you get a connection and to roll back the connection if you get an exception. I suggest to write a wrapper:
public <T> T withTransaction( TxCallback<T> closure ) throws Exception {
Connection conn = getConnection();
try {
boolean autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
T result = closure.call(conn); // Business code
conn.commit();
conn.setAutoCommit(autoCommit);
} catch( Exception e ) {
conn.rollback();
} finally {
conn.close();
}
}
This code will correctly handle the connection for you and you don't need to worry about it anymore in your business code.
Interestingly, conn.setAutoCommit(true); implies a commit (if it's in autoCommit(false) mode, see here, but it might be clearer to people if you still break them out.
In Oracle 12c connection will be defaulted to the autocommit true. But if you set the autocommit as false, you need to reset the autocommit as true before release to the connection pool.
conn.setAutoCommit(autoCommit); should move to the finally block
Does BoneCP (or any other pool) close connection's statements when connection is returned to pool? As I understand, it does not call actual connection's close method, so there is no automatic statement closing. So, does it close statements in any other way or do I need to close them manually?
The JDBC spec is very unclear on what should happen under normal connection close so, irrespective of the pool you use, you should always make sure to close off the statements manually. Consider what would happen to your application if you opt to switch to a different pool in the future that does not do what you expect it to do for you.
As regards BoneCP, the answer is no, it will not close off your statements for you though it can be configured to close off your connections if you forget. This is for performance reasons since some JDBC drivers will close off any still active statements internally if you close off the connection.
However, BoneCP will close off any cached statements if you have statements caching enabled.
EDIT: As of v0.8.0, support has been added to close off unclosed statements (+ print out stack trace of location where statement was opened if you want).
BoneCP (0.8.0 -RC3), there are 2 possible results,
close off with some configuration for non-cached statement only
non-close off no matter how you configure it for cached statement even you invoke the statement.close() explicitly.
There is a StatementCache class to cache the preparedStatement & callableStatement. The default is disabled. You need call BoneCPConfig.setStatementsCacheSize() with the >0 parameter to enable it. After enable the cache,
1 BoneCP.Statement.Close() will bypass the underlying statement close if it is cached.
public void close() throws SQLException {
this.connectionHandle.untrackStatement(this);
this.logicallyClosed.set(true);
if (this.logStatementsEnabled){
this.logParams.clear();
this.batchSQL = new StringBuilder();
}
if (this.cache == null || !this.inCache){ // no cache = throw it away right now
this.internalStatement.close();
}
}
2 BoneCP.Connection.close()
Will just simply clear the cache through the function "clearStatementCaches()"
The good news is MYSQL JDBC driver, Connector/J, will close all the opened statements when you close the connection through the function "closeAllOpenStatements()"