COMMIT OR conn.setAutoCommit(true) - java

I have noticed some programmer using COMMIT other using conn.setAutoCommit(true); to end the transaction or roll back so what are the benefits of using one instead of the other?
Where is the main difference?
conn.setAutoCommit(true);
over
statement.executeQuery(query);
statement.commit();

You should in general use Connection.commit() and not Connection.setAutoCommit(true) to commit a transaction, unless you want to switch from using transaction to the 'transaction per statement' model of autoCommit.
That said, calling Connection.setAutoCommit(true) while in a transaction will commit the transaction (if the driver is compliant with section 10.1.1 of the JDBC 4.1 spec). But you should really only ever do that if you mean to stay in autoCommit after that, as enabling / disabling autoCommit on a connection may have higher overhead on a connection than simply committing (eg because it needs to switch between transaction managers, do additional checks, etc).
You should also use Connection.commit() and not use the native SQL command COMMIT. As detailed in the documentation of Connection:
Note: When configuring a Connection, JDBC applications should use the appropriate Connection method such as setAutoCommit or setTransactionIsolation. Applications should not invoke SQL commands directly to change the connection's configuration when there is a JDBC method available.
The thing is that commands like commit() and setAutoCommit(boolean) may do more work in the background, like closing ResultSets and closing or resetting Statements. Using the SQL command COMMIT will bypass this and potentially bring your driver / connection into an incorrect state.

The usage of conn.setAutoCommit(); applies to the connection and gives you the possibility to execute X queries in a single transaction, or use one single transaction per execute
As the API describes:
void setAutoCommit(boolean autoCommit)
throws SQLException
Sets this connection's auto-commit mode to the given state. If a connection is in auto-commit
mode, then all its SQL statements will be executed and committed as
individual transactions. Otherwise, its SQL statements are grouped
into transactions that are terminated by a call to either the method
commit or the method rollback. By default, new connections are in
auto-commit mode
For a simple case:
conn.setAutoCommit(false);
statement.executeQuery(query);
statement.commit();
will be the same as:
conn.setAutoCommit(true);
statement.executeQuery(query);

Related

Will con.rollback() rollback already commited changes by con.commit()?

I am working on Legacy project now and it requires some deep knowledge of low level JDBC API
in some places I see code like this:
try {
con = ....
con.setAutoCommit(false);
//insert_1 pack to db
con.commit();
//insert_2 pack to db
con.commit();
//insert_3 pack to db
con.commit();
} catch (SQLException e) {
try {
con.rollback();
} catch (SQLException e) {
log.warn("SQLException on rolling back the destination connection. ", e);
throw e;
}
throw ex;
}
and sometimes con.rollback(); is not invoked in the the catch:
try {
con = ....
con.setAutoCommit(false);
//insert_1 pack to db
con.commit();
//insert_2 pack to db
con.commit();
//insert_3 pack to db
con.commit();
} catch (SQLException e) {
throw new MyBusinessException(ex);
}
Could you please explain difference from transaction standpoint ?
P.S.
I've read java doc for rollback but it doesn't answer my question.
Undoes all changes made in the current transaction and releases any
database locks currently held by this Connection object. This method
should be used only when auto-commit mode has been disabled. Throws:
SQLException – if a database access error occurs, this method is
called while participating in a distributed transaction, this method
is called on a closed connection or this Connection object is in
auto-commit mode See Also: setAutoCommit
The code looks incorrect.
There are several commits executed. This would mean that the explicit rollback could just rollback the state to the beginning of the last commit. This is, I guess, not what the rollback should do.
If no explicit rollback is called, at least in a pooled database connection (in a pooled database connection the connection is never closed, but reused) the already executed statements are still in transaction. I.e. calling here a rollback later would lead to strange kind of errors and also rolling back the last executed statements actually not related to the current business process. I see in the Hikari Connection Pool Implementation that dirty states are automatically rolled back, but maybe this correct behavior cannot be assumed for every implementation. E.g. the Apache DBCP is not that straight forward in the code and asking the maintainers if they are doing this should be helpful.
I see no close call.
For your questions this means the second code snippet should:
In a single non pooled connection not commit the last statements after the last commit, this is what would be correct (by accident).
In a pooled connection leave the transactions pending which could lead to the error that the next business logic committing something and reusing the connection will also commit this.

Does autoCommit var resets in c3p0 on close?

I am working with DB2. I created ConnectionPool for it. Some of the queries required to be executed in "aumoCommit = false" mode.
Connection con = ConnectionPool.getConnection // wrapper
con.setAutoCommit(false);
PreparedStatment ps = con.prepareStatement(// query... );
ps.setString(...);
ps.executeUpdate();
con.commit();
ps.close();
con.close();
con.setAutoCommit(true); // should be here ?
Question is: should i add con.setAutoCommit(true) line in the end or C3P0 resets state of every closed(returned to pool) Connection?
Edit: after a few comments, I add that my question is: should con.setAutoCommit(false) call be there in general or c3P0 reset state of this connection and nexct time this connection will be in con.setAutoCommit(false) by default?
c3p0 always releases Connections with autoCommit set to true, per the JDBC spec.
In general, c3p0 endeavors to make it true that nothing you do during an ordinary client session will have any effect on future client sessions. Once a Connection is checked back into the pool, c3p0 fully resets and restores its state.
The only place you can define Connection state that "sticks" across client sessions is in a ConnectionCustomizer's onAcquire(...) method, called before any client Session has been initiated. But this only works for Connection attributes whose value is undefined by the JDBC spec. Since autoCommit is required by spec to be true of a new Connection, and JDBC transparent Connection pooling requires pooled and new Connections be indistinguishable in application semantics, freshly checked-out Connections always have autoCommit set to true.

Do the statements execute the batches synchronously in jdbc

In a multi-threaded environment, each thread is executing this call() >>
PreparedStatement st1 = null
PreparedStatement st2 = null;
.....
.....
st1.executeBatch();
st2.executeBatch();
connection.commit();
Is it guaranteed that for each thread, the sql batches inside st1 will execute BEFORE the sql batches in st2? In other words, is the synchronous behavior guaranteed in this case?
Thanks
According to the documentation, the connection object can have auto-commit mode either enabled (default mode) or disabled. When auto-commit is false:
If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either the method commit or the method rollback. By default, new connections are in auto-commit mode.
Therefore if auto-commit is false, the first batch will be committed before the second. If auto-commit is true, the first and second batches will be committed as one transaction. Note that Connection.close() should only be used when auto-commit is false.
In both cases, the execution order (regardless of the commit mode) follows the order in the code.

Behaviour regarding db pool and connection.setReadOnly() method

I have Java application with Hibernate framework(no spring) connect to MySQL DB , manage connection pooling via c3p0
i try to configure my apllication to read from slave db and write to master db , i have following this link to some extend Master/Slave load balance
let's say if the application already got a session with connection in pool and it need to execute a read-only method , like this
public someReadOnlyMethod()
{
Session session = (get session from current Thread)
//set read-only so that it read from slave db
session.connection().setReadOnly(true);
(...connect to db to do something...)
//set it back in case of this method is followed by write method so that it go to master db
session.connection().setReadOnly(false);
}
Is the pooling create a new connection to connect to db 2 times for read-only and write operation(if so,this will heavily impact performance) or it smart enough to swap the operation to already existing read-only and writable connection pool ?
thx for your advice.
so this has nothing to do with the pool; it's all in the mysql driver. c3p0 will pass your call to setReadOnly (whether true or false) to the underlying Connection, and the Connection will route to the master or the slaves accordingly.
if you don't like how your Connections default (probably by default they are not read only), you can set the read-only property in the onAcquire method of a c3p0 ConnectionCustomizer, and the value use set (true or false) will become th default that c3p0 resets Connections to.
good luck!
tl;dr: It will re-use existing connections whenever you switch setReadOnly(true/false).
JDBC will connect to all servers listed in your connection URL when you do ReplicationDriver().connect(url). Those connections will remain open for re-use no matter how many times you switch setReadOnly().
Source: I just tested Connector/J version 5.1.38 with com.mysql.jdbc.ReplicationDriver.

JDBC opening a new database session

I just want to make sure that if I use the following, I am opening a separate DB session and not resuing the same one (for testing I need individual sessions).
Connection connection = DriverManager.getConnection(URL,USER,PASSWORD);
each time I do the above code, I run my query, then do a connection.close()
So for example:
while(some condition) {
Connection connection = DriverManager.getConnection(URL,USER,PASSWORD);
//now use the connection to generate a ResultSet of some query
connection.close();
}
So, each iteration of the loop (each query) needs its own session.
Is this properly opening separte sessions as I need (and if not, what would I need to add/change)? thanks
The javadoc says:
Attempts to establish a connection to
the given database URL
Slightly woolly language, and I suspect that this is up to the JDBC driver, but I'd be surprised if this did anything other than open a new connection.
I suppose it's possible for a JDBC driver to perform connection pooling under the hood, but I'd be surprised to see that.
In the case of the Oracle JDBC driver, this will open a new connection every time. This is a relatively slow process in Oracle, you may want to consider using a connection pool (e.g. Apache Commons DBCP, or c3p0) to improve performance.

Categories