views:

1231

answers:

12
+6  Q: 

Hibernate + Swing

What's the best practice?

  • A single session for the whole app
  • A session per window
  • A session per thread, and detaching everything at random
  • Something else?

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?

+1  A: 

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.

Elie
+1  A: 

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.

Jamie Lewis
+1  A: 

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).

Don
+4  A: 

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.

razenha
A: 

I prefer session per model update. It helps avoid long-lasting sessions and also it helps minimize total number of session creations.

Rastislav Komara
A: 

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.

James Black
+1  A: 

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

Tom
+2  A: 

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).

Aaron Digulla
I must say, that is an excellent definition of the problem. But not a solution (although, a definition of the problem does help obtain a solution). Thanks.
Tordek
Since computers are still so slow today that we have to care about performance, there is not yet a general solution.
Aaron Digulla
+3  A: 

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);
  }
}
Kim L
A: 

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.

ammoQ
Hibernate sessions are quite cheap, using one session per application will lead to memory leakage :(
Kim L
Would you care to elaborate why session creation is so expensive in Oracle? I use Hibernate with Oracle and haven't noticed any major performance problems, but I don't really do anything too heavy and our DB is very fast so it might just be hiding any problems.
Pavel
A: 

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.

Pavel
I don't see how having one session for the lifetime of the program could keep you from doing a commit whenever a request has been processed; so why should there be a big data loss?
ammoQ
You're right actually, that shouldn't stop you from using one session.
Pavel
+2  A: 

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.

  1. Since all the modifications to underlying data is done through this single long running session , you can explicitly demarcate transaction boundaries at convenient locations .

  2. 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 .

  1. Your app might suffer from ” bloated session cache ” phenomenon..with each your application getting slower and slower with as the session cache grows bigger and bigger as hibernate has to go through all the objects in the session cache for every query ( with huge session this might slow you down ,If the session gets big, flushing it will take more and more time, even if there are no changes). Essentially advantage no 3 above can become a disadvantage ( due to hibernates transactional write-behind ) if session in allowed to grow big. A workaround that might be useful in some cases might be to use query.setReadOnly(true) , session.setReadOnly() for queries/ entities which load up readonly objects into the session.
  2. Manual “eviction” to keep session consistent, You might have use sql queries for various reasons in various places in the app to perform updates/deletes in such case you have do manual evicts in order to keep session from being corrupted.
  3. Transactional write-behind might come in your way ( depending on how you manage long running session ) if you plan to access data from “other sessions” .
  4. User might experience GUI freezing when hibernate is doing its lazyloading magic.
  5. Classic lazy loading n+1 query problems ( see links below)
  6. Stale data problems .if the data is updated by some background job or thread not using the main session ..your main session will not be up to date with the changes.

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 .

Surya