Reputation: 10872
What's the best practice?
I've googled around, and there's no consensus. Everyone says something, and then takes it back. I find lugging a session around stupid...
So, what's the deal?
Upvotes: 13
Views: 6528
Reputation: 1
I use a session per series of database actions that can be wrapped in a transaction, this includes passing Collections of objects whenever possible at a go, but the issue for closed session for lazy loaded fields / properties kept cropping up.
My Applications are hybrid of Swing and JavaFX and this is how I fixed them :
My first approach was not to have any Lazy Loading in use, but this turned out to be nightmare for some of the Entities with more complicated Object Graphs.
The Table View control from JavaFX plays an important role in my apps, I think the swing JTable sucks as compared to the TableView on JavaFX.
Now, when I load a JFrame or an JInternalFrame or a JDialog, I preload the most recent 'worked on' entities, all their children are Loaded with Lazy Loading, no perfomance hit, I bind the data to the TableView control with the primary core eager loaded fields being visible. A user is required to select a record from the TableView Grid to work on, when they do that I fetch the instance of the entity afresh since I have a handle to its Id,after the object is fetched, then the .size() method is invoked for all the children collections that are loaded lazily. This forces Hibernate to load the lazy fields / properties.
I can now do whatever I want with a particular instance of an Object, and all the children are populated.
I think this approach improves performance two-fold. When fetching List data,its fast. When you fetch an Object with an Id, its pin point, and since the children are all lazy loaded means the selects are also light and more compact.
Upvotes: 0
Reputation: 4972
We started out with one-session-per-app , I've listed the the motivations and issues below,
As the name suggests the whole user interaction with the app would consist of one single long living session and all ( main) the communication with the database would be through this session. Your “unit of work” would now encompass all the user interaction with the application …meaning your “application transaction ” is the whole life of the application.
Why ? 1. The Biggest driver for doing this seems to be ability to use hibernate’s lazyintialization capabilities , for example a dialog displaying Book information can dynamically populate author information when the drop down combo box is selected.
Since all the modifications to underlying data is done through this single long running session , you can explicitly demarcate transaction boundaries at convenient locations .
Take advantage of session cache without having to load/unload object each time from the DB.
How :
If you perform all the major work in GUI thread ( almost all java GUI systems are single event threaded you can use tweak thread local pattern to get the main session from a GUI thread .
Issues:
Above advantages might paint a rosy picture but using long running sessions without much forethought might cause some serious problems that might not really be apparent until the application get big or complex enough .
I still think if you have a consistent eviction strategy , for example evicting the order graph when order window is closed, this might be a good startegy .
We switched to session-per-window , which had its own issues like managing inter window communication ( for example changes in prices window could make order window data stale ). Might want to Look at Christian Bauer's swing example which uses this approach.
I guess , as with everything else , there is no single right way to do it, how you manage the session depends on how the application is laid out , for example , if each window can be an independent unit of work , which doesnt interfere with other units of work ,then session per window might be a better approach .
Upvotes: 5
Reputation: 3539
What you really want to avoid is the session-per-operation scenario, where for every single database operation a new Hibernate session is created and closed.
EDIT: Ignore my earlier nonsense about one session for the lifetime of the app, it's entirely possible (and probably the most efficient way) of doing this. Just make sure to demarcate your transactions appropriately - be it declaratively using something like Spring @Transaction, or programmatically.
Upvotes: 0
Reputation: 36977
Since session creation is expensive in Oracle, and I mostly use Oracle as RDBMS, I'd go for "one single session for the whole application". "One session per event handling" seems ridiculous in the context of a GUI application. There are reasons to do that on the web, but those reasons do usualy not apply for a GUI program.
Upvotes: 0
Reputation: 6873
I've used Hibernate in a enterprise size Rich Internet Application, which by technique resembles a lot of a hibernate + swing combination. I spent quite a lot of time investigating the different design patterns for using hibernate in a 3-tier application.
Hibernate isn't exactly meant for this kind of application, so there wasn't that much information available on this subject. I tried a different design patterns but most of them lead to memory leakage, problems with attaching and de-attaching objects in session(s), problems with transactions etc etc. The final solution was to use a session-per-request pattern. Basically, at every request you make from the UI logic to the business logic, you create a new hibernate session. Your business logic then performs whatever you want it to do and right before ending the business logic execution, you flush and close the open session. This way there won't be any memory leakage, objects will be correctly attached and de-attached from sessions.
This isn't a perfect solution, as you will encounter some problems, such as with lazy loading and transactions. I'll briefly explain those problems and how to solve them.
Transactions
Because you terminate the hibernate session after each request, you cannot have transactions which live beyond one request. This can be somewhat problematic, for example, let's say you want to store 10 objects within the same transaction. You cannot make a save request separately for each object, as the transaction is terminated. Therefore you need to make a method which takes as input a list of objects and saves all those objects within the same transaction. If the transaction fails, then you rollback all the objects.
Lazy loading
Your objects' lazy loading will not work because they most likely not attached to a session (that is, if you lazy load something once the session has been terminated). For the lazy loading to work, you need to reattach the objects to the session. I came up with a workaround for this as well, which is a bit tricky when creating new entity objects, but works nicely in everyday development. The idea is to have duplicated getters and setters for a field which is lazy loaded. One pair is private and the other one is public. The idea is that the private getter/setter pair is what hibernate uses internally and the public getters and setter are transient and used by the developer. So, what happens is that when the developer calls on the public getter, the getter checks if the field has already been loaded, if not, then attach the object to a session, load the field and close the session. Voilá, everything works and the developer never noticed anything. Here's a small code to give you the idea:
@Entity
public class Foo {
List<Bar> bars = new ArrayList<Bar>();
@OneToMany
private List<Bar> getBarsInternal() {
return bars;
}
private void setBarsInternal(List<Bar> bars) {
this.bars = bars;
}
@Transient
public List<Bar> getBars() {
// pseudo code
if(check if bar is still lazy loaded) {
// the field is still lazy loaded
// attach the field to a session an initialize it
// once the field is initialized, it can be returned
}
return getBarsInternal();
}
public void setBars(List<Bar> bars) {
setBarsInternal(bars);
}
}
Upvotes: 6
Reputation: 328556
What you want to solve is synchronizing two models: You have the in-memory model and the database model. Just propagating each change into the database, as it happens, is too expensive. Also, you want to be able to handle errors (i.e. roll back your model to a consistent state). To be able to do this, you must have a way to say "now, it's consistent".
The current solution is to start a transaction in the database when the model is known to be consistent (usually before you start making changes) and then do all the changes in your in-memory mode, map them somehow to the database, update the database and then commit the transaction.
While this sounds simple, OO programming actively gets in the way. We hide model operations deep in the call structure, we try really hard for users of a piece of code not to know what the code actually does. In a perfect world, your development tools should unroll all the code an operation needs into a single method/function, wrap that in a transaction and be done with it.
This doesn't work. Instead, we decided to introduce a global variable: the session. Which is bad, and we're ashamed of it, so we try to hide this fact but session is global - per operation. Now you need a way to attach the session to the operation. You can say "all code which gets executed in the current thread is one operation". If you do, the natural solution is to make the session global per thread.
Or you have some token. Then you will attach the session to the token and pass that around.
But the fundamental problem is and always was: How to attach a session to a single operation on the model. The hard parts are to know when an operation starts, when it ends and how to handle errors.
For web applications, using a request is the natural way to define an operation: Everything that happens during a request is considered a single step. The app doesn't really keep the model in memory; everything is forgotten at the end of the request and loaded again from the database when the next request comes in. Slow but manageable.
Desktop application are a completely different kind of beast. They usually keep the whole model in memory all the time. We don't persist changes unless the user asks for it (when she "saves" her work) because that would be too slow and, since there is nothing like a request, there is no simple, automatic way to define an "operation".
The idea to attach an operation to an event is good. Only, in desktop apps, you can have multiple threads which communicate with events and now, you need a way to mark all events as "they belong to the operation started with event X received long ago". Events are usually small, immutable pieces of data, so you can't attach your session to them. But you need some kind of token to mark all events which belong together. Which is why most desktop apps either work with a server (which again works like a web app) and without a big in-memory model or they don't use a database but save their model in a custom format (think Office).
Upvotes: 2
Reputation: 262
Best Practice (your mileage may vary): The most common pattern in a multi-user client/server application is session-per-request. In this model, a request from the client is send to the server (where the Hibernate persistence layer runs), a new Hibernate Session is opened, and all database operations are executed in this unit of work. Once the work has been completed (and the response for the client has been prepared), the session is flushed and closed. You would also use a single database transaction to serve the clients request, starting and committing it when you open and close the Session. The relationship between the two is one-to-one and this model is a perfect fit for many applications.
see: http://docs.jboss.org/hibernate/stable/core/reference/en/html_single/#transactions-basics-uow
Upvotes: 1
Reputation: 41858
If you create a threadpool then you don't pay much of a price for making connections per transaction or per request, depending on what you are doing.
I tend to divide my code at the lower level into a class that deals with a table or some other object, and then above that will be a controller, which knows how to talk to multiple daos. For example, to place a book order you check if it is in stock, then you process payment, then decrement the item and place the order. These would be a transaction spanning multiple daos, so they should use the same session.
But, using one connection would be problematic, as you are tying up a connection for a while, and you may lose the connection, while assuming it still exists. The same problem would be compounded if one request per screen or page.
You could look at some best practices from Spring users, as Hibernate seems to be pretty popular with that framework.
Upvotes: 0
Reputation: 1721
I prefer session per model update. It helps avoid long-lasting sessions and also it helps minimize total number of session creations.
Upvotes: 0
Reputation: 7732
Maybe a session per event handling is the right approach. In web applications, we usually create one session per request (using all those OpenSessionInView stuff and etc). If we think through, every request in web application is a different interaction in the application, and in a Swing application, every fired event is a different interaction in the application. If we apply the same principles we apply in web applications in Swing applications, then we should create a session per event handling.
But we must determinate if using Hibernate is the best option... The database you are accessing is a local or a remote database? Do other applications share the same database? You should consider creating an EJB\HTTP backend instead having your Swing applications directly accessing the database. Using Hibernate suggest that you have a not-so-simple database, so I think you should consider create a EJB\HTTP backend.
Upvotes: 6
Reputation: 187499
I can't really see any compelling reason why you would want to things differently in an app with a Swing front-end, then a web front-end. In the latter case, usually a Session is created for each request, though in many cases the actual creation of the Session is handled transparently (e.g. by Spring).
Assuming your Swing app implements the MVC pattern, then why not just create the Session at the beginning of each controller method, and close it at the end. If your app is multi-threaded, you could use a ThreadLocal variable to easily scope each Session to a particular thread. This is almost exactly analogous to what happens in a Servlet container (i.e. a web app).
Upvotes: 1
Reputation: 4956
It really depends on the app, but really you only want a session to last as long as the logical transaction on the database. I'm pretty sure sessions aren't thread-safe so you may want to look at localising the behaviour to a single class.
Upvotes: 1
Reputation: 13843
When closing the session, be aware that this causes your objects to become detached, and will involve some additional overhead to reattach the objects to the session in order to save or update them with the database.
That being said, I agree with you that lugging a session around doesn't make much sense, and you should be releasing the session at the end of each transaction. I would lead toward having a pool of sessions from which each thread can access a session as it needs it, and release it back to the pool when it is done. You can use Spring to help you manage the sessions for this.
Upvotes: 1