Disable oracle autocommit on close connection - java

I have a question about Oracle autocommit on close connection event.
Information from oracle docs(http://docs.oracle.com/cd/E16655_01/java.121/e17657.pdf):
If the auto-commit mode is disabled and you close the connection without explicitly committing or rolling back your last changes, then an implicit COMMIT operation is run.
So, I want to disable this feature programmatically in JDBC driver. I don't wan't do autocommit on connection close at least one transaction. Is it possible?
P.S. setAutoCommit change JDBC action. "Auto_Commit" start new transaction and call commit on every statement. It is not solution for my problem.

EDIT. Possible duplicate : Does Java Connection.close rollback?
According to the javadoc, you should try to either commit or roll back before calling the close method. The results otherwise are implementation-defined.

You can have a look at the setAutoCommit() method of the Connection class. More details here

Related

How setting Auto Commit off, helps to start a transaction in JDBC?

Many articles and documents says that by setting Auto Commit off, You can start a transaction in JDBC. This Topic also ask same question but It doesn't answer to the question and just said:
Changing the auto-commit mode triggers a commit of the current transaction (if one is active).
Ok. but next?
for finding an answer, I searched and found this:
Autocommit transactions:
Each individual statement is a transaction.
Explicit transactions:
Each transaction is explicitly started with the
BEGIN TRANSACTION statement and explicitly ended with a COMMIT or
ROLLBACK statement.
Implicit transactions:
A new transaction is implicitly started when the prior transaction completes, but each transaction is explicitly
completed with a COMMIT or ROLLBACK statement.
And then I found this:
Committing Transactions
After the 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.
Therefore I conclude that after the auto-commit mode is disabled, we are in Implicit mode and we also know that for disabling auto-commit, a COMMIT statement has been run so after setting the Auto-commit off, we've started a new transaction.
Can we draw such a conclusion based on these cases? is it a right conclusion?
No, you cannot. In JDBC, auto-commit only governs when a transaction is to end. A driver is expected to start a transaction when it is needed. Specifically the JDBC 4.3 specification says in section 10.1 Transaction Boundaries and Auto-commit:
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 other words, when you call connection.setAutoCommit(false) no transaction will be started. Only when a statement is executed (or another operation that requires a transaction), a transaction will be started if there is no active transaction.

Why is "hibernate.connection.autocommit = true" not recommended in Hibernate?

In Hibernate API, there is a property hibernate.connection.autocommit which can be set to true.
But in the API, they have mentioned that it is not recommended to set it like so:
Enables autocommit for JDBC pooled connections (it is not
recommended).
Why is it not recommended ?
What are the ill-effects of setting this property to true ?
All database statements are executed within the context of a physical transaction, even when we don’t explicitly declare transaction boundaries (BEGIN/COMMIT/ROLLBACK).
If you don't declare the transaction boundaries, then each statement will have to be executed in a separate transaction. This may even lead to opening and closing one connection per statement.
Declaring a service as #Transactional will give you one connection for the whole transaction duration, and all statements will use that single isolation connection. This is way better than not using explicit transactions in the first place. On large applications you may have many concurrent requests and reducing the database connection acquiring request rate is definitely improving your overall application performance.
So the rule of thumb is:
If you have read-only transactions that only execute one query, you can enable auto-commit for those.
If you have transactions containing more than one statement, you need to disable auto-commit, since you want all operations to execute in a single unit-of-work and you don't want to put extra pressure on your connection pool.
By default the autocommit value is false, therefore the transaction needs to be commited explicitly. This might be the reason why the changes not getting reflected in database, else can try flush to force the changes before commit.
When you close the session, then it will get commited in database implicitly [depends on the implementation].
When you have cascading transactions & needs to rollback for atomicity, you need to have control over transactions & in that case, autocommit should be false.
Either set autocommit as true or handle transactions explicitly.
Here is a good explanation on it.
Hibernate forum related to this.
Stackoverflow question on it.
My understanding is that if Hibernate autocommits, then a flush that fails part way through won't be rolled back. You'll have an incomplete/broken object graph.
If you want a connection with autocommit on for something, you can always unwrap a newly created Session to get the underlying JDBC Connection, setAutocommit(true) on it, do your work via JDBC APIs, setAutocommit(false), and close the session. I would not recommend doing this on a Session that's already done anything.
Do not use the session-per-operation antipattern: do not open and close a Session for every simple database call in a single thread. The same is true for database transactions. Database calls in an application are made using a planned sequence; they are grouped into atomic units of work. This also means that auto-commit after every single SQL statement is useless in an application as this mode is intended for ad-hoc SQL console work. Hibernate disables, or expects the application server to disable, auto-commit mode immediately. Database transactions are never optional. All communication with a database has to occur inside a transaction. Auto-commit behavior for reading data should be avoided, as many small transactions are unlikely to perform better than one clearly defined unit of work. The latter is also more maintainable and extensible.
find more information on this topic

mysql jdbc unsuccessful rollback

I've got some Java code uses JDBC to connect to a MySQL database, then the code does some read operations then a single update, all using the same connection. If there is an exception, then connection.rollback() is called; if there's no exception, connection.commit() is called. At this stage, the connection is newly created each time I run my test (i.e. it does not come from a pool). My code only ever creates one connection, and it is used throughout the test.
The connection being used has connection.setAutoCommit(false) called immediately after the connection instance is created.
For some reason, when there is an exception and connection.rollback() is called, it turns out that my update has been committed rather than rolled back.
Via debugging, I have confirmed the following,
After calling connection.setAutoCommit(false), connection.getAutoCommit() returns a value of false, as expected. Also, "Select ##session.autocommit" returns a value of 0, indicating that auto commit is off, as expected.
Immediately prior to calling connection.rollback(), the same checks show that auto commit is off, as expected.
connection.commit() is definitely not being called and connection.rollback() is definitely being called.
I have also tried explicitly running the statement "rollback;" but it does not solve my issue. I have also tried explicitly running the statement "Set AUTOCOMMIT = 0;" after creating the connection.
All of my tables use a storage engine of InnoDB. Via SQL Workbench, with auto commit off, rollbacks and commits work as expected.
I am using MySQL version '5.0.91-community-nt'. The MySQL jdbc driver version is 5.1.19. I'm using Java 5.
Has anyone got any suggestions as to why my update is getting committed even though auto-commit is off, commit is never called, and rollback is explicitly called?
Cheers.
The code I refer to in the OP is not in a single block, it is dispersed across classes. To satisfy the queries above for examples of the code, I prepared a single block of code to illistrate the issue. Once the block of code was completed, I tested it to ensure I could replicate the issue. Well, I couldn't replicate the issue - the rollback worked just fine. This caused me to go through the production code to work out all the things it was doing beyond the block of code I had put together.
It turns out that the production code both creates and drops temporary tables. Most relevant, it drops the temporary tables after performing the update, and it drops the temporary tables whether or not there is an exception. It turns out that dropping a table in MySQL issues an implicit commit call. Hence, in between my code throwing an exception and my code calling connection.rollback(), it drops tables which causes an implicit call to commit. Hence my 'auto commit' issue.

How to start a transaction in JDBC?

Connection.setTransactionIsolation(int) warns:
Note: If this method is called during a transaction, the result is implementation-defined.
This bring up the question: how do you begin a transaction in JDBC? It's clear how to end a transaction, but not how to begin it.
If a Connection starts inside in a transaction, how are we supposed to invoke Connection.setTransactionIsolation(int) outside of a transaction to avoid implementation-specific behavior?
Answering my own question:
JDBC connections start out with auto-commit mode enabled, where each SQL statement is implicitly demarcated with a transaction.
Users who wish to execute multiple statements per transaction must turn auto-commit off.
Changing the auto-commit mode triggers a commit of the current transaction (if one is active).
Connection.setTransactionIsolation() may be invoked anytime if auto-commit is enabled.
If auto-commit is disabled, Connection.setTransactionIsolation() may only be invoked before or after a transaction. Invoking it in the middle of a transaction leads to undefined behavior.
See JDBC Tutorial by Oracle.
JDBC implicitly demarcates each query/update you perform on the connection with a transaction. You can customize this behavior by calling setAutoCommit(false) to turn off the auto-commit mode and call the commit()/rollback() to indicate the end of a transaction. Pesudo code
try
{
con.setAutoCommit(false);
//1 or more queries or updates
con.commit();
}
catch(Exception e)
{
con.rollback();
}
finally
{
con.close();
}
Now, there is a type in the method you have shown. It should be setTransactionIsolation(int level) and is not the api for transaction demarcation. It manages how/when the changes made by one operation become visible to other concurrent operations, the "I" in ACID (http://en.wikipedia.org/wiki/Isolation_(database_systems))
I suggest you read this you'll see
Therefore, the first call of
setAutoCommit(false) and each call of
commit() implicitly mark the start of
a transaction. Transactions can be
undone before they are committed by
calling
Edit:
Check the official documentation on JDBC Transactions
When a connection is created, it is in auto-commit mode. This means
that each individual SQL statement is treated as a transaction and is
automatically committed right after it is executed. (To be more
precise, the default is for a SQL statement to be committed when it is
completed, not when it is executed. A statement is completed when all
of its result sets and update counts have been retrieved. In almost
all cases, however, a statement is completed, and therefore committed,
right after it is executed.)
The way to allow two or more statements to be grouped into a
transaction is to disable the auto-commit mode. This is demonstrated
in the following code, where con is an active connection:
con.setAutoCommit(false);
Source: JDBC Transactions
Startingly, you can manually run a transaction, if you wish to leave your connection in "setAutoCommit(true)" mode but still want a transaction:
try (Statement statement = conn.createStatement()) {
statement.execute("BEGIN");
try {
// use statement ...
statement.execute("COMMIT");
}
catch (SQLException failure) {
statement.execute("ROLLBACK");
}
}
Actually, this page from the JDBC tutorial would be a better read.
You would get your connection, set your isolation level and then do your updates amd stuff and then either commit or rollback.
You can use these methods for transaction:
you must create the connection object like con
con.setAutoCommit(false);
your queries
if all is true con.commit();
else con.rollback();
Maybe this will answer your question:
You can only have one transaction per connection.
If autocommit is on (default), every select, update, delete will automatically start and commit (or rollback) a transaction.
If you set autocommit off, you start a "new" transaction (means commit or rollback won't happen automatically). After some statements, you can call commit or rollback, which finishes current transaction and automatically starts a new one.
You cannot have two transactions actively open on one JDBC connection on pure JDBC.
Using one connection for multiple transactions (reuse, pooling or chaining) some weird problems can lurk creating problems people have to live by since they usually cant identify the causes.
The following scenarios come to mind:
(Re-)Using a connection with an ongoing / uncommitted transaction
Flawed connection pool implementations
Higher isolation level implementations in some databases (especially the distributed SQL and the NoSQL one)
Point 1 is straight forward and understandable.
Point 2 basically leads to either point 1 or (and) point 3.
Point 3 is all about a system where a new transaction has begun before the first statement is issued. From a database perspective such a transaction might have started long before the 'first' real statement was issued. If the concurrency model is based on the snapshot idea where one reads only states/values that were valid at the point the transaction begins but no change that has changed later on, it is very important that on commit the full read set of the current transaction is also validated.
Since NoSQL and certain isolation levels like MS SQL-Server Snapshot often do not validate the read-set (in the right way), all bets are usually off to what to expect. While this is a problem always being present, it is way worse when one is dealing with transactions that start on the last commit or when the connection was pooled rather than the connection being actually used, it is usually important to make sure the transaction actually starts when it is expected to start. (Also very important if one uses a rollback-only read-only transaction).
I use the following rules when dealing with JDBC in JAVA:
Always rollback a JDBC connection before using it (scraps everyting and starts a new transaction), if the company uses plain JDBC in conjunction with any pooling mechanism
Use Hibernate for Transaction handling even if only using a session managed JDBC connection for plain SQL. Never had a problem with transactions till now.
Use BEGIN / COMMIT / ROLLBACK as SQL-Statements (like already mentioned). Most implementations will fail if you issue a BEGIN statement during an active transaction (test it for your database and remember the test database is not the production database and JDBC Driver and JDBC Server-side implementations can differ in behaviro than running a SQL console on the actual server).
Use 3 inside one's own wrapper for a JDBC connection instances. This way transaction handling is always correct (if no reflection is used and the connection pooling is not flawed).
3+4 I only use if response time is critical or if Hibernate is not available.
4 allows for using some more advanced performance (response time) improvement patterns for special cases

Will Hibernate flush my updated persistent object when calling session.close() with FlushMode.AUTO?

If FlushMode.AUTO is set, will Hibernate flush my updated persistent object when I call session.close()?
I know that session.close() does not normally flush the session but I'm not sure how FlushMode.AUTO affects this.
From the Docs:
FlushMode.AUTO
The Session is sometimes flushed before query execution in order to ensure that queries never return stale state. This is the default flush mode.
Does this mean I can rely on Hibernate to verify my changes are flushed sometimes before my session is closed?
Small code example:
Session session = HibernateSessionFactory.getSession();
PersistedObject p = session.get(PersistedObject.class,id);
p.setSomeProperty(newValue);
session.close();
UPDATE
According to the docs these are the places where the session will flush (when AUTO is used)
before some query executions
from org.hibernate.Transaction.commit()
from Session.flush()
This does not say anything about Session.close()
Will Hibernate flush my updated persistent object when calling session.close() (using FlushMode.AUTO)?
No it won't, and you should use a transaction with well defined boundaries. Quoting Non-transactional data access and the auto-commit mode:
Working nontransactionally with Hibernate
Look at the following code, which
accesses the database without
transaction boundaries:
Session session = sessionFactory.openSession();
session.get(Item.class, 123l);
session.close();
By default, in a Java SE environment
with a JDBC configuration, this is
what happens if you execute this
snippet:
A new Session is opened. It doesn’t obtain a database connection at this
point.
The call to get() triggers an SQL SELECT. The Session now obtains a JDBC
Connection from the connection pool.
Hibernate, by default, immediately
turns off the autocommit mode on this
connection with setAutoCommit(false).
This effectively starts a JDBC
transaction!
The SELECT is executed inside this JDBC transaction. The Session is
closed, and the connection is returned
to the pool and released by Hibernate
— Hibernate calls close() on the JDBC
Connection. What happens to the
uncommitted transaction?
The answer to that question is, “It
depends!” The JDBC specification
doesn’t say anything about pending
transactions when close() is called on
a connection. What happens depends on
how the vendors implement the
specification. With Oracle JDBC
drivers, for example, the call to
close() commits the transaction! Most
other JDBC vendors take the sane route
and roll back any pending transaction
when the JDBC Connection object is
closed and the resource is returned to
the pool.
Obviously, this won’t be a problem for
the SELECT you’ve executed, but look
at this variation:
Session session = getSessionFactory().openSession();
Long generatedId = session.save(item);
session.close();
This code results in an INSERT
statement, executed inside a
transaction that is never committed or
rolled back. On Oracle, this piece of
code inserts data permanently; in
other databases, it may not. (This
situation is slightly more
complicated: The INSERT is executed
only if the identifier generator
requires it. For example, an
identifier value can be obtained from
a sequence without an INSERT. The
persistent entity is then queued until
flush-time insertion — which never
happens in this code. An identity
strategy requires an immediate INSERT
for the value to be generated.)
Bottom line: use explicit transaction demarcation.
closing a session will always flush al work to the database. Flushmode.AUTO flushes work to the database when there are changes and you are quering the table with changed records.

Categories

Resources