Version 2

    Christian: I have now written a proof of concept that shows how to work with Hibernate in a two-tiered thick client scenario. Please read my weblog entry. This should answer most questions on this page.

    Started by Gunther Schadow.

     

    Much has been written about how to use Hibernate in the application server layer in a 3 tier web-application. Conversely, this page is to collect (and discuss) patterns in which people apply Hibernate to applications that are not web applications, but rather two-tier thick clients that use Hibernate directly to connect to the database. This application architecture is still a perfect fit for a highly interactive application, such as a CAD design tool, Word Processor, or other engineering workstation, etc. The primary characteristic of these applications is that they are highly interactive, with multiple views and tasks open at the same time, with multiple things happening in the background, etc.

     

    This page should evolve, from different people's contributions. I will cut right to the chase stating what we are practicing now with Hibernate 3.1:

    Hibernate 3.1 requires that a Transaction is open for writes and for read access.

     

    When you have complex object graphs in memory, it is too costly to throw them away each time a user has executed a command (as in Command Pattern) which involves a database transactions. There are still many views open looking at the objects, and at the click of a button may want to follow links to related objects that can only be lazily loaded.

     

    We have to keep a Session open for a very long time, and the session cache may become big, but we don't worry because this is a thick client, running as more or less a signle application on an entire workstation, so it can grow to use quite a lot more memory than your typical 5000th sever thread of an application server.

     

    We would like to have a way to do read-only lazy loading for objects as the user clicks and browses around in the object graph, while all actions that modify the graph would be carried out in some specifically demarcated transaction. In the best case one would pull those objects that we want to modify into a different session, do our modifications, and then commit, and then have these objects still in the read-only session.

     

    This might be possible with some (significant?) enhancements to Hibernate, but short of that we are using the following workaround:

     

    Implicit Humongous Transation

    We have a single session, we assume that all object transactions, even lazy reads, are made thread safe somehow. Then we have this single session open and immediately begin a transaction. When we are at the point where a transaction needs to be committed, we commit all changes that have happened to the object graph in memory, then immediately begin a new transaction and carry on in the same session.

     

    This is basically the same thing that you have when working on an SQL command line: you do selects, inserts and updates, and then you write "COMMIT;" to commit all that you did. After committing a new transaction is immediately open.

     

    We will just be careful in our application not to change objects around inadvertently outside of what we call a command (as in Command Pattern). (Christian: I don't see how this stuff has anything to do with the Command pattern. -- Gunther: see Wikipedia on Command pattern, it's an object representing a transaction. That's how I do all our user transactions, in a Command, and each Command has its own Session & Transaction.)

     

    Multi-Session Pattern

    Many applications can be divided into modules which appear on the GUI as tool-boxes. Often you have certain lists and trees at the left, right or bottom margin, and then some main editor box in the center. Most IDE's look that way. The center is often where the main transactions are happening. If you use the Command pattern or some other Task pattern which are represented by these center windows, you can open a separate transaction for each of those tasks plus one very long running, even read-only transaction for everything else. This seems to be what I will try next in my application.

     

    • One session will be read-only and allows the user to browse around in the application's data.
    • Then for each Commands (or major delineated Task) I would create a separate session and transaction.
    • For instance, in an IDE you would have the object browser tree on the left sidebar. Then when double-clicking an object it would come up in an editor in the center. This center editor task is represented by a command (as in Command-Pattern), and when submitting the changed (as in Save), this transaction is committed.
    • This works because the coupling between the browser view and the specific command view may be very loose, only passing only a few arguments, such as the object which is to be edited. It is not too much to have this object loaded again in the command-specific session.
    • We are playing with this now, and I just wrote 2 pages of analysis on the subject which this wiki has just lost. I hate it. The message was: it should be possible to hold the same java object in two open sessions concurrently.

     

    Discussion of the Multi-Session Pattern

    Many might say that this is a bad idea (as Christian has said), because it violates fundamental Hibernate concepts (or any persistence service that has a single-threaded identity map). A Session is the identity scope for a set of objects that represent a set of database rows. You can not ever have two concurrent identity scopes for the same set of objects.)

     

    But still, the issue seems pressing. So, I have a rich client, with model, view, controller, and possibly multiple actions happening on the same object model. I say two approaches:

     

    A) Delineate user transactions (e.g., individual Commands, as in Command-pattern) and have each work on its own set of Java objects, using a separate session. No object is ever shared between such Commands. This can be done by forcing the Commands to clone (reload) objects given to it as arguments before transacting with them, then commit (or rollback) and close the session.

     

    Advantage: Hibernate can do that now. Transactions are clearly delineated.

    Disadvantage: It's not natural to have one real world objects represented in Java memory twice just because it is used in different Transactions. It can be quite expensive to reload object graphs that are already in memory just to use them in a different session. Finally it leads to StaleSession problems when multiple parallel sessions interact with the same real world objects.

     

    B) Share Java objects between sessions. Multiple Sessions referencing the same Java objects poses difficulties when dealing with lazy loading of associations, and apparently also with the version number (apparently Sessions use the saved version numbers, not the ones stored in the object.) All that can be fiddled with.

     

    Advantage: one Java object represents one real-world object. Less need to reload data from database. Less chance for StaleSession because of concurrent modifications to the same objects.

     

    Disadvantage: transactions are not entirely delineated. For instance, say object Foo@123 is set in Transaction t1 to bar++, and in Transaction t2 we set baz++ then t1 commits, while t2 rolls back. Now the change of baz++ is still committed and won't be rolled back.

     

    The latter may be perceived as a very big deal. However, why is it we can create interactive object applications (e.g., Word processors) that operate unconstrained and only because of Hibernate we suddenly have to change that? And this problem can be minimized if the Objects we deal with are rather small in scope. For instance, if the two transactions do not modify the very same object, but only related objects, then it should not be such a problem. And if we say it is a huge problem, then how can it be addressed? I don't think one should throw out the use case as invalid only because it doesn't seem to fit into the specific transaction model.

     

    References

     

    Some resources from the forum:

    * http://forum.hibernate.org/viewtopic.php?p=2266139#2266139 - discusses issues with StaleObjectExceptions, which is bound to occur if two users have long running sessions with concurrent updates of the same objects.

     

    External References

    * http://www.360works.com/dev_tools/HibernateLazyLoading.html - Jesse Barnum's a very small, non-disruptive patch to allow lazy loading outside of an open, connected session. (Christian: This is absolutely not recommended. I don't have time to debunk this right now, but don't do it.)