autocommit=true during spring transaction - java

Trying to understand transactions... I'm using Spring's JdbcTemplate and #Transactional annotation in a method. Although the transaction is active during the method execution (as indicated by both the logs and TransactionSynchronizationManager.isActualTransactionActive()), autocommit also seems to be turned on (as shown by jdbcTemplate.getDataSource().getConnection().getAutoCommit())).
I don't understand two things
Isn't the idea of jdbc transactions turning off autocommit and manually commiting at the end of a code block? Isn't spring transaction manager supposed to be doing something like that under the hood?
If autocommit is really on: If one modification to the database succeeds, and a subsequent one fails, how can spring rollback the previous changes (which it does, despite the autocommit value)?

If the doBegin() method of the DataSourceTransactionManager is processed, the flag is handled.
https://docs.spring.io/spring/docs/2.5.x/javadoc-api/org/springframework/jdbc/datasource/DataSourceTransactionManager.html#doBegin(java.lang.Object,%20org.springframework.transaction.TransactionDefinition)
If you take a look at the sources there is:
if (con.getAutoCommit()) {
...
con.setAutoCommit(false);
}

Related

How does default #Transactional work in the low level?

Does a propagation-Required default #Transactional collects all queries and executes them at the end of the method altogether or does it open a db transaction and executes BEGIN, every query as it finds it and when transaction finishes executes COMMIT?
Is this what is referred as Logical vs Physical transactions?
I am wondering that because I am using a #Transactional tests that executes GET endpoint + DELETE endpoont + GET endpoint with READ_UNCOMMITED, behavior manages to work well, but I see no trace of delete queries in the logs, only selects.
I would have expected I see all the queries issued and then a rollback, but I have the feeling that the transaction is just modifying the managed entities of the persistance context and just tries to save by the end of the test...
If I should be seeing all the delete queries as the repository.removes() are executed then it might be that for some reason hibernate is only logging queries out of a readonly=false transaction
Maybe this answer helps you: JPA flush vs commit
If there is an active transaction, JPA/Hibernate will execute the flush method when transaction is committed. Meanwhile, all changes applied to entities are collected in the Unit of Work.
In flush() the changes to the data are reflected in database after encountering flush, but it is still in transaction.flush() MUST be enclosed in a transaction context and you don't have to do it explicitly unless needed (in rare cases), when EntityTransaction.commit() does that for you.
You can change this behavior changing the flush strategy.

Spring #Transactional read-only mode rollback behaviour

I have some service layer method with #Transactional(readOnly=true) and this method causes some RuntimeException quite often (let's say it is some NotFoundException exception).
I'm using ORM Hibernate also for the DB interaction process.
Is it legal pattern to do so?
What is the default behaviour in this case in sense of "roll-back" behaviour? Can it influence somehow badly on connections's state or lead to any problems?
It isn't something like "why not try it by your self?". I have a suspicion that this could lead to the Transaction rolled back because it has been marked as rollback-only error in the same method after some number of exceptions. This could be very specific JDBC PostgreSQL driver error. That is why I'm wondering about this design in general: is it something legal or illegal to do so?
So as far as I understand, you are worried about the roll-back. In this case a readOnly is a select statement and usually there is nothing to roll-back from a read. The only place where this is handy is when you read under a lock and when the transaction finishes you release that lock.
AFAIK readOnly will set the flushmode to FlushMode.NEVER and that is good and bad at the same time. Good, because there will no dirty checking, as described here. Bad because if you call a read/write transaction within a readOnly transaction, the transaction will silently fail to commit because the session is not flushed. This is easily testable btw - and I hope things have not changed since I've tried this.
Then there is the pool of connections. I know that C3P0's default policy is to rollback any uncommitted work. The flag to control this is autoCommitOnClose.
Then there is this link about readOnly and postgres - which I have not worked with and can't really tell my opinion on.
Now to your point about Transaction rolled back because it has been marked as rollback-only. For a readOnly transaction there might be nothing to roll-back as I said before, so this really depends on how you chain your #Transactional methods IMO.

Should autocommit of a datasource be set to false?

please see the comments in spring DataSourceTransactionManager.java, function doBegin:
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
In the project I'm working on, autocommit is not configured. So it is true by default. We are using Spring to manage transactions, and all SQLs are executed within #Transactional annotated functions. So Transactions are acturally manually committed. Everytime a transaction begin, the db connection is set autocommit to false, and after the transaction exit autocommit is set back to true. A typical workflow would be (at JDBC level):
conn = dataSource.getConnection();
conn.setAutoCommit(false);
stmt = conn.createStatement();
stmt.executeQuery(...);
conn.commit()/ conn.rollback();
conn.setAutoCommit(true);
Is setting autocommit back and forth expensive? should we config datasource connection pool autocommit=false for performance reason? to skip the step 2 and step 6.
1) autocommit is totally dependent on the database, what it means is that, each and every statement through the connection will be executed in a separate transaction which is implicitly carried out. Unless and until, you want to use personal coding and avoid those locks being held by multiple statements which could lead to conflicts with other users, there is no need to set the autocommit to false.
2)From performance points of view,
a) if you have a lot of users and there is some conflict that is occuring because of holding of database locks,
then, there may be a need to check into the issues pertaining to it but as a general
rule, autocommit was introduced for simplification of stuff for beginners.
b) there may be instances where you need to rollback .
c) you want to commit the transaction manually based on a specific condition.
EDIT: I see you have edited the question, to answer you simply, autocommit=false will force you to write your own commit/rollback/etc, performance is totally dependent on the database, the number of locks held at a moment in real time!!
No. setting autocommit to false and true again will not increase the toll on the system.
NO, do not config datasource connection pool autocommit=false unless you are doing it for some specific reason and are an experienced person. From performance points of view, as i already decalred, it is dependent on the type of database and real time users accessing the database at an instance, for your project, 99.99 percent you wouldn't be needing to set it to false.
setting autocommit to true will just ensure that commit is called after each and every statement.
I also see that you are getting your connection from a datasource, in such cases, it is best to leave the connection with default settings so that the next time that connection is fetched from the pool, there wouldn't be any trouble with the workflow
Hope this helped!!
In bulk operations you can set it off in your session and set again on after bulk operation completion to gain performance.
SET autocommit=0;
your code here....
SET autocommit=1;
Update:
As #codemania explained very well that even there is option to disable autocommit as per your requirement but you should not do this. Even this is basic need of transaction to either successfully commit a set of instructions or rollback, if you do it disable then how you will achieve it.
This will be useful if you are doing some bulky task like data migration etc, for it you can disable autocommit to gain performance but only in that session.
You should set autocommit to true whenever you're issuing database transactions. A database trasaction is a logical unit of work, which usually consists of multiple database operations (usually multiple updates) and you want either all of them to succeed or all of them to fail. With autocommit=false, your changes will not be made persistant until you call commit() on the connection object, so this approach guarantees that all your updates will either succeed or fail (by calling rollback in case of an exception etc).
When autocommit is set to true (the default), you can, for instance, change one table but then on the second update (whether it be an update/insert or delete), an exception may occur and your second table doesn't get updated, thus leaving your database in an inconsistent state.
In conclusion, autocommit=true is OK when just reading data or when the database data model is simple and accessed by few users (so that concurrent access to the same regions of data is very rare and when some database inconsistencies can even be tolerated)

Is HikariCP autocommit usage the same as regular java connection autocommit usage?

I recently uses HikariCP. Before I use my own simple ConnectionPool to meet our needs. In our software sometimes we need to perform multiple database inserts where each inserts depends on some validation. More or less like the sample from this site: http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#commit_transactions
In my old way, when I was using my own conn pool, I will always set the connection object to setAutoCommit(false) before giving it to the requesting object so the database manager can rollback the data manually when something goes wrong. Like in the sample, if the try catches any exceptions, then it will call the rollback function. When I return the connection, I call the connection.commit() in the connection return and set autocommit back to true in the connection pool manager.
My question: does HikariCP still uses the same procedure for my needs? Meaning, set the autocommit to false (I read the manual, you have autocommit parameters for your config), and then we just manually rollback or commit the transaction then return to the pool? Or is there some automation done where we can just throw an exception and HikariCP will automatically call the rollback upon error or call commit upon connection return if I do not set the config param for Autocommit = false?
Thank you for any info.
Rendra
HikariCP auto-commit behavior is the same as without a pool. If autoCommit=false, you are responsible for commit/rollback in a try-finally. So, yes, you just commit/rollback and then return the connection to the pool.
The truth is that if autoCommit=false, and you run queries without committing, then HikariCP will automatically rollback on return to the pool. However, this is for safety and I discourage you from coding based on this behavior. Doing so will make your code less portable if you ever choose to switch pools.

Does Hibernate's Session.close() automatically rollback uncommitted transactions?

I'm reading the Hibernate documentation at present and I came across the following quote:
If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.
This all makes sense as far as I'm concerned, but it does make me wonder what the effect of closing a session is with a transaction neither committed nor rolled back?
E.g consider the following:
session = getSessionFactory().openSession();
session.beginTransaction();
session.save(carObject);
//session.getTransaction().commit();
session.close();
With commit commented out, and no rollback called here, what is the expected behaviour of session.close()? Does it simply rollback that commit automatically, does it leave a 'hanging' transaction? etc.
(I understand this obviously wouldn't be good practice - I'm just trying to get my head around the underlying concepts a bit more.)
I've done a bit of digging into Hibernate:
Persistence sessions keep their life-cycle somewhat independent from JDBC connections. When you close Hibernate's Session the connection is released. Exact meaning of "releasing connection" depends on how the connection was obtained in the first place:
if the connection was provided manually (e.g. via sessionFactory.openStatelessSession(connection)) you will get your connection with possibly unfinished transaction back when calling session.close()
in other cases calling session.close() will usually end up in calling connection.close()
No automatic session flushing or transaction commit / rollback is made by Hibernate. The same states for the JPA's EntityManager.
So what happens in the end depends on your connection provider / data source. With C3PO any unfinished transaction will be rolled-back when the connection is returned to the pool. On the other hand if you have managed JTA connection then the actual transaction handling might be completely out of scope to your application.

Categories