-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Proposed session management strategy for thick clients
PostPosted: Wed Mar 03, 2004 11:43 am 
Regular
Regular

Joined: Wed Sep 03, 2003 9:56 pm
Posts: 58
In my efforts to address the problems encountered using the 'open session->load data->close session' model in a thick-client environment, I've written a 'mini-spec' that proposes a 'synchronized singleton' pattern approach.

I thought that before I released it to development for implementation, I'd post it here for (valued) critical review. If this is all wrong, I'd much rather hear about it now than discover it during/after development.

Its long, and may not be relevant to many, but my hope is that someone will have time to peruse it, and if there are glaring errors and/or ommissions, provide feedback.

Enjoy.

-Mitch

Quote:
OVERVIEW

The use of the Hibernate Object/Relational mapping tool within [our product] necessitates special consideration of our database session management strategy. Our current 'open session->load objects->close session' model has lead to significant data redundancy issues that warrant a new Hibernate session management strategy.

'SESSION' DEFINITION

Within the context of Hibernate, a database 'session' defines both the physical (i.e. JDBC) database connection and a local cache of database objects that Hibernate manages with respect to the database. Hibernate uses this session cache to optimize interaction with the database. When a request is made to load objects from the database, the Session object being used for the request will first consult it's local cache prior to generating a database query. If the desired object exists within the session cache, no database interaction is required, and the object is retrieved from the cache. If the object does not exist in the session cache, the associated data is retrieved from the database, local data objects are created from that data and added to the session cache, and finally returned to the requestor.

This session cache facilitates 'lazy loading' of database objects. If the Session object is closed, the session cache is destroyed. In this case, Hibernate must honor the relationships specified by the Hibernate mapping files, and retrieve and populate the entire object graph on behalf of the requestor. Obviously, this precludes loading objects on demand.

PROBLEM DEFINITION

Without an existing session object, and it's associated session cache, significant duplication of data exists in the client. For example, if a client component requests a given object from the database, and subsequently closes the Session with which it accessed the object's data, Hibernate has no choice but to populate an entire object graph representing that object(i.e. loads all reachable objects as defined by the object mappings). This situation results in a couple of problems. The client component may not need, and may never use much of the data contained in the object graph associated with a particular object. This results in data retrieved from the database that is never accessed. Furthermore, assume that the user requests an object via a different client component. A new Session object will be allocated to service the request, with an empty session cache. This results in pulling the entire object graph for that object, regardless of the fact that this data already resides in the client as a result of the previous client component's query. Not only does this result in needless overhead as redundant data is retrieved and redundant local objects populated, it creates a dangerous situation in that if one of the two copies of a given object graph is modified, and updated to the database, the 'other' copy of the object graph now contains stale data.

PROPOSED SOLUTION

A simple solution is to migrate from our current 'open session->load data->close session' semantics to an implementation of the singleton pattern for a single, system-wide database session object within the client. This single database session object will be used for *all* database interaction, resulting in a valid cache of all database objects retrieved from the database. This means that once a particular object graph has been loaded from the database, all subsequent requests for objects within that object graph will come from the local session cache, and not require redundant database access. This also ensures that all references to database objects within the client remain fresh (with regard to the session cache). In other words, changes to objects within one portion of the client, are immediately visible to any other client components holding references to the modified data objects.

IMPLEMENTATION

In accordance with the singleton pattern specification, there will exist a HibernateSession object that implements a 'public static synchronized Session getCurrent()' method which returns the singleton hibernate session object. Technically speaking, the singleton pattern specifies a 'getInstance()' method. However, getCurrent() is more syntactically correct for this implementation.

THREAD MANAGEMENT

Since Hibernate session objects are inherently *not* thread safe, HibernateSession.getCurrent() enforces a single threaded access model. If a thread makes a call to HibernateSession.getCurrent(), and the singleton Session object is already in use by another thread, the subsequent calling thread will block (via Object.wait()) until the Session object becomes available. This approach requires that the HibernateSession class implement a 'public static HibernateSession.releaseSession(Session s)' method. This method *must* be called when a thread is done with the Session object. HibernateSession.releaseSession() is responsible for notifying waiting threads and re-allocating the Session object.

For consistency sake, there is also a 'public static synchronized Session HibernateSession.getCurrent(long timeout)' method which returns after the specified number of milliseconds if the singleton Session object does not become available. In this case, HibernateSession.getCurrent() throws an InterruptedException.

DATABASE CONNECTION MANAGEMENT (POOLING)

While it is reasonable to maintain a long-lived session object from a data consistency perspective, this approach presents problems from a physical database connection (i.e. JDBC) perspective. Physical database connections are subject to failure. These failures can result from session timeouts, network interruptions, etc. Fortunately, Hibernate supports the ability to dissociate the physical database connection from the session object. Since the management of physical database connections is better left to existing JDBC connection pooling technologies, interaction with the database connection pooling mechanism will be incorporated into the singleton session pattern implementation. HibernateSession.getCurrent() will allocate a JDBC connection from the connection pool, and associate that JDBC connection with the Session object prior to returning the Session to the requestor. HibernateSession.releaseSession() will detach the JDBC connection from the Session object and return the connection to the pool implementation. This allows for validation and/or error recovery of connection related problems by the pool implementation.

USAGE PATTERN

In order to access persistent objects within the client, the singleton Session object is accessed via the HibernateSession.getCurrent() method. The returned Session object is then used for interaction with the persistence layer. When the requestor is done with the Session object, HibernateSession.releaseSession() is called. It is imperative that HibernateSession.releaseSession() be called to release the singleton Session object for use by other requestors.

A typical implementation is as follows:

try{
Session s = HibernateSession.getCurrent(5000);
{...interact with the database...}
} catch (HibernateException he) {
{...error handling code...}
} catch (InterruptedException ie) {
{...notify the user...}
} finally {
// guard against InterruptedExceptions
if(s != null)
HibernateSession.releaseSession(s);
}


Top
 Profile  
 
 Post subject: Re: Proposed session management strategy for thick clients
PostPosted: Wed Mar 03, 2004 1:11 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
mitchellch wrote:
Within the context of Hibernate, a database 'session' defines both the physical (i.e. JDBC) database connection and a local cache of database objects that Hibernate manages with respect to the database. Hibernate uses this session cache to optimize interaction with the database.

Not only optimisation, it is required to respect DB constraints.

mitchellch wrote:
When a request is made to load objects from the database, the Session object being used for the request will first consult it's local cache prior to generating a database query. If the desired object exists within the session cache, no database interaction is required, and the object is retrieved from the cache. If the object does not exist in the session cache, the associated data is retrieved from the database, local data objects are created from that data and added to the session cache, and finally returned to the requestor.

Check find, list and load API, what you said isn't always true.

mitchellch wrote:
This session cache facilitates 'lazy loading' of database objects. If the Session object is closed, the session cache is destroyed. In this case, Hibernate must honor the relationships specified by the Hibernate mapping files, and retrieve and populate the entire object graph on behalf of the requestor. Obviously, this precludes loading objects on demand.

I don't get you. if the session object is closed, then the lazy collection cannot be retrieved. To solve that, the object has to be reassociated to a new session.

Tell people that this is a very specific case due to your 1 client per session usage.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 03, 2004 1:20 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
On quick perusal, I think this is a workable usage solution for fat clients, provided the sessions are actually managed in each of the clients (as opposed in a common service layer utilized by all the clients). Three points to bring up for consideration:

1) As you mention, the Hibernate session acts as a "Unit of Work" semantic cache. As such, it makes no effort to purge "stale" data from its internal cache; and rightful so, since by definition a "Unit of Work" should be relatively short lived. So your HibernateSession class should take some pains to avoid this staleness issue.

2) The whole releaseSession() thing puts a lot of onous on the developers of the client to cleanup after themselves. You may want to consider a unified solution to this and the cache staleness issue, as they are both really resource release concerns.

3) (Personal preference) Rather than Object.wait() calls, I would highly recommend Doug Lea's concurrency package. It greatly simplifies concurrency programming. That package is what became the standard java.util.concurrency package in JDK1.4 (Sun got Doug to write that standard package).


Provided you have either coded solutions to #1 and #2 above or simply choose to acknowledge those as limitations this will work. Another option is utilize the "Unit of Work" concept. This is how TopLink and Hibernate work. You'd just need to expand this a bit for explicit fat-client usage. Specifically, the definition of what constitutes a UnitOfWork in your fat-client system. The controller could determine what use-case the current request belongs to and either pass the currently existing UnitOfWork to the model or generate a new one.

The UnitOfWork could have methods like commit(), suspend(), resume()etc to manage the lifecycle of its resource usage. Commit() would flush the current contents to the database through the Hibernate Session and then release the session. Suspend() would free up the underlying JDBC connection back to its pool and resume() would grab a connection again.

An implementation like this takes a lot of fore-thought, however, especially in fat-client systems in terms of modeling out all use-cases in the system.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 03, 2004 4:20 pm 
Regular
Regular

Joined: Wed Sep 03, 2003 9:56 pm
Posts: 58
Thanks for the feedback. This is exactly what I was hoping for.

In repsponse to emmanuel's posting.

WRT checking the session cache prior to loading objects, I did look at the Reference documentation and API (JavaDoc) as you recommend, but I'm still not sure under what circumstances cached data is used, and when data is loaded from the database. Can you elaborate?

WRT my statement on lazy loading, if I load a single object that is the root of an extensive object graph, then close the Session object, the entire object graph is loaded (per the relationships defined in the mapping files), even though I have 'lazy=true' defined throughout. This subject was originally posted as:

http://forum.hibernate.org/viewtopic.ph ... ighlight=8

Thanks again for you help.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 03, 2004 5:32 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
mitchellch wrote:
WRT checking the session cache prior to loading objects, I did look at the Reference documentation and API (JavaDoc) as you recommend, but I'm still not sure under what circumstances cached data is used, and when data is loaded from the database. Can you elaborate?

load(), session.iterate(), query.iterate will look into the session cache
session.find(), query.list() will load objects from the DB

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 03, 2004 6:05 pm 
Regular
Regular

Joined: Wed Sep 03, 2003 9:56 pm
Posts: 58
Got it. Thanks.

...but ( :) ), if I load an object via session.find() (which I do), what about the objects on the other end of it's one-to-many association? If any/all of those objects alread exist in the session cache, will they also be reloaded?

Thanks again for you help.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 03, 2004 6:27 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
It will try to find the initialized collection and children objects in cache unless you explicitly fetch children in HQL

_________________
Emmanuel


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.