I'm using Spring-tx-4.0.9 in a multithreaded application and have a problem with "stalled" transaction after the thread is killed by OutOfMemory.
Each thread ask PlatformTransactionManager (implementation org.springframework.jdbc.datasource.DataSourceTransactionManager) to get an transaction via TransactionTemplate Propagation is REQUIRED. This template has a timeout set. State of a transaction is managed by application and after all, work is done, then thread calls commit. This returns a transaction back to the PlatformTransactionManager, and almost every time releases connection to the DB (in DB, the session for this connection is mostly released)
But if the thread is killed by OutOfMemory, then the underlying transaction is returned to internal transaction pool and another thread can acquire this "broken" transaction. This transaction has the timeout set from the first time and cannot be reset even if new thread call TM to get transaction with new TransactionTemplate. So after this timeout expires, every request on transaction throws TransactionTimedOutException.
Only available methods on PlatformTransactionManager are getTransaction, commit and rollback.
Method getTransaction can return new or previous transaction. From returned instance of TransactionStatus, I'm able to detect new or stalled transaction, but I'm unable to release it.
Method rollback sets flag isRollbackOnly, but the transaction is still not released and getTransaction returns stalled. If I then call commit as recommended by JavaDoc, then nothing changed and the transaction is still hanged.
If I use propagation REQUIRES_NEW, then "broken" transaction is suspended, which solve a problem with TransactionTimedOutException, but an old transaction is still hanged and holds the connection to the DB (unwanted state).
Is there a way, how to proper release a broken transaction (rollback and release connection)?
EDIT- Current code for getting a TransactionalStatus
public TransactionStatus beginTransaction() {
// transaction begin
logger.debug("Create new transaction.");
TransactionTemplate template = new TransactionTemplate(transactionManager);
template.setTimeout(txConnectionTimeout);
TransactionStatus txStatus = transactionManager.getTransaction(template);
logger.debug(
"Transaction created. TransactionStatus: isCompleted=" + txStatus.isCompleted() + ", isNewTransaction=" +
txStatus.isNewTransaction());
return txStatus;
}
Related
The hibernate documentation says:
A org.hibernate.Session begins when the first call to
getCurrentSession() is made for the current thread. It is then bound
by Hibernate to the current thread. When the transaction ends, either
through commit or rollback, Hibernate automatically unbinds the
org.hibernate.Session from the thread and closes it for you. If you
call getCurrentSession() again, you get a new org.hibernate.Session
and can start a new unit of work.
If the property current_session_context_class is set to thread then what exactly hibernate does? What does it mean when they say current thread?
This post says SessionFactory.getCurrentSession() returns a session bound to a context, what it means exactly, can someone please clarify?
In Glassfish, there is a JDBC Pool option called
Non Transactional Connections
So am I correct in thinking that "Non Transactional Connections" is the same as setting auto-commit=false ?
If that is correct, then why, when this option is disabled (i.e. non-transactional enabled) do I get an error saying
org.postgresql.util.PSQLException: Cannot commit when autoCommit is enabled.
This is when I have java code that looks like :
try {
preparedStatement = connection.prepareStatement(.....);
preparedStatement.executeQuery();
connection.commit();
}
Non Transactional Connections does not set the autoCommit property to false by default. That's not what non - transactional connections are for. From the Oracle glassfish documentation below,
The main advantage of using non-transactional connections is that the overhead incurred in enlisting and delisting connections in transaction contexts is avoided. However, use such connections carefully. For example, if a non-transactional connection is used to query the database while a transaction is in progress that modifies the database, the query retrieves the unmodified data in the database. This is because the in-progress transaction hasn’t committed. For another example, if a non-transactional connection modifies the database and a transaction that is running simultaneously rolls back, the changes made by the non-transactional connection are not rolled back.
You should
Connection con = ds.getConnection();
boolean initValue = con.getAutoCommit();
con.setAutoCommit(false);
//do your work here and commit or rollback
con.setAutoCommit(initValue );
From the docs - http://docs.spring.io/spring-framework/docs/2.0.x/api/org/springframework/orm/hibernate/HibernateTransactionManager.html
HibernateTransactionManager - Binds a Hibernate Session from the specified factory to the thread, potentially allowing for one thread-bound Session per factory
OpenSessionInViewFilter - This filter makes Hibernate Sessions available via the current thread, which will be autodetected by transaction managers.
What is the difference between both of them and at what scenarios should they be used ?
OpenSessionInViewFilter
Now when you are using OpenSessionInViewFilter, by default the session's flush mode is set to NEVER. So when you try to save something in your action using hibenate and commit it, it wont be reflected in your database. To solve this you need to flush the session in your action class or extend OpenSessionInViewFilter and override closeSession(Session session, SessionFactory sessionFactory).
Now it is also possible that you are maintaining a single transaction for per request. In your action you edit the attributes of a object and update it using session.update(object). But it is not yet commited as some other processing is remaining. At the same time, some other request is invoking a action which tries to retrieve the object which you were updating. Since the object is not yet commited the other request will get the old object. To solve this you need to begin a transaction before you load object and commit the transaction after you update the object. So that as soon as the object is saved/updated it is commited. With this there can be many transaction in single user request but only one session.
The OpenSessionInView pattern only guarantees that the session is open during one single thread execution.
When the page has been rendered and has been returned to the browser, the session gets closed by the filter.
So subsequent requests (e.g. navigation request) require another new session which will be opened by the OpenSessionInViewFilter. But as the "old" person object is not connected to the "new" session, it is considered as disconnected object which's references cannot be loaded lazily.
I'm creating a hibernate Session and try to start a new [Jta]Transaction. Though, the transaction cannot be started because the JtaTransaction that is used in the background seems to be rolled back.
Here is what I'm doing.
Session session = sessionFactory.openSession();
CustomSessionWrapper dpSession = new CustomSessionWrapper(session, this);
if (!session.isClosed() && !session.getTransaction().isActive()) {
session.beginTransaction();
}
Nevertheless the transaction is still not active after the beginTransaction is called. When I debug the beginTransaction method I come to the doBegin method of the JtaTransaction (I do not override this method, I'm just posting the original code of this method).
#Override
protected void doBegin() {
LOG.debug( "begin" );
userTransaction = locateUserTransaction();
try {
if ( userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION ) {
userTransaction.begin();
isInitiator = true;
LOG.debug( "Began a new JTA transaction" );
}
}
catch ( Exception e ) {
throw new TransactionException( "JTA transaction begin failed", e );
}
}
The userTransaction.getStatus() returns Status.STATUS_ROLLEDBACK and no transaction is started. Does anyone know how I can fix that?
UPDATE 1 (you can skip that since that was a mistake, see UPDATE 2)
I found out that there are two threads, one using the main session and another using smaller sessions for logging. The main session (and transaction) is open for a longer period of time, so basically until the operation is finished. It seems that locateUserTransaction always returns the same userTransaction. This means that the main session opens this userTransaction and one of the side transactions commit/rollback that transaction. Does anyone know what to do so that different transactions are retrieved?
UPDATE 2
I found out that I don't have two threads, it is only one threads that opens two sessions in parallel. Each session should then open their own transaction, though both get the same UserTransaction. How can I tell hibernate that each session should get its own [User]Transaction?
Hibernate abstracts both local as JTA transactions behind its own abstraction layer, so I don't see why you'd have to write such low level transaction handling code.
In Java EE you have the app server to manage transactions, in stand alone apps Bitronix + Spring do a job too.
Although you can manage to write your own transaction management logic, I always advice people to reuse what they have already available. Xa/JTA and Hibernate require extensive knowledge to work seamlessly.
Update 1
Two different threads shouldn't use the same user transaction, you should use different transactions for each thread.
When a Hibernate session is opened (sessionFactory.openSession()) it might be closed. It is ok. In case it is missed to close an opened session which is used to retrieve data (not to save or update or delete) any where in the application, how to close opened sessions if exists?
(Let's say when a JFrame is closed, if there are opened sessions available, they must be closed. Closing sessions can be done by going through the codes one by one, but I mean here, without checking codes, is there any way to close sessions which are missed to close with some piece of code).
Why dont you close the session when your database operation finished?
I mean, In DAO classes you get opened session perform database operation. And in finally block, Close your session.
You can close session like :
finally {
if(session!=null){
session.close();
}
}
OR
You can get the current session using
Session sess = sessionFactory.getCurrentSession();
And close session on closing event of JFrame.
I get following lines from this link
The main contract here is the creation of Session instances. Usually
an application has a single SessionFactory instance and threads
servicing client requests obtain Session instances from this factory.
The internal state of a SessionFactory is immutable. Once it is
created this internal state is set. This internal state includes all
of the metadata about Object/Relational Mapping.
Implementors must be threadsafe.
And it is our duty to close session when finished the operation or transaction. When we close sessionfactory all resources(connection pools etc) are released properly.