I am learning Hibernate now and I need help to understand how Sessions work. I have some methods in a class which I have given below.
I see there is a getCurrentSession() in SessionFactory class. So, it seems that only one Session can be "active" inside a SessionFactory. Is this SessionFactory like
a queue of transactions where the transactions are completed in in order ? If yes, then
is it possible to promote a transaction to a higher or lower priority ?
private static SessionFactory factory;
//Get a hibernate session.
public static Session getSession(){
if(factory == null){
Configuration config = HibernateUtil.getConfiguration();
factory = config.buildSessionFactory();
}
Session hibernateSession = factory.getCurrentSession();
return hibernateSession;
}
public static void commitTransaction(){
HibernateUtil.getSession().getTransaction().commit();
}
public static void rollbackTransaction(){
HibernateUtil.getSession().getTransaction().rollback();
}
And some more methods that use getTransaction().
SessionFactory's job is to hide the session creation strategy. For example, in a web application, you probably want the SessionFactory to return create a Session the first time getCurrentSession() is called on a thread, and then return the same Session from that point forward for the duration of the request. (Since you probably want to load customer data from that session, then maybe modify their account in that same session.) Other times, you may want SessionFactory to create a brand new session every time you call getCurrentSession(). So by hiding this decision behind the SessionFactory API, you simply write code that gets the Session from the factory and operates on it.
The Session is what handles transactions. As you probably expect, transactions are started in a Session, and then either complete or rollback. There is really no way to prioritize them since once they are started, you are committed to either rolling it back or committing it.
Related
if I use SessionFactory
private SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
public Session getSession() {
return sessionFactory.openSession();
}
And use it in my DAO class:
public List<Entity> fetchEntities(Date fromDate, Date toDate) {
Criteria criteria = getSession().createCriteria(Entity.class);
criteria.add(Restrictions.between("cts", fromDate, toDate));
criteria.addOrder(Order.desc("cts"));
return (List<Entity>) criteria.list();
}
Need I close session or not? How curectly do it?
public List<Entity> fetchEntities(Date fromDate, Date toDate) {
Criteria criteria = getSession().createCriteria(Entity.class);
criteria.add(Restrictions.between("cts", fromDate, toDate));
criteria.addOrder(Order.desc("cts"));
return (List<Entity>) criteria.list();
}
Need I close session or not? How correctly do it?
If you create a session instance and close it for each executed query, it may work but it may also have side effects such as a connection pool bottleneck or a overuse of memory.
close() releases the JDBC connection and performs some cleaning.
The Connection close() method of the org.hibernate.Interface Session class states :
End the session by releasing the JDBC connection and cleaning up.
So you should not open and close the session for each executed query.
You should rather close the connection when all queries associated to a client request/processing (user, batch ...) were executed.
Of course if the client request consists of a single query, it makes sense to close it after the query execution.
Actually you use SessionFactory.openSession() that creates a new session that is not bound to the Hibernate persistence context.
It means that you have to explicitly close the session as you finish working with it otherwise it will never be closed.
Supposing that the method is the single one executed in the client request, you could write something like :
public List<Entity> fetchEntities(Date fromDate, Date toDate) {
Session session;
try{
session = getSession();
Criteria criteria = session.createCriteria(Entity.class);
criteria.add(Restrictions.between("cts", fromDate, toDate));
criteria.addOrder(Order.desc("cts"));
return (List<Entity>) criteria.list();
}
finally {
if (session != null) {
session.close();
}
}
Note that if you used SessionFactory.getCurrentSession(), you would not need to close it explicitly as it obtains the current session from the persistence context that creates the session as the transaction is started and close it as the transaction is finished.
The scope of the hibernate session should be the current unit of work (i.e. the transaction) (keep it mind that session is not thread safe).
The factory should be kept for the whole app lifecycle.
See also Struggling to understand EntityManager proper use
No. Actually your web franework ( like spring ) or declarative transaction anagement will do this for you. But there are some cases when you would like to manage sessions yourself. Like batch processing - as hibernate session caches are growing constantly
This question already has answers here:
Hibernate Performance Best Practice?
(3 answers)
Closed 7 years ago.
I am facing some performace issue in my application. Especially while saving to and retrieving from the database.
My application is a standalone application. Not a webapplication. I am using hibernate for database communication.
This application has not gone in production. This application will be used 50 people simultaneously.
Here is the below code which I have used for getting session. I suspect below is the code for getting slowness.
public class HibernateUtil {
public Session getSession() {
SessionFactory buildSessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
Session session = buildSessionFactory.openSession();
return session;
}
public Transaction getTransaction() {
return getSession().getTransaction();
}
}
I am not sure how to write a good code for opening session.
Can anyone suggest how to refactor this code to a good code?
Also, are the session getting closed, means once the thread has completed a process, are we closing the session.
There is no single statement answer for your question unless the code is observed, there are lot of attributes on which the performance of the system depends.
1.) GC - When and which algorithm being used.
2.) How many session are getting opened, are there any sessions which are left idle. Long running sessions should be avoided.
3.) Introducing Caching mechanism in the code, Hibernate 2nd Level cache, query caching
4.) How big the objects are getting retrieved from the database.
5.) How many queries are getting fired into DB.
Please refer this, this
I faced the similar type of issue in past, the base culprit for this is your are creating the factory instance every time when you are trying to get a transaction.
Please use the singleton pattern to create the SessionFactory object, means just create Session factory once and get all the session instance from that factory object, I am pretty sure you will not face the performance issue after this change
change Something like below-
static SessionFactory buildSessionFactory;
public static getSessionFactory() {
if(buildSessionFactory==null) {
buildSessionFactory = new
AnnotationConfiguration().configure().buildSessionFactory();
}
}
public Session getSession() {
getSessionFactory();
Session session = buildSessionFactory.openSession();
return session;
}
public Transaction getTransaction() {
return getSession().getTransaction();
}
It will solve your problem
I am using annotation driven transaction management with Spring JDBC.
I would like to have Spring throw an exception when by mistake I forgot to annotate with #Transactional a service method that inserts/updates/deletes.
By default data can be inserted/updated/deleted even not within a transaction.
You can use Propagation.MANDATORY in your DAO layer.
Propagation.MANDATORY will not start a transaction. It will check whether perticular method is attached to a transaction or not, if not container will throw an exception.
According to the documentation (Spring docs) it's just metadata to give an indication that the method or interface can be configured by something that is 'transactionally aware' (ie
With just tx:annotation-driven and no #Transactional attributes I believe you get the "default" transactionality applied:
Propagation setting is REQUIRED.
Isolation level is DEFAULT.
Transaction is read/write.
Transaction timeout defaults to the default timeout of the underlying transaction system, or none if timeouts are not supported.
ny RuntimeException triggers rollback, and any checked Exception does not.
Assuming you're using the tx:annotation to drive it via a transaction manager then missing out the #Transactional attribute means you can't apply such properties as readOnly, isolation, propagation,rollbackFor, noRollbackFor etc.
I believe that MVC is slightly different - the hibernate session is tied directly to the MVC request - ie when the request is received the transaction starts.
Back to your example, the code for getSession() in HibernateDAOSupport is as follows:
protected final Session getSession()
throws DataAccessResourceFailureException, IllegalStateException
{
return getSession(this.hibernateTemplate.isAllowCreate());
}
Which in turn calls to:
/**
* Obtain a Hibernate Session, either from the current transaction or
* a new one. The latter is only allowed if "allowCreate" is true.
*.......
protected final Session getSession()
throws DataAccessResourceFailureException, IllegalStateException {
return getSession(this.hibernateTemplate.isAllowCreate());
}
which ultimately calls to :
/**
* ....
* #param allowCreate whether a non-transactional Session should be created
* when no transactional Session can be found for the current thread
* ....
*/
private static Session doGetSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
Fundamentally, a Transaction:Session is tied 1:1 afaik, and the only way to run without a transaction is by using say JBoss which has a 'baked in' persistence layer which provides the transactionality for you (under the covers). Even if you call getQuery() after getSession() you still effectively have a transaction occuring as it's a JDBC/Hibernate connection.
I am referring to https://developer.jboss.org/wiki/SessionsAndTransactions and currently trying to understand demarcation with JTA. It states that within a particular transaction using getCurrentSession() always gives the same current session. Does it mean:
If another user is executing the same piece of code (which fetches a transaction by lookup, then uses getCurrentSession() and then closes the transaction) in another thread - that user will have his own transaction and his own current session i.e. the current sessions of 2 users are same for themselves but different from each other?
If 1 is true and based on the code shown in the link for JTA demarcation - how does the system (read Hibernate) understand which session to respond to which user when getCurrentSession() is used? After all we don't pass the transaction as argument to getCurrentSession().
Any pointers/help is much appreciated.
Thanks
According to javadoc SessionFactory.getCurrentSession()
The definition of what exactly "current" means controlled by the {#link org.hibernate.context.spi.CurrentSessionContext} impl configured
for use.
JTA is configured this will default to the {#link org.hibernate.context.internal.JTASessionContext} impl.
Then you can see JTASessionContext javadoc and implementation.
If a session is not already associated with the current JTA transaction at the time {#link #currentSession()} is called, a new session will be opened and it will be associated with that JTA transaction.
public Session currentSession() throws HibernateException {
...
final TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager();
...
txn = transactionManager.getTransaction();
...
final Object txnIdentifier = jtaPlatform.getTransactionIdentifier( txn );
...
Session currentSession = currentSessionMap.get( txnIdentifier );
...
}
TransactionManager javadoc
Internally, the transaction manager associates transactions with threads,
and the methods here operate on the transaction associated with the
calling thread.
So, it's similar (but more clearly) to
Sessions and Transactions/Transaction demarcation with plain JDBC:
In other words, the session is bound to the thread behind the scenes, but scoped to a transaction, just like in a JTA environment.
In spring the HibernateTransactionManager uses the SessionFactory it was initialised with to "bind" a Session to the current thread context when creating a new transaction. Then when HibernateTemplate is used it find that bound Session and uses it.
However I found today that HTM also binds its transaction to the underlying DataSource as well as the SessionFactory (if possible). This allows code to use JdbcTemplate within the transaction scope and, provided the DataSource used by JdbcTemplate is the same as the SessionFactory uses, the Jdbc operations will participate in the transaction (using the same underlying Connection).
This bit me quite badly today when I had some code in my hibernate id allocator that was creating a DataSourceTransactionManager and JdbcTemplate to allocate ids out of a high-lo table. I was intending that this be a standalone transaction that would fetch the next high number and then commit the change to the id table. However because of the above behaviour it was actually participating in my "outer" hibernate transaction AND even worse committing it early. Suffice to say not good.
I tried playing around with transaction propogation settings (used REQUIRES_NEW) but this didn't help.
Does anyone know the best way to use JdbcTemplate within a hibernate transaction and NOT have them share a transaction, even tho they share the same DataSource?
EDIT:
I have a SessionFactory (S) which is created by the spring LocalSessionFactoryBean using a DataSource (D). The HibernateTransactionManager is created with that SessionFactory (S).
some business logic code would look like this..
hibernateTransactionOperations.execute( new TransactionCallbackWithoutResult()
{
#Override
protected void doInTransactionWithoutResult( TransactionStatus status )
{
// some transactional code here using a HibernateTemplate
// will include calls to id allocation when doing hibernateTemplate.save(obj)
}
} );
my id allocation does this (paraphrased), the DataSource below is the same (D) as the one used in the SessionFactory (S).
PlatformTransactionManager txManager = new DataSourceTransactionManager( dataSource );
TransactionOperations transactionOperations = new TransactionTemplate( txManager );
return transactionOperations.execute( new TransactionCallback<Long>()
{
public Long doInTransaction( TransactionStatus status )
{
return allocateBatchTxn( idKey, batchSize );
}
} );
When the transactionOperations execute above completes it will commit the underlying transaction which seems to be the same as the 'outer' hibernate transaction. I have confirmed this by checking locks/transactions in the DB.
Don't create a new DataSourceTransactionManager in your id allocation code. Instead use REQUIRES_NEW and the HibernateTransactionManager.
In allocateBatchTxn(), the safest way to get the JDBC connection is via Spring's DataSourceUtils.getConnection() method.
I tried it with REQUIRES_NEW - it works as expected (on HSQLDB), perhaps it's DB-dependent:
// txManager is a HibernateTransactionManager obtained from the application context
TransactionOperations transactionOperations = new TransactionTemplate( txManager );
transactionOperations.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
return transactionOperations.execute(new TransactionCallback<Long>() {
public Long doInTransaction( TransactionStatus status ) {
return allocateBatchTxn( idKey, batchSize );
}
});
Answering my own question.
The root cause of my problem is a couple of things in HibernateTransactionManager.
The setting 'autodetectDataSource' which defaults to true
In afterPropertiesSet() with the above true it auto-detects the DataSource from the SessionFactory
In doBegin() if the DataSource is not null it will bind new transactions to the SessionFactory AND the DataSource
This is causing my problem because also I have a new DataSourceTransactionManager it still uses the same underlying storage (TransactionSynchronizationManager) to manage transactions and because both use the DataSource you get this leaking of transactions between txn managers. I might argue that a txn manager should include its own 'key/id' in the key for the transactional resources so there are independent but it doesn't appear to do that.
The response above are sensible. Using the hibernate txn manager rather than creating a new DataSourceTransactionManager and then using REQURES_NEW would solve the problem. However in my case that would introduce a circular dependency between HTM -> SessionFactory -> IdAllocator -> HTM.
I came up a solution that works but isn't the most elegant thing ever.
When constructor the id allocator it is passed a DataSource in the constructor. I simply wrap that DataSource in a delegating wrapper that is 100% pass through. This changes the DataSource reference so the txn logic does not think there is a transaction in progress and works as I want it to.