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.
Related
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.
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'm using a Connection via severals pools :
DataSource ds = initialContext.lookup("poolname1");
Connection cn = ds.getConnection();
submethod1(cn);
submethod2(cn);
void submethod1(Connection cn)
{
// using connection
// ..
}
My question is: how to log "poolname" in submethods ? or similar informations about DataSource.
Perhaps this will help
getClientInfo()
or
getMetaData()
as mentionned in the Official Java Doc
The poolname in your example is actually a JNDI Name. This is generally a configuration which is configured in resource definition (e.q under in tomcat config).
For your problem as #sourlcheck mentioned, its not possible as connection are not aware of their datasource.
Once way to solve your problem is to give label to the datasource. Most of the Pooled datasource implementations (e.q. C3P0) offer a setter for setting up a name to the datasource. In C3P0 the datasournce class is ComboPooledDataSource and the method is getDataSourceName(). Once you get this name, it remains the same throughout its life cycle. But off course you need to introduce third party library
You can't log "poolname" in methods that receive Connection. Connections don't know if they are pooled or served directly from non-pooling DataSource.
Also you can create and access your pool in many different ways, not all of them guaranteeing existence of "poolname".
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.)