I was setting con.setAutoCommit(false); as soon as I create connection so that nothing goes in DB uncommitted. But it turns out if you close the connection all transaction will be committed, no matter what your setAutoCommit() status is.
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection("jdbc:oracle:thin:#192.168.7.5:xxxx:xxx", "xxx", "xxx");
con.setAutoCommit(false);
st = con.createStatement();
String sql = "INSERT INTO emp (eid, name, dob, address) VALUES (?, ?, ?, ?)";
PreparedStatement statement = con.prepareStatement(sql);
statement.setInt(1, 8 );
statement.setString(2, "kkk" );
statement.setDate(3, date );
statement.setString(4, "pppp" );
pst = con.prepareStatement(sql);
int rowsInserted = statement.executeUpdate();
//con.commit ();
System.out.println ("rowsInserted "+rowsInserted);
con.close ();
Even after commenting con.commit (); row is being inserted when connection closes so I was wondering what is the use of con.commit (); ?
Another answer says it's vendor specific:
If a Connection is closed without an explicit commit or a rollback; JDBC does not mandate anything in particular here and hence the behaviour is dependent on the database vendor. In case of Oracle, an implict commit is issued.
It does not make sense.Thanks.
Logging off of Oracle commits any pending transaction. This happens whether you use sqlplus or the JDBC driver through conn.close(). Note that it's not the driver that issues the commit it's the server. During logoff the server commits pending changes. From your Java program you can always call conn.rollback() before calling conn.close() if you want to make sure that pending changes are not committed.
You asked what is the use of conn.commit(). It's used to explicitly commit a transaction at a specific point of your business logic. For example if you cache connections in a connection pool, you can disable auto-commit and commit pending changes before releasing the connection back to the pool. Some people prefer enabling auto-commit mode (which is the default in JDBC) and not worry about explicitly committing or rolling back changes. It depends on your business logic. You can ask yourself: will I ever need to rollback a DML execution? If the answer is yes then you should disable auto-commit and explicitly commit transactions.
Oracle documentation provides a very good explanation of when and why this should be used. Please go through the same!
If your JDBC Connection is in auto-commit mode pro grammatically or by default, (this is by default, fyi), then every SQL statement is committed to the database upon its completion.
You can refer to this question for more detailed explanation on the same topic.
con.commit() is an explicit commit and you can call it whenever you have to commit the transaction. In your case there is no explicit commit or rollback though you have set AutoCommit to false. The Oracle Database commits all the transactions of a session which exits the connection gracefully. If the session terminates abnormally then it rolls back the transactions.
Related
I have a Java application where I am executing some queries to a SQL server 2008 database.
I am trying to execute a stored procedure with that piece of code:
//...
try (Connection connection = dataSource.getConnection()) {
PreparedStatement preparedStmt = connection.prepareStatement("exec dbo.myProc");
preparedStmt.execute();
connection.commit();
connection.close();
}
//...
But with some debugging I found out that the procedure was not over when the connection is being commited and closed.
So my question is, why is that ? And how can I ensure that the procedure is over before closing the connection ?
Make sure you have SET NOCOUNT ON in the proc code and/or consume all results returned using preparedStmt.getMoreResults(). This will ensure the proc runs to completion.
SET NOCOUNT ON will suppress DONE_IN_PROC (row counts) that need to be consumed. Besides row counts, other operations that return results to the client, such as SELECT, PRINT, and RAISERROR, will require getMoreResults() to retrieve the results and ensure the proc runs to completion. If you don't have those in the proc code and no exceptions are raised, SET NOCOUNT ON alone will be enough.
I am working on reducing deadlocks and it was pointed out to me that I should not use multiple queries with one connection because the transaction may not be committed and open, causing deadlocks. So in pseudo code, something like this:
try(Connection con = datasource.getConnection())
{
PreparedStatement stm1 = con.prepareStatement(getSQL());
stm1.setString(1, owner);
stm1.setTimestamp(2, Timestamp.valueOf(LocalDateTime.now()));
stm1.setInt(3, count);
int updateCount = stm1.executeUpdate();
stm1.close();
PreparedStatement stm2 = con.prepareStatement(getSQL2());
stm2.setString(1, owner);
ResultSet rs = stm2.executeQuery();
List<Object> results = new ArrayList<>();
while(rs.next()) {
results.add(create(rs));
}
return results;
} catch (SQLException e) {
throw new RuntimeException("Failed to claim message",e);
}
When does stm1 commit the transaction when auto-commit is set to true?
Is it good practise to reuse a connection like that or should both statements use separate connections instead?
Questions like these can usually be answered by reading the JDBC specification. JDBC 4.2 section 10.1 Transaction Boundaries and Auto-commit says:
When to start a new transaction is a decision made implicitly by
either the JDBC driver or the underlying data source. Although some
data sources implement an explicit “begin transaction” statement,
there is no JDBC API to do so. Typically, a new transaction is started
when the current SQL statement requires one and there is no
transaction already in place. Whether or not a given SQL statement
requires a transaction is also specified by SQL:2003.
The Connection attribute auto-commit specifies when to end
transactions. Enabling auto-commit causes a transaction commit after
each individual SQL statement as soon as that statement is complete.
The point at which a statement is considered to be “complete” depends
on the type of SQL statement as well as what the application does
after executing it:
For Data Manipulation Language (DML) statements such as Insert, Update, Delete, and DDL statements, the statement is complete as soon
as it has finished executing.
For Select statements, the statement is complete when the associated result set is closed.
For CallableStatement objects or for statements that return multiple results, the statement is complete when all of the associated
result sets have been closed, and all update counts and output
parameters have been retrieved.
In your code a transaction is committed as part of stm1.executeUpdate() (this transaction might have been started on prepare, or on execute). A new transaction is started at prepare or execute of stmt2, but as you don't close stmt2 or rs, the connection close will trigger the commit.
As to whether you should reuse connections and statements: it depends on context and your code. For a specific unit of work you use a single connection. If you want further reuse of connections you should use a connection pool. Reusing statements should only be done when it makes sense to do so (otherwise your code might get complicated with resource leaks as a consequence), and again there are connection pools that provide built-in statement pooling that reduces this complexity for you.
Statements like "[..] should not use multiple queries with one connection because the transaction may not be committed and open, causing deadlocks." are usually incorrect and would lead to badly performing applications if applied. It might apply to misbehaving drivers that don't properly follow the auto-commit rules above, or maybe in situations were the connection is much longer lived and you don't properly finish a statement (like in the case of stmt2). That said, it is usually better to disable auto-commit and explicitly commit or rollback when you are done.
Your code could be improved by using try-with-resources for the statements and result sets as well, as this ensures result set and statement are closed as soon as possible, even when exceptions occur:
try (Connection con = datasource.getConnection()) {
try (PreparedStatement stm1 = con.prepareStatement(getSQL())) {
stm1.setString(1, owner);
stm1.setTimestamp(2, Timestamp.valueOf(LocalDateTime.now()));
stm1.setInt(3, count);
int updateCount = stm1.executeUpdate();
}
try (PreparedStatement stm2 = con.prepareStatement(getSQL2())) {
stm2.setString(1, owner);
try (ResultSet rs = stm2.executeQuery()) {
List<Object> results = new ArrayList<>();
while(rs.next()) {
results.add(create(rs));
}
return results;
}
}
} catch (SQLException e) {
throw new RuntimeException("Failed to claim message", e);
}
When auto-commit mode is disabled, no SQL statements are committed until you call the method commit explicitly. All statements executed after the previous call to the method commit are included in the current transaction and committed together as a unit.
As you set auto-commit true, it is committed immediate in data base.
It good practice to reuse a connection.This is perfectly safe as long as the same connection is not is use by two threads at the same time
Follow up question to Closing Database Connections in Java
Connection conn = DriverManager.getConnection(
"jdbc:somejdbcvendor:other data needed by some jdbc vendor",
"myLogin",
"myPassword" );
Statement stmt = conn.createStatement();
try {
stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
} finally {
//It's important to close the statement when you are done with it
stmt.close();
}
conn.close();
I know that conn.close() is necessary but do not know why. Won't the garbage collector free the connection object (and with it release every handler stored in it that points to the database), once the method call is over?
Won't the garbage collector free the connection object (and with it release every handler stored in it that points to the database), once the method call is over?
It doesn't. The JDBC driver retains a reference to to connection, so it is not cleaned up unless you can close().
BTW creating a database connection is very expensive, so you will want to recycle your connections where possible.
You're trusting that you're the only one who has references to the Connection. The driver probably keeps track of them, and it won't let go of a connection that hasn't been closed. That's why you always need to close all more complicated resources (connections, streams, etc.).
We are having two instances of oracle (two physical machines) and one schema for our application.
we are using weblogic application server. application uses datasource for which XA transaction is enabled.
I am having one bean managed EJB, where i do -
update some data in table and then commit
submit oracle job
again update some data in table and then commit
Here i am getting error - java.sql.SQLException: could not use local transaction commit in a global transaction.
strangely this error is not commig every execution, it is comming 1 in 7-8 executions.
Now my questions are
What is significance of bean managed transaction if i use XA enabled transaction ?
why it is not encountering in every execution ?
Thanks.
below is code -
DataObject.updateDataAndReturnCount(" UPDATE EOD_Trn_BatchProcess SET iJobNo = ?, szParameters = ? WHERE iProcessSeqNo = ? ", conn, new String[]{null, strParameters, (String)mapParameters.get("__PROCESS_SEQ_NO")});
conn.commit();
String strStatement = "{? = call submitProcAsJob(?, ?)}";
//String strStatement = "begin ? := submitProcAsJob(?, ?); end;";
CallableStatement pStmt = conn.prepareCall(strStatement);
pStmt.registerOutParameter(1, OracleTypes.NUMBER);
pStmt.setObject(2, strJobName);
pStmt.setObject(3, strInstanceNo);
pStmt.execute();
vString strJobNo = pStmt.getString(1);
vpStmt.close();
DataObject.updateData(" UPDATE EOD_Trn_BatchProcess SET iJobNo = ?, szParameters = ? WHERE iProcessSeqNo = ? ", conn, new String[]{strJobNo, strParameters, (String)mapParameters.get("__PROCESS_SEQ_NO")});
conn.commit();
here first commit is required because i want to save parameters used during call, even if job submission fails.(or any thing ahead.)
The reason for the exception is that you can not mannaully call commit()/rollback under a global transaction,you can only marked it for rollback.You have three options:
Throw an exception,which depends on the ejb-jar.xml/weblogic-ejb-jar.xml,the default is for any RuntimeException the transaction is marked for rollback;
call the EJBContext.setRollbackOnly() method in case of CheckedException or whenever you need;
If none of above happend for all the resouces under the same transaction,it will be commited sooner or later by the transaction manager.
The transaction manager is responsible for commit()/rollback() the transaction for you, so that the it has a chance to co-operate with
different resources(two oralce db for example).You can check the detail by gooble the key word "two phased transaction" or "global transaction",here is what I found:
Global Transaction
As for your question
What is significance of bean managed transaction if i use XA enabled transaction ?
The bean-managed transaction is a "Global transaction" if the transaction-attribute in in ejb-jar.xml enable the transaction propagation. A global transaction need the datasource to be XA enabled,that is the jdbc driver itself is XA kind driver such as oracle.jdbc.xa.client.OracleXADataSource ,or the thin driver oracle.jdbc.OracleDriver with XA enabled (a simulation of two phased transaction,but not real one )
why it is not encountering in every execution ?
I am not sure why,I guess the driver have some mechanism to check whether the ruled is breaked.Or the transaction-attribute is configured to Supports ,so if caller has a transaction context,then your ejb is under global transaction,otherwise not.
I wish my answer to be helpful,good luck!
I've encountered the same problem,and when I set local transaction to auto commit false solved the problem.
Connection.setAutoCommit(false)
I guess you are using a 2-phrase transaction,What if you do not commit in you 1st step?
I am using JTDS to connect to MS-SQL 2005. I am using c3p0 as the DB Connection pool, configured with Spring.
I am randomly getting an SQLException: Invalid state, the ResultSet object is closed in a Groovy script in which I have passed a reference to the connection pool. The script is executed by a timer every so often. By random, I mean that the script works perfectly 99% of the time, but when it fails, it will do so a couple of times, then go back to working correctly again, picking up where it left off. All of the critical work is done in a transaction, pulling off of a Message Queue.
Logic below:
//passed into the groovy context
DataSource source = source;
Connection conn = source.getConnection();
...
//Have to omit proprietary DB stuff... sorry...
PreparedStatement fooStatement = conn.prepareStatement("INSERT INTO foo (x,y,z) VALUES (?,?,?) select SCOPE_IDENTITY();");
ResultSet identRes = fooStatement.executeQuery();
//This is where the execption is thrown.
identRes.next();
...
try{
log.info("Returning SQL connection.");
conn.close();
}catch(Exception ex){}
There is a separate timer thread that runs a similar groovy script, in which we have not seen this issue. That script uses similar calls to get the connection, and close it.
Originally, we thought that the second script may have been grabbing the same connection off the pool, finishing first, then closing the connection. But c3p0's documentation says that calling conn.close() should simply return it to the pool.
Has anyone else seen this, or am I missing something big here?
Thanks.
We solved this... C3P0 was configured to drop connections that were checked out longer than 30 seconds, we did this to prevent dead-lock in the database (we don't control the tuning). One of the transactions was taking horridly long to complete, and C3P0 was dropping the connection, resulting in the ResultSet Closed error. Surprisingly, however, C3P0 was not logging the incident, so we didnt see this in the application's logs.