I lately observed a problem in my code, I opened a hibernate session but forgot to close it. Although I fixed it but I can't seem to understand the impact of this mistake I did.
This code was in HTTP service, I need answers for two cases:
The container spawns a new thread when a request comes
It fetches from thread pool
I have read in other posts that session is bound to thread, so what I have inferred from that is if a new thread is spawned in each request and I have left a session open in one of them. It will be then destroyed when request gets completed. I dont know I am right or not.
It depends of what you use to control a session life cycle.
If you use a "session per request pattern" with, for an example, OpenSessionInViewFilter, a session will be closed by a filter. You can use your own filter to control a session as well.
If you don't use filters or similar stuff you should always close a session.
Threads that process requests know nothing about Hibernate session.
SessionFactory.openSession() always open a new session.
SessionFactory.getCurrentSession() do a check, if the current session exists — return it, if the current session doesn't exist — create a new one. A meaning of "current session" depends of a session factory parameters. In the classical case, a session is bounded to the thread (as you describe), but it can be bounded to the transaction, for an example.
Related
I inherited some legacy Business Objects code that needs some TLC. The code is a Servlet. A new IEnterpriseSession is created for every new request into the Servlet. My initial concern is that creating a new IEnterpriseSession for every request seems wasteful and leads to slow requests. Please note that this is an admin IEnterpriseSession, its used over and over again for administrative purposes.
So my question is, is it OK to just create one of these during init() for the Servlet and use it over and over again for as long as the server is up? Can it be shared across threads or does it have to be ThreadLocal? Is there a known timeout exception that is thrown if the session grows stale?
The IEnterpriseSession can be cached and reused. There is a timeout setting inside Business Objects' CMC that can end the session, so do pay attention to if the session itself is valid before using it, if you go about caching. The session itself is an actual login to the Business Objects system so should be unique per user.
Is it possible to model the following using Hibernate + Spring.
Open session
Begin transaction
Do some work
Commit
Begin transaction
More work
Commit
Close session
I use the Spring TransactionTemplate which does both session + transaction lifetime scoping.
The reason is that sometimes I have a few stages in a business process and I would like to commit after each stage completes. However I would like to continue using the same persistent objects. If I have a separate session per transaction then I get transient/detached exceptions because the original session was closed.
Is this possible?
Yes, Hibernate's Sessions can begin and commit several transactions. What you need to do is to store open session somewhere, then reuse it. Note, that Session is not a thread-safe object, but if you're sure it won't have problems with concurrency, what you need is just to use TransactionSynchronizationUtils to bind a session to the thread resources and then unbind it when desired, you can find an example here or you can take a look at OSIV and its standard implementations.
This is a very complicated thing, it's much easier and thus desirable that you close your session right away and don't reuse it, because it may bring troubles:
The objects inside of cache are not automatically evicted, thus your Session will grow in size until OutOfMemory.
The objects inside of session are not flushed unless they are dirty, thus the chance that object was changed by another user is larger and larger. Ensure that only a single user is going to change writable objects.
If some exception happens during one of steps, you have to ensure you close the session. After exception occurred inside of Session, this object is not reusable.
If transaction was rolled back, the session is cleared by Spring, thus all your objects become detached. Make sure your discard everything if at least one of transactions was rolled back.
You could achieve this using the OpenSessionInView pattern. Spring provides a javax.servlet.Filter implementation which you could use if you're working in a servlet environment (question doesn't say so). This will ensure that your Hibernate session is kept open for the duration of the request rather than just for an individual transaction.
The Javadoc on this class is pretty comprehensive and might be a good starting point.
I have just started using Hibernate with HSQLDB. In the tutorial they tell me not to use the anti-pattern "session-per-operation". However, each time I commit a transaction the session is closed as well. How am I supposed to avoid using getCurrentSession() if commit() closes the session?
I'm a bit curious on how people usually hande the scope of the session. I have seen several samples on building web applicatons where you have one session per request. In my case I'm building a service, and cannot apply the same idea. The service is running 24/7, and sporadically it does some database operations. Should I keep the database session alive all the time, and just use transactions as boundaries between operations (considering a case where my transaction commits do not close the session) or should I just create a new one for each operation (which is the anti-pattern, but how else?).
Thanks in advance!
That behaviour is determined by the implementation of CurrentSessionContext in use. The default happens to be ThreadLocalSessionContext which does close-on-commit, but you're by no means constrained to that.
You can configure/build any type of session scope you like by using ManagedSessionContext and binding/unbinding sessions at the appropriate beginning and end of life cycle. It seems to make sense for you that you would bind a Session at the entry to your service's unit of work and unbind it at the exit. (It is of course no trivial task to build robust code for doing this. Particularly remember that you're expected to make a new Session if an exception comes out of one of its methods.)
Responding to comment was getting too large for a comment.
That is the default behaviour because it's the only thing that's "safe" without additional work or configuration provided by the user. The "commit" is the only lifecycle point that Hibernate is able to "see" if you don't help it out, so it has to close there or risk the session being left dangling forever.
Determining potential session life cycle boundaries requires a fair bit of knowledge about what you're actually doing. "It's a background service" isn't much to go on. Assuming it does something like sit idling and wake up every X minutes, do some work, then go back to sleep for another X minutes, then that would be a good boundary to open then close a session.
You may be using an over-broad definition of 'operation' when talking about 'session per operation' being an anti-pattern.
They mean don't do something like (fictitious requirements for your service):
Wake Up Service
Open Session
Read File location from database
Close Session
Open file
Open Session
Update Database tables from current file state
Close Session
Open Session
Write Activity Log to Database
Close Session
Sleep Service
It would be perfectly reasonable to do that in one session then close it at the end. In a single threaded environment where you're managing everything yourself within known boundaries, you can really just open and close the session yourself without using currentSession if you want. You just need to make sure it gets closed in the event of an exception. If you're listening for an operating system event, the event handling would be a perfectly fine scope for the session.
I need to know, whether the Hibernate's session is thread safe or not. But obvious a new session is attached to every thread for execution. But my question is if in one thread I have updated some value of an entity, so will that be reflected in other thread during same time execution?
My problem is when I fire update from two threads sequentially, the value is updated properly but when I fire the update almost altogether then it fails.
for eg.
current stage of table.
ID NAME MARKS
------- --------- --------
1 John 54
I am trying to do follwing :
Student student = session.load(Student.class, 1);
student.setMarks(student.getMarks() + 1);
session.update(student);
session.close();
When I try to run the above code in loop say 10, then value of "marks" in table "student" is properly updated i.e. the value gets updated to 64 which is proper.
But when I try to run the same code in threaded environment, it gives bad results.
It is not intended that implementors be threadsafe. Instead each thread/transaction should obtain its own instance from a SessionFactory.
Even with this in mind, your behaviour might still not be what you expect, because transactions come into play. You will have to set a proper transaction isolation level. See the configuration guide, hibernate.connection.isolation property.
Hibernate session and threads do not mix.
You should not use a session from multiple threads at once, and I recommend you only use a session from a single thread. DB session implementations are not even required to be theadsafe.
You also must consider what happens to the transactions when you start doing things in multiple threads. Transactions are tied to the current thread. This becomes quickly mindblowing and you enter areas where the implementers have not tested their products.
In the end life is too short to get lost in that swamp.
It depends on how you are creating a session.
Session can be created in two ways in hibernate.
getCurrentSession()
Yes. It offers thread safety as it'll ensure that it'll create a session for each thread if session not exist. transaction and automatic session closing is attached to this.
openSession()
It's not thread safe. developer manually needs to manage transactions and session flush and close operations.
Hibernate sessions are not thread safe. Use TheadLocal class to create sessions for each thread:-
private static ThreadLocal<Session> threadSafeSession = new ThreadLocal<Session>() {
protected Session initialValue(){
return sf.openSession();
}
};
In your method get session for each thread as:-
Session session = threadSafeSession.get();
Just wondering if beginning a new transaction in Hibernate actually allocates a connection to the DB?
I'm concerned b/c our server begins a new transaction for each request received, even if that request doesn't interact with the DB. We're seeing DB connections as a major bottleneck, so I'm wondering if I should take the time narrow the scope of my transactions.
Searched everywhere and haven't been able to find a good answer. The very simple code is here:
SessionFactory sessionFactory = (SessionFactory) Context.getContext().getBean("sessionFactory");
sessionFactory.getCurrentSession().beginTransaction();
sessionFactory.getCurrentSession().setFlushMode(FlushMode.AUTO);
thanks very much!
a
According to the section 11.1. Session and transaction scopes of the Hibernate documentation:
A SessionFactory is an
expensive-to-create, threadsafe
object, intended to be shared by all
application threads. It is created
once, usually on application startup,
from a Configuration instance.
A Session is an inexpensive,
non-threadsafe object that should be
used once and then discarded for: a
single request, a conversation or a
single unit of work. A Session will
not obtain a JDBC Connection, or a
Datasource, unless it is needed. It
will not consume any resources until
used.
In order to reduce lock contention in
the database, a database transaction
has to be as short as possible. Long
database transactions will prevent
your application from scaling to a
highly concurrent load. It is not
recommended that you hold a database
transaction open during user think
time until the unit of work is
complete.
Now, to answer your question:
getting a Session does not immediately acquire a connection (the connection is lazy loaded)
but calling beginTransaction() will cause the load of the connection for the given Session
subsequent calls will reuse the same connection
Look at org.hibernate.impl.SessionImpl#beginTransaction() and go through the code for more details.
(Updated per Pascal Thivent's comment)
Each Session creates a database connection if there is a need for that - e.g. if a transaction is started. The connection is not opened with the mere creation of the session.
To overcome this, you can use a connecetion pool so that connections are reused. Or you can make sure (as it appears you did) that no transaction is started automatically.
(This discusses read-only transactions. Take a look.)