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.
Related
I am working on building a microservice which is using transaction manager implemented based on Java Transaction API(JTA).
My question is does Trasaction maanger have ability to handle concurrency issue in distributed database scenario's .
Scenario:
Assume there are multiple instance of a service running and we get two requests to update balance amount by 10 in an account. Initially an account can have $100 and the first instance gets that and increments it to $10 but has not been commited yet.
At the same time the second instance also retreive's account which is still 100 and increments it by $10 and then commits it updating balance to $110 and then service one updates account again to $110.
By this time you must have figured that balance was supposed to be incremented by $20 and not 10. Do I have to write some kind of Optimistic lock exception mechanism to prevent the above scenario or will Transaction Manager based on JTA specification already ensure such a thing will not happen ?
does Trasaction maanger have ability to handle concurrency issue in distributed database scenario's .
Transactions and concurrency are two independent concepts and though Transactions become most siginificant in context where we also see concurrency , transactions can be important without concurrency.
To answer your question : No , Transaction Manager generally does not concern itself with handling issues that arise with concurrent updates. It takes a very naive and simple ( and often most meaningful ) approach : if after the start of a transaction , it detects that the state has become inconsistent ( because of concurrent updates ) it would simply raise it as an exception and Rollback the transaction. If only it can establish that all the conditions of the ACID properties of the transaction are still valid will it commit the transaction.
For such type of requests, you can handle through Optimistic Concurrency where you would have a column on the database (Timestamp) as a reference to the version number.
Each time when a change is commited it would modify the timestamp value.
If two requests try to commit the change at the same time, only one of them will succeed as the version (Timestamp) column will change by then negating other request from comitting its changes.
The transaction manager (as implementation of the JTA specification) makes transparent a work above multiple resources. It ensures all the operations happens as a single unit of work. The "work above multiple resources" mean that that the application can insert data to database and meanwhile it sends a message to a JMS broker. Transaction manager guarantees ACID properties to be hold for this two operations. In simplistic form when the transaction finishes successfully the application developer can be sure both operation was processed. When some trouble happens is on the transaction manager to handle it - possibly throw an exception and rollback the data changes. Thus neither operation was processed.
It makes this transparent for the application developer who does not need to care to update first database and then JMS and checks if all data changes were really processed or a failure happens.
In general the JTA specification was not written with microservice architecture in mind. Now it really depends on your system design(!) But if I consider you have two microservices where each one has attached its own transaction manager then the transaction manager can't help you to sort out your concurrency issue. Transaction managers does not work (usually) in some synchronization. You don't work with multiple resources from one microservice (what is the usecase for the transaction manager) but with one resource from multiple microservices.
As there is the one resource it's the synchronization point for all you updates. It depends on it how it manages concurrency. Considering it's a SQL database then it depends on the level of the isolation it uses (ACID - I = isolation, see https://en.wikipedia.org/wiki/ACID_(computer_science)). Your particular example talks about lost update phenomena (https://vladmihalcea.com/a-beginners-guide-to-database-locking-and-the-lost-update-phenomena/). As both microservices tries to update one record. One solution for the avoiding the issue is using optimistic/pesimistic locking (you can implement it on your own by e.g. timestamps as stated above), the other is to use serializable isolation level in your database, or you can design your application for not reading and updating data based on what is read first time but change the sql query having the update atomic (or there are possibly other strategies how to work with your data model to achieve the desired outcome).
In summary - it depends on how your transaction manager is implemented, it can help you in a way but it's not its purpose. Your goal should be to check how the isolation level is set up at the shared storage and consider if your application needs to handle lost update phenomena at application level or your storage cang manage it for you.
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.
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 set of batch/cron jobs in Java that call my service classes. I'm using Hibernate and Spring as well.
Originally the batch layer was always creating an outer transaction, and then the batch job will call a service to get a list of objects from the DB w/ the same session, then call a service to process each object separately. Theres a tx-advice set for my service layer to rollback on any throwable. So if on the 5th object theres an exception, the first 4 objects that were processed gets rolled back too because they were all part of the same transaction.
So i was thinking this outer transaction created in the batch layer was unnecessary. I removed that, and now i call a service to get a list of objects. THen call another service to process each object separately, and if one of those objects fail, the other ones will still persist because its a new transaction/session for each service call. But the problem I have here now is after getting a list of objects, when i pass each object to a service to process, if i try to get one of the properties i get a lazy initialization error because the session used to load that object (from the list) is closed.
Some options i thought of were to just get a list of IDs in the batch job and pass each id to a service and the service will retrieve the whole object in that one session and process it. Another one is to set lazy loading to false for that object's attributes, but this would load everything everytime even if sometimes the nested attributes aren't needed.
I could always go back to the way it was originally w/ the outer transaction around every batch job, and then create another transaction in the batch job before each call to the service for processing each individual object...
What's the best practice for something like this?
Well I would say that you listed every possible option except OpenSessionInView. That would keep your session alive across transactions, but it's difficult to implement properly. So difficult that it's considered an AntiPattern by many.
However, since you're not implementing a web interface and you aren't dealing with a highly threaded environment, I would say that's the way to go. It's not like you're passing entities to views. Your biggest fear is an N+1 call to the database while iterating through a collection, but since this is a cron job, performance may not be a major issue when compared with code cleanliness. If you're really worried about it, just make sure you get all of your collections via a call to a DAO who can do a select *.
Additionally, you were effectively doing an Open Session In View before when you were doing everything in the same transaction. In Spring, Sessions are opened on a per transaction basis, so keeping a transaction open a long period of time is effectively the same as keeping a Session open a long period of time. The only real difference in your case will be the fact that you can commit periodically without fear of a lazy initialization error down the road.
Edit
All that being said, it takes a bit of time to set up an Open Session in View, so unless you have any particular issues against doing everything in the same transaction, you might consider just going back to that.
Also, I just noticed that you mentioned opening a transaction in the batch layer and then opening "mini transactions" in the Service layer. This is most emphatically NOT a good idea. Spring's annotation driven transactions will piggyback on any currently open transaction in the session. This means that transactions that are supposed to be read-only will suddenly become read-write if the currently open transaction is read-write. Additionally, the Session won't be flushed until the outermost transaction is finished anyways, so there's no point in marking the Service layer with #Transactional. Putting #Transactional on multiple layers only lends to a false sense of security.
I actually blogged about this issue some time ago.
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.)