I have a requirement to use 2 different databases within single DAO class. One of the databases is read/write while the other is read only.
I have created 2 data sources, 2 session factories and 2 transaction managers (transaction manager for the read/write database is the platform transaction manager) for these databases. I am using #Transactional on the service method to configure Spring for transaction management.
We are getting random Session is closed! exceptions when we call sessionFactory.getCurrentSession() in the DAO class ( I can not always produce it, it sometimes works ok, sometimes gets error) :
org.hibernate.SessionException: Session is closed!
at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:133)
at org.hibernate.internal.SessionImpl.setFlushMode(SessionImpl.java:1435)
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:99)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
I don't have a requirement to use global transaction (XA), I just want to query 2 different databases.
I have read this thread, it suggests injecting two separate session factories in the DAO layer as we do now: Session factories to handle multiple DB connections
Also AbstractRoutingDataSource does not work for single Dao class as per this answer: https://stackoverflow.com/a/7379048/572380
Example code from my dao looks like this:
Criteria criteria = sessionFactory1.getCurrentSession().createCriteria(MyClass.class);
criteria.add(Restrictions.eq("id", id));
criteria.list();
criteria = sessionFactory2.getCurrentSession().createCriteria(MyClass2.class); // generates random "Session is closed!" error.
criteria.add(Restrictions.eq("id", id));
criteria.list();
I have also tried using "doInHibernate" method. But the session passed to it is also randomly throwing "Session is closed!" exceptions:
#Autowired
protected HibernateTemplate hibernateTemplate;
#SuppressWarnings("unchecked")
protected List<Map<String, Object>> executeStaticQuery(final String sql) {
HibernateCallback<List<Map<String, Object>>> hibernateCallback = new HibernateCallback<List<Map<String, Object>>>() {
#Override
public List<Map<String, Object>> doInHibernate(Session session) throws HibernateException {
SQLQuery query = session.createSQLQuery(sql);
query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
return query.list();
}
};
return hibernateTemplate.execute(hibernateCallback);
}
So you do have the below code in your application? If you don't you should add it,might be it is causing the problem.
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven/>
Remove this property as mentioned below
<property name="current_session_context_class">thread</property>
You are overriding Spring which sets this to SpringSessionContext.class. This is almost certainly at least part of your problem.
Spring manages your session objects. These session objects that it manages are tied to Spring transactions. So the fact that you are getting that error means to me that it is most likely due to how you are handling transactions.
in other words don't do this
Transaction tx = session.beginTransaction();
unless you want to manage the life cycle of the session yourself in which case you need to call session.open() and session.close()
Instead use the framework to handle transactions. I would take advantage of spring aspects and the declarative approach using #Transactional like I described earlier its both cleaner and more simple, but if you want to do it pragmatically you can do that with Spring as well. Follow the example outlined in the reference manual. See the below link:
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/orm.html#orm-hibernate-tx-programmatic
Above error suggest, you are not able to get the session as session is closed sometimes. You can use openSession() method instead of getCurrentSession() method.
Session session = this.getSessionFactory().openSession();
session.beginTransaction();
// Your Code Here.
session.close();
Drawback with this approach is you will explicitly need to close the session.
In single threaded environment it is slower than getCurrentSession().
Check this Link Also:- Hibernate Session is closed
The problem is that you have a single hibernate session and two data stores. The session is bound to the transaction. If you open a new transaction towards the other database this will effectively open a new session for this database and this entity manager.
this is equivalent to #Transactional(propagation = Propagation.REQUIRES_NEW)
You need to ensure that there are two different transactions/sessions bound to each of the persistent operations towards the two databases.
If all configurations are correct, then every thing should work fine without error
I think you missed #Qualifier(value="sessionFactory1") and #Qualifier(value="sessionFactory2") at your DAO
kindly look at those examples
Hibernate configuring multiple datasources and multiple session factories
https://medium.com/#joeclever/using-multiple-datasources-with-spring-boot-and-spring-data-6430b00c02e7
HibernateTemplate usage is discouraged already. The clear explanation is given here https://stackoverflow.com/a/18002931/1840818
As stated over there, declarative transaction management has to be used.
Related
So,
The hibernate SessionFactory is available.
Now, on java.sql.Connection there is a method connection.setTransactionIsolation.
Now, from sessionFactory you can openSession(), or getCurrentSession()
From there, we can do:
session.doWork(connection -> { ... connection.setTransactionIsolation ... } )
However, my memory tells me this is already too late.
One actually needs to do connection.setTransactionIsolation likely before the Session has even been created.
Is this not true?
Further, setReadOnly has the same requirements.
Now, to get the connection before the session is created there is this answer:
https://stackoverflow.com/a/29844998/961018
But that requires the datasource.
So is there anyway one, can get the datasource from SessionFactory, so I can create this logic from what I currently have?
EDIT
RESPECTFULLY, DO NOT UTTER THE WORDS S.P.R.I.N.G or A.N.N.O.T.A.T.I.O.N.S.
The "Transaction Isolation" property has got a "Connection" level, that's why you have to apply it once creating the connection and not at transaction level or (like general rule for all connection) on SessionFactory.
Considering what you are trying to do, you have actually two different options to set the transaction isolation.
The most used, clean and recommended one is by setting the specific hibernate property, like following one:
<property name="hibernate.connection.isolation">2</property>
On the specific case, value 2 correspond "READ COMMITTED".
Otherwise, you could try to get the connection instance from the Session, like following code:
Session session = sessionFactory.getSession();
try {
session.connection().setTransactionIsolation(2);
} catch (HibernateException | SQLException e) {
e.printStackTrace();
}
Please bear in mind that the above is quite a dirty way to do that and it's not guarantee that it works. Moreover the method connection() of org.hibernate.Session is deprecated, so you should not use it.
However, I personally used for some unit tests (as an experiment) and it worked fine.
I have a spring managed web app where we're using Hibernate. In my hibernate.cfg.xml we have
<property name="hibernate.current_session_context_class">thread</property>
It's also configured to use HikariCP connection pooling.
When we open sessions, we follow the paradigm of this. Note that I'm not explicitly closing the session.
Transaction tx = null;
Session session = sessionFactory.getCurrentSession();
try {
tx = session.beginTransaction();
// do DB stuff.
tx.commit(); // or, sometimes session.getTransaction().commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
throw new CustomDbException(e);
}
In some cases, when closing the session, we do session.getTransaction().commit() at a minimum, this is incosistent, but I'm not sure if it's giving a different trasancation or otherwise causing some problems.
We've been seeing intermittent issues with LockAcquisitionException: could not execute statement, StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect), and TransactionException: nested transactions not supported. We're not trying to do nested exceptiosn and always clean up the transaction, but I'm starting to think that maybe the sessions is being shared.
The application is multi threaded, and I'm starting to suspect that getCurrentSession is actually being shread across threads. I was under the impression that by setting hibernate.current_session_context_class to thread, then getCurrentSession() would provide a separate session per thread. Should I be taking a different approach here? Should I be using openSession() instead, or somehow configuring hibernate to give me unique sessions per thread?
We use #JmsListener methods, and the transactional exceptions seem most prevalent there, but are not localized there.
Exact hibernate versions are as follows:
hibernate: [ 'org.hibernate.common:hibernate-commons-annotations:4.0.4.Final',
'org.hibernate:hibernate-core:4.3.11.Final',
'org.hibernate:hibernate-hikaricp:4.3.11.Final'],
hikari: 'com.zaxxer:HikariCP:2.4.1',
I had a web software running in a Jboss AS 7 container witch saves our data in a PostgreSQL 9.1 database via JPA, an its configuration delegated to JTA.
Last year it was adapted to run at AWS EC2 cloud. As the user demand grown our database usage growed too. As expected our database server becomes busy at rush times, an it affected the usage experience from our users.
After some replication researches on PostgreSQL we realise that PGPool2 could be a nice replication solution for our case: it offers Load Balancing for SELECT queries, and Replication for CUD operations ( UPDATE, INSERT and DELETE ) as well.
So far so good, except that it turns the software slow. If fact, as explicited in PGPool2 documentation, SELECT queries will not be load balanced if it was defined in explicit BEGIN/END transaction.
For a query to be load balanced, all the following requirements must be met:
- PostgreSQL version 7.4 or later
- the query must not be in an explicitly declared transaction (i.e. not in a BEGIN ~ END block)
- it's not SELECT nextval or SELECT setval
- it's not SELECT INTO
- it's not SELECT FOR UPDATE nor FOR SHARE
- it starts with "SELECT" or one of COPY TO STDOUT, EXPLAIN, EXPLAIN ANALYZE SELECT...
- ignore_leading_white_space = true will ignore leading white space.
Two questions:
How I could figure out our SELECT queries that was running in explicit transactions?
Does _javax.ejb.TransactionAttributeType.NOT_SUPPORTED_ fix the transaction scopes, granting that my SELECT method will be running as "transaction-free"?
How I could figure out our SELECT queries that was running in explicit transactions?
Turn on pgpool2 logging of SQL and connections:
Put the following statements into pgpool.conf (which you can setup via cp $prefix/etc/pgpool.conf.sample $prefix/etc/pgpool.conf):
log_per_node_statement
log_connections
Alternatively, turn on log tracing of JPA:
This requires a different method depending or your JPA implementation ( How to view the SQL queries issued by JPA? , JPA 2.0 (logging and tracing through) with Glassfish 3.0.1 and NetBeans 6.9.1: ).
This will log SQL, but will not log transaction start/commit/rollback.
Additionally, put your own debug logging code into methods which start & end transactions, so that you can see when transaction start/commit/rollback.
Does _javax.ejb.TransactionAttributeType.NOT_SUPPORTED_ fix the transaction scopes, granting that my SELECT method will be running as "transaction-free"?
If you are using Container Managed Transactions (annotations #TransactionManagement(CONTAINER) and #TransactionAttribute), then NOT_SUPPORTED will temporarily disassocate the JTA transaction from the current thread. Then the method will run with no transaction context.
Your subsequent JPA query will run outside of the JTA transaction - because the JTA transaction is not available for it to use.
If you already use a Transaction-Scoped EntityManager
Within your Stateless Session Bean you have an EntityManager annotated
#PersistenceContext(type=PersistenceContextType.TRANSACTION), or
annotated #PersistenceContext without type attribute (because
TRANSACTION is the default):
then that EM will lose it's persistence context within the NOT_SUPPORTED method because the PC is associated with the current transaction, which is no longer accessible
so you cannot use such an EM in the method (e.g. to run queries or lookup cached objects)
so you must use an additional application-managed EM within the NOT_SUPPORTED method
you must create the app-managed EM from an EntityManagerFactory in a place where no JTA transaction is active (e.g. in the NOT_SUPPORTED method), because the app-managed EM will automatically associate itself with the current thread's JTA transaction during creation
any objects returned from queries by the new app-managed EM will be in a different persistence context from the original EM, so you need great care to cleanly detach such objects from the PC (e.g. appMgdEM.clear() or appMgdEM.close() or appMgdEM.detach(someEntity)) if you are to modify/merge them with the original EM.
If you already use an Extended-Scoped EntityManager
Within your Stateful Session Bean you have an EntityManager annotated #PersistenceContext(type=PersistenceContextType.EXTENDED).
then that EM will still have it's persistence context within the NOT_SUPPORTED method because the PC is associated with the stateful session bean
but the EM is using a connection that is already in the middle of a "live" transaction
so if you want to run queries outside of a transaction, you cannot use such an EM in the method
so again, you must use an additional application-managed EM within the NOT_SUPPORTED method (same points apply as above).
Example
#Stateless
public class DepartmentManagerBean implements DepartmentManager {
#PersistenceUnit(unitName="EmployeeService")
EntityManager txScopedEM;
#PersistenceUnit(unitName="EmployeeService")
EntityManagerFactory emf;
#TranactionAttribute(REQUIRED)
public void modifyDepartment(int deptId) {
Department dept = txScopedEM.find(Department.class, deptId);
dept.setName("New Dept Name");
List<Employee> empList = getEmpList();
for(Employee emp : empList) {
txScopedEM.merge(emp);
dept.addEmployee(emp);
}
dept.setEmployeeCount(empList.size());
}
#TranactionAttribute(NOT_SUPPORTED)
public void getEmpList() {
EntityManager appManagedEM = emf.createEntityManager();
TypedQuery<Employee> empQuery = appManagedEM.createQuery("...", Employee.class);
List<Employee> empList = empQuery.getResultList();
// ...
appManagedEM.clear();
return empList;
}
}
Alternative/Adjusted Approach
The above has some restrictions on how you query and how you use resulting objects. It requires creating an EM "on the fly", if you use stateless session beans, and also requires entityManager.merge() to be called. It may not suit you.
A strong alternative is to redesign your application, so that you run all queries before the transaction starts. Then it should be possible to use a single Extended-Scoped EntityManager. Run the queries in "NOT_SUPPORTED" method 1 (no transaction), using extended-scope EM. Then run modifications in "REQUIRED" method 2 (with transaction), using the same extended-scope EM. A Transaction-Scoped EntityManaged wouldn't work (it would try to be transactional from the very start, and would have no PC in NOT_SUPPORTED methods).
Cheers :)
You may want to consider partitioning in JPA using EclipseLink data partitioning,
http://java-persistence-performance.blogspot.com/2011/05/data-partitioning-scaling-database.html
I am currently using version 3.3 of hibernate.
Currently the setting of hibernate is such that it will autocommit after each persistence of individual object.
I want to wrap a transaction around it, so it will only commit after end of a batch.
The code is in question:
getHibernateTemplate().saveOrUpdateAll(collectionOfObject);
I have consulted the documentation here, but want to see if there is alternative (other than rewriting it to use HSQL)
EDIT
My goal is to have a transaction around a bunch of insert. Currently it is auto-commit per insert
If you want to use transaction management in Spring read here on how to do it.
Also, Use should not be using HibernateTemplate use the Session object instead as below.
sessionFactory = getHibernateTemplate().getSessionFactory();
Session session = sessionFactory.getCurrentSession();
for (Bean bean : listBeans) {
session.saveOrUpdate(bean );
}
As there is no way to save the collection at one shot in session. This will commit the data after method exit.
I'm writing a simple project, a business app written in Swing, using Hibernate for back-end. I come from Spring, that gave me easy ways to use hibernate and transactions. Anyway I managed to have Hibernate working. Yesterday, while writing some code to delete a bean from DB, I got this:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
The deletion code is simply:
Session sess = HibernateUtil.getSession();
Transaction tx = sess.beginTransaction();
try {
tx.begin();
sess.delete(ims);
} catch (Exception e) {
tx.rollback();
throw e;
}
tx.commit();
sess.flush();
and my HibernateUtil.getSession() is:
public static Session getSession() throws HibernateException {
Session sess = null;
try {
sess = sessionFactory.getCurrentSession();
} catch (org.hibernate.HibernateException he) {
sess = sessionFactory.openSession();
}
return sess;
}
additional details: I never close a hibernate session in my code, just on application closing. Is this wrong? Why do I get this on delete (only for that bean, others do work), and I don't on other operations (Insert, query, update)?
I read around and I tried to modify my getSession method simply in a sessionFactory.getCurrentSessionCall(), but I got: org.hibernate.HibernateException: No CurrentSessionContext configured!
Hibernat conf:
<hibernate-configuration>
<session-factory >
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/joptel</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">******</property>
<property name="hibernate.connection.pool_size">1</property>
<property name="show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
..mappings..
</session-factory>
</hibernate-configuration>
I wanted to ask you one thing, why are you trying to use "OpenSession" method?
public static Session getSession() throws HibernateException {
Session sess = null;
try {
sess = sessionFactory.getCurrentSession();
} catch (org.hibernate.HibernateException he) {
sess = sessionFactory.openSession();
}
return sess;
}
You don't have to call openSession(), because getCurrentSession() method is always returns current session (Thread in case if you have configured it to be).
I got it!...
You have to specify current context in your hibernate.cfg.xml file
it should be:
<property name="hibernate.current_session_context_class">thread</property>
No CurrentSessionContext configured
Read the reference guide on Contextual Sessions. You're required to configure some provided or custom strategy for this. In a hibernate.cfg.xml, you'd configure it with
<property name="hibernate.current_session_context_class">...</property>
You'd probably want to use "thread" as the value to get per-thread sessions. When using Spring, it automatically sets this to a SpringSessionContext, allowing Spring to easily integrate Hibernate with its transaction management framework.
I come from Spring, that gave me easy ways to use hibernate and transactions.
If you're familiar with Spring, why aren't you using it to manage Hibernate here? You must already know how simple and foolproof it makes it.
I never close a hibernate session in my code, just on application closing. Is this wrong?
Yes, this is very wrong. Every session not closed is an open database connection, so your app is currently hemorrhaging connections.
Illegal attempt to associate a collection with two open sessions
That means exactly what it says. You tried to do some persistence operation (save(), update(), delete()) on something that was already associated to a different session. That's what will happen when you go randomly opening new sessions whenever, which is what's happening since SessionFactory.getCurrentSession() will always fail when no "current session context" is set. In general, never open a session just because one wasn't already there. You need to have well-defined strategies for opening and closing sessions and never let anything open a session outside of these "strategies". That's a sure path to resource leaks and errors like the one you've encountered.
I faced the same problem when I am working on a portal where I am using spring remoting with hibernate.
This kind of problem arise only if when the called service method contains multiple DAO calls that hit database with hibernate session.
And the solution is set the #Transaction annotation for those methods with multiple DAO calls. (Implies all the DOA calls with in this method should be under one transaction.)