I am trying to set jpa transaction isolation level to read_committed. I am using hibernate 4.1.6. there was a time we could do
Connection connection = session.connection();
connection.setTransactionIsolation(Connection.READ_COMMITTED);
But now that session.connection is not available any more, i am a bit confused. what is the best way of setting isolation on jpa? I am using seam 2.3.
thanks in advance
apparently this is one way of doing it:
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
}
});
but in seam it will break if you are change the transation isolation level once the transaction started already.
Related
I am working on a Hibernate project.I had used Hibernate 4.3.1 libraries of Netbeans. Then I needed to use Apache Lucene for fulltext searching. To be able to use Lucene I needed to switch to Hibernate 5.x jars.I can define a new Transaction object but wasRollecBack method of Transaction class is not working. I used this method in several places and now I'm stuck. When I look at javadoc of Hibernate 5.0.6 there is nothing like org.hibernate.transaction . There is org.hibernate.engine.transaction but it does not work either.
When I get back to 4.3.1 wasRolledBack is working but this time I cant run project with lucene libraries. I am confused.
wasRolledBack method is not included in the Hibernate 5.0.6 version Transaction interface Here .
4.3.1 version that was taking place in the wasRolledBack method.
Existing methods:
public interface Transaction {
void begin();
void commit();
void rollback();
TransactionStatus getStatus();
void registerSynchronization(Synchronization synchronization) throws HibernateException;
void setTimeout(int seconds);
int getTimeout();
void markRollbackOnly();
}
I did not test, but you can use the getStatus method.
Example:
TransactionStatus transactionStatus = session.getTransaction().getStatus();
if(transactionStatus.equals(TransactionStatus.ROLLED_BACK)){
//action s.a :)
}
EDIT 1:
TransactionStatus Enum Constant and Description:
ACTIVE : The transaction has been begun, but not yet completed.
COMMITTED : The transaction has been competed successfully.
COMMITTING :Status code indicating a transaction that has begun the
second phase of the two-phase commit protocol, but not yet completed
this phase.
FAILED_COMMIT:The transaction attempted to commit, but
failed.
MARKED_ROLLBACK:The transaction has been marked for rollback only.
NOT_ACTIVE:The transaction has not yet been begun
ROLLED_BACK:The transaction has been rolled back.
ROLLING_BACK:Status code indicating a transaction that is in the
process of rolling back.
I'm trying to set a transaction isolation level for the connection associated with a given entity manager (TRANSACTION_SERIALIZABLE etc.).
I have scoured the internet for a solution and found a few. However, none of them seem to work.
When I try to do the following :
EntityManagerFactory emfactory = Persistence.createEntityManagerFactory("BankingPU");
public EntityManager em = emfactory.createEntityManager();
java.sql.Connection connection = (java.sql.Connection) em.getDelegate();
connection.setTransactionIsolation(TRANSACTION_SERIALIZABLE);
I get the following exception:
Exception in thread "main" java.lang.ClassCastException:
org.eclipse.persistence.internal.jpa.EntityManagerImpl cannot be cast to java.sql.Connection
When I do
EntityManagerFactory emfactory = Persistence.createEntityManagerFactory("BankingPU");
public EntityManager em = emfactory.createEntityManager();
java.sql.Connection connection = em.unwrap(java.sql.Connection.class);
The value stored to connection is null.
I ran the following in order to find out more:
Object obj = em.getDelegate();
The class type stored in obj is org.eclipse.persistence.internal.jpa.EntityManagerImpl
Edit: (oops, guess I could have told that from the exception)
Edit2:
I have managed to obtain the session (i think) by calling :
Session session = ((EntityManagerImpl) em).getSession();
However, neither the connection(), nor the DoWork() methods are present in it.
Any idea how to follow up on this and set the transaction isolation level?
JPA API does not have transaction (TX) isolation level management API. So it is not possible do it via entity manager.
If you want to use the same entity manager than you need, as it already mentioned in Neil Stockton comment, your JPA provider specific code to get JDBC access for changing TX isolation level.
Adam Bien almost answered on your question at his blog post.
Isolation Levels can be set on java.sql.Connection level. In a Java EE environment isolation levels can be configured on Data Source / Connection Pool (see e.g. Glassfish).
As workaround, if you want use only JPA API, you may create in your application server the several data sources for the same database with required isolation level.
However you need to understand that you will have the several 1st level caches (in different entity managers) and you should manage your entities state in appropriate way.
I have found the solution:
emfactory = Persistence.createEntityManagerFactory("BankingPU");
em = emfactory.createEntityManager();
Session session = ((EntityManagerImpl) em).getSession();
DatabaseLogin databaseLogin = (DatabaseLogin) session.getDatasourceLogin();
databaseLogin.setTransactionIsolation(DatabaseLogin.TRANSACTION_SERIALIZABLE);
I'm performing a transaction in JPA (EclipseLink) using a custom transaction isolation level, which I set on the underlying connection of the JPA EntityManager using this code:
// begin transaction
entityManager.getTransaction().begin();
// store the old isolation level
int isolationLevelOld = entityManager.unwrap(Connection.class).getTransactionIsolation();
// set the desired isolation level for this transaction
entityManager.unwrap(Connection.class).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
[...Queries...]
// commit transaction
entityManager.getTransaction().commit();
// reset isolation level to the old value (throws NullPointerException)
entityManager.unwrap(Connection.class).setTransactionIsolation(isolationLevelOld);
If I try to reset the isolation level to the old value after having committed the transaction, the underlying connection is null (entityManager.unwrap(Connection.class) returns null). I'm worried, if I just don't reset the isolation level, a connection with a bad isolation level gets leaked back to the pool.
What is the correct way of cleaning up after changing the isolation level? Should I maybe do it before calling commit()?
The java.sql.Connection gets returned to the pool in the call to entityManager.getTransaction().commit(); So resetting the isolation level afterwards is not possible and prevented by EclipseLink by returning a null connection.
Maintaining a reference to the Connection to circumvent this will likely leak a connection with altered settings, so I cannot accept your answer RomanC
I ended up creating two instances of EntityManagerFactory. One that creates default EntityManagers and one that creates EntityManagers with Connections with my desired transaction level using a SessionCustomizer:
public static class SessionCustomizer implements org.eclipse.persistence.config.SessionCustomizer {
#Override
public void customize(Session session) throws Exception {
DatabaseLogin databaseLogin = (DatabaseLogin) session.getDatasourceLogin();
databaseLogin.setTransactionIsolation(DatabaseLogin.TRANSACTION_SERIALIZABLE);
}
}
private void init() {
entityManagerFactoryRegular = Persistence.createEntityManagerFactory("MyPersitenceRegular");
Map<String, String> props = new HashMap<>();
props.put(PersistenceUnitProperties.SESSION_CUSTOMIZER, SessionCustomizer.class.getName());
entityManagerFactoryTransactionSerializable = Persistence.createEntityManagerFactory("MyPersitenceTransactionSerializable", props);
}
See here Set Isolation level in eclipselink
I then use the EntityManagerFactory that provides whichever connection type I need. Caveat: Transactions cannot span EntityManagers from multiple EntityManagerFactories
Try the following code
Connection conn = entityManager.unwrap(Connection.class);
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
[...Queries...]
// commit transaction
entityManager.getTransaction().commit();
conn.setTransactionIsolation(isolationLevelOld);
I suspect this one is embarrassing an I am doing it wrong in a terrible way, but please bear with me.
I have a Spring application with Spring-managed transactions.
It uses EclipseLink JPA.
I have a method which does a findByNativeQuery() followed by a merge(). I need this to happen in a real SERIAL transaction isolation level.
I tried adding
#Transactional(isolation=Isolation.SERIALIZABLE)
This doesn't work because org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect#beginTransaction
does not support any transaction isolation level but the default.
So then I tried getting to UnitOfWork internals of ElcipseLink and starting/comitting my own transactions, but then I get an error:
"java.lang.IllegalStateException : Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
Which of course makes sense... but what do I do??
I've given a try to this, but I am not completely sure of the solution. I've taken the code from this blog and adapted it for EclipseLink. Here's the code:
package com.byteslounge.spring.tx.dialect;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
public class CustomEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
private static final long serialVersionUID = 1L;
private boolean lazyDatabaseTransaction = false;
#Override
public void setLazyDatabaseTransaction(boolean lazyDatabaseTransaction) {
this.lazyDatabaseTransaction = lazyDatabaseTransaction;
}
#Override
public Object beginTransaction(final EntityManager entityManager,
final TransactionDefinition definition)
throws PersistenceException, SQLException, TransactionException {
UnitOfWork uow = (UnitOfWork) getSession(entityManager);
uow.getLogin().setTransactionIsolation(definition.getIsolationLevel());
entityManager.getTransaction().begin();
if (!definition.isReadOnly() && !lazyDatabaseTransaction) {
uow.beginEarlyTransaction();
}
return null;
}
}
I'm seeing the SERIALIZABLE isolation being logged when the transaction is initiated, but this needs to be tested properly to confirm it works.
Support for custom isolation level was added in Spring 4.1.2 to EclipseLinkJpaDialect
You can refer
"To achieve serializable transaction isolation with EclipseLink, we recommend that you use an isolated client session as follows:
Configure the database transaction isolation as serializable.
Configure objects as isolated (see Configuring Cache Isolation at the Project Level or Configuring Cache Isolation at the Descriptor Level).
Use the UnitOfWork method beginTransactionEarly (see Unit of Work Method beginTransactionEarly).
If you are only concerned about the write aspect of serializable, optimistic locking is sufficient."
at http://docs.oracle.com/middleware/1212/toplink/OTLCG/cache.htm or go through
http://docs.oracle.com/middleware/1212/toplink/OTLCG/cache.htm
if any of isolation level meets your requirement
I have a little of confusion about JDBC connection, transactions and their integration in EJB, JTA, Hibernate environment. My doubts are:
when we use #Resource DataSource ds; ... ds.getConnection() , are we working in the same transaction used by the managed bean? Should we close the connection, statement, resultset?
what about session.doWork? Are we in the same transaction? What about closing statement and result set?
aggressive release mode in Hibernate means that connections are closed after each statement. Does it mean that transaction is committed too? (I don't think this is true, but I can't understand how Hibernate works here)
There are a few things you need to figure out. First thing you need to identify what is your unit of work.
The session-per-request pattern is one of the most used and unless you have specific needs stick with that.
If you are using Hibernate you don't use statements and result sets directly. Hibernate will do that for you. what you need to close is the hibernate session
What you use is a SessionFactory and a Session object. The session pretty much represents your unit of work. Inside the hibernate session you get your objects, you change them and save them back.
The session per request pattern opens a session when a request is received and closes it when the response is sent back.
In a container managed EJB session bean a transaction is available and the datasource you (or hibernate) use in such a container is automatically handled by a JTA TransactionManager.
Now because Hibernate is smart it can automatically bind the "current" Session to the current JTA transaction.
This enables an easy implementation of the session-per-request strategy with the getCurrentSession() method on your SessionFactory:
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
So to answer your questions:
If you are using Hibernate with JTA in a container you'd be better off using a JPA EntityManager or maybe spring hibernate template.
Here are some references:
http://community.jboss.org/wiki/sessionsandtransactions#Transaction_demarcation_with_JTA
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/HibernateTemplate.html