Red Hat

In Relation To Hibernate ORM

In Relation To Hibernate ORM

Hibernate 3.2: Transformers for HQL and SQL

Posted by    |       |    Tagged as Hibernate ORM

People using the Criteria API have either transparently or knowingly used a ResultTransformer . A ResultTransformer is a nice and simple interface that allows you to transform any Criteria result element. E.g. you can make any Criteria result be returned as a java.util.Map or as a non-entity Bean.

Criteria Transformers

Imagine you have a StudentDTO class:

public class StudentDTO {
  private String studentName;
  private String courseDescription;
  public StudentDTO() { }

Then you can make the Criteria return non-entity classes instead of scalars or entities by applying a ResultTransformer:

List resultWithAliasedBean = s.createCriteria(Enrolment.class)
  .createAlias("student", "st").createAlias("course", "co")
  .setProjection( Projections.projectionList()
                   .add(""), "studentName" )
                   .add("co.description"), "courseDescription" )
          .setResultTransformer( Transformers.aliasToBean(StudentDTO.class) )

 StudentDTO dto = (StudentDTO)resultWithAliasedBean.get(0);  

This is how ResultTransformer have been available since we introduced projection to the Criteria API in Hibernate 3.

It is just one example of the built in transformers and users can provide their own transformers if they so please.

Jealous programming

Since I am more a HQL/SQL guy I have been jealous on Criteria for having this feature and I have seen many requests for adding it to all our query facilities.

Today I put an end to this jealousy and introduced ResultTransformer for HQL and SQL in Hibernate 3.2.

HQL Transformers

In HQL we already had a kind of result transformers via the (select new syntax, but for returning non-entity beans it only provided value injection of these beans via its constructor. Thus if you used the same DTO in many different scenarios you could end up having many constructors on this DTO purely for allowing the select new functionality to work.

Now you can get the value injected via property methods or fields instead, removing the need for explicit constructors.

List resultWithAliasedBean = s.createQuery(
  "select as studentName," +
  "       e.course.description as courseDescription" +
  "from   Enrolment as e")
  .setResultTransformer( Transformers.aliasToBean(StudentDTO.class))

StudentDTO dto = (StudentDTO) resultWithAliasedBean.get(0);

SQL Transformers

With native sql returning non-entity beans or Map's is often more useful instead of basic Object[]. With result transformers that is now possible.

List resultWithAliasedBean = s.createSQLQuery(
  "SELECT as studentName, co.description as courseDescription " +
  "FROM Enrolment e " +
  "INNER JOIN Student st on e.studentId=st.studentId " +
  "INNER JOIN Course co on e.courseCode=co.courseCode")
  .setResultTransformer( Transformers.aliasToBean(StudentDTO.class))

StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);

Tip: the addScalar() calls were required on HSQLDB to make it match a property name since it returns column names in all uppercase (e.g. STUDENTNAME). This could also be solved with a custom transformer that search the property names instead of using exact match - maybe we should provide a fuzzyAliasToBean() method ;)

Map vs. Object[]

Since you can also use a transformer that return a Map from alias to value/entity (e.g. Transformers.ALIASTOMAP), you are no longer required to mess with index based Object arrays when working with a result.

List iter = s.createQuery(
  "select as studentName," +
  "       e.course.description as courseDescription" +
  "from   Enrolment as e")
  .setResultTransformer( Transformers.ALIAS_TO_MAP )

String name = (Map)("studentName");

Again, this works equally well for Criteria, HQL and native SQL.

Reaching Nirvana of native sql

We still miss a few things, but with the addition of ResultTranformer support for SQL and the other additions lately to the native sql functionality in Hibernate we are close to reach the Nirvana of native sql support.

Combined with StatelessSession you actually now got a very flexible and full powered sql executor which transparently can map to and from objects with native sql without any ORM overhead.

...and when you get tired of managing the sql, objectstate, lifecycles, caching etc. of your objects manually and want to benefit from the power of an ORM then you got it all readily available to you ;)

Pro Hibernate 3 deadlocking example

Posted by    |       |    Tagged as Hibernate ORM

A bug report was recently opened in Hibernate's JIRA stating that Hibernate incorrectly handles deadlock scenarios. The basis for the report was an example in the /Pro Hibernate 3/ book (Chapter 9). For those perhaps not familiar with the term deadlock, the basic gist is that two processes each hold resource locks that the other needs to complete processing. While this phenomena is not restricted to databases, in database terms the idea is that the first process (P1) holds a write lock on a given row (R1) while the second process (P2) holds a write lock on another row (R2). Now, to complete its processing P1 needs to acquire a write lock on R2, but cannot do so because P2 already holds its write lock. Conversely, P2 needs to acquire a write lock on R1 in order to complete its processing, but cannot because P1 already holds its write lock. So neither P1 nor P2 can complete its processing because each is indefinitely waiting on the other to release the needed lock, which neither can do until its processing is complete. The two processes are said to be deadlocked in this situation.

Almost all databases have support to circumvent this scenario by specifying that locks should be timed-out after a certain period of time; after the time-out period, one of the processes is forced to rollback and release its locks, allowing the other to continue and complete. While this works, it is not ideal as it requires that the processes remained deadlocked until the underlying timeout period is exceeded. A better solution is for the database to actively seek out deadlock situations and immediately force one of the deadlock participants to rollback and release its locks, which most databases do in fact also support.

So now back to the /Pro Hibernate 3/ example. Let me say up front that I have not read the book and so do not understand the background discussion in the chapter nor the authors' intent/exceptations in regards to the particular example code. I only know the expectations of a (quite possibly mis-guided) reader. So what this example attempts to do is to spawn two threads that each use their own Hibernate Session to load the same two objects in reverse order and then modify their properties. So the above mentioned reader expects that this sould cause a deadlock scenario to occur. But it does not. Or more correctly, in my running of the example, it typically does not, although the results are inconsistent. Sometimes a deadlock is reported; but the vast majority of runs actually just succeed. Why is that the case?

So here is what really happens in this example code. As I mentioned before, the example attempts to load the same two objects in reverse order. The example uses the entities Publisher and Subscriber. The first thread (T1) loads a given Publisher and modifies its state; it is then forced to wait. The second thread (T2) loads a given Subscriber and modified its state; it is then forced to wait. Then both threads are released from their waiting state. From there, T1 loads the same Subscriber previously loaded by T2 and modifies its state; T2 loads the same Publisher previously loaded by T1 and modifies its state. The thing you need to keep in mind here is that so far neither of these two Sessions have actually been flushed, thus no UPDATE statements have actually occurred against the database at this point. The flush occurs on each Session after each thread's second load and modify sequence. Thus, until that point neither thread (i.e. the corresponding database process) is actually holding any write locks on the underlying data. Clearly, the outcome here is going to depend upon the manner in which the two threads are actually allowed to re-awaken by the underlying threading model, and in particular whether the UPDATE statements from the two sessions happen to get interleaved. If the two threads happen to interleave their requests to the database (i.e. T1's UPDATE PUBLISHER happens first, T2's UPDATE SUBSCRIBER hapens second, etc) then a deadlock will occur; if not interleaved, then the outcome will be success.

There are three ways to inequivocally ensure that lock acquisition errors in the database force one of these two transactions to fail in this example:

  • use of SERIALIZABLE transaction isolation in the database
  • flushing the sesssion after each state change (and the end of the example code's step1() and step2() methods)
  • use of locking (either optimistic or pessimistic)

Seems simple enough. Yet apparently not simple enough for the reader of the /Pro Hibernate 3/ book that opened the previously mentioned JIRA case. After all this was explained to him, he wrote me some ill-tempered, misconception-laden replies in private emails. I am not going to go into all the misconceptions here, but one in particular I think needs to be exposed as many developers without a lot of database background seem to stumble over various concepts relating to transactions. Isolation and locking are not the same thing. In fact, to a large degreee, they actually have completely opposite goals and purposes. Transaction isolation aims to isolate or insulate one transaction from other concurrent transactions, such that operations performed in one transaction do not effect (to varying degrees, based on the exact isolation mode employed) operations performed in others. Locking, on the other hand, has essentially the exact opposite goal; it seeks to ensure that certain operations performed in a transaction do have certain effects on other concurrent transactions. In fact locking really has nothing to do with transactions at all except for the fact that their duration is typically scoped to the transaction in which they are acquired and that their presence/absense might affect the outcome of the different transactions. Perhaps, although I cannot say for sure, this confusion comes from the fact that a lot of databases use locking as the basis for their isolation model. But that is just an implementation detail and some databases such as Oracle, Postgres, and the newest SQL Server have very sophisticated and modern isolation engines not at all based on locking.

Hibernate 3.1.1 released

Posted by    |       |    Tagged as Hibernate ORM

Hibernate 3.1.1 has been released earlier this week. This maintenance release focused on bugfixes and improvements, especially regarding:

  • SQL Server support
  • Native Query support
  • Connection handling

For details check out the release notes

Hibernate 3.1 introduced non OLTP features as well as better environment integration:

  • Custom strategy for session handling through CurrentSessionContext including 2 default implementations (JTA based and ThreadLocal based session handling)
  • more natural and aggressive connection handling in J2EE and J2SE environments
  • command-oriented API (StatelessSession API) for operations on huge number of objects
  • bulk UPDATE, DELETE, INSERT INTO ... SELECT for multi table entities
  • extra lazy collections for huge collection handling
  • use of join fetch on collection through scrollable resultsets through break processing
  • database generated properties (including database timestamps)
  • additional ON clauses for joins
  • dirty checking short-cuts for instrumented classes

Downloads are available here

Hibernate 3.1: Reduced wiring code needed for native sql

Posted by    |       |    Tagged as Hibernate ORM

Over the past few months we have been adding some simplifications to the way you can use and specify native sql queries in Hibernate. Gavin even blogged about some of them earlier , but I thought it were about time we brought some more news on this blog about it.

Auto detect return types and aliases

The newest feature related to native sql is that Hibernate will now auto detect the return types and even aliases of any scalar value from a sql query.

Before you had to do something like:

List l = s.createSQLQuery("SELECT emp.regionCode as region, emp.empid as id, {employee.*} 
                           FROM EMPLOYMENT emp, EMPLOYEE employee ...")
         .addScalar("region", Hibernate.STRING)
         .addScalar("id", Hibernate.LONG)
         .addEntity("employee", Employee.class)
Object[] data = l.get(0);

Today you do not need to specify the type of the scalar values, but simply the alias name so Hibernate knows what data you actually want to extract from the resultset.

List l = s.createSQLQuery("SELECT emp.regionCode as region, emp.empid as id, {employee.*} 
                           FROM EMPLOYMENT emp, EMPLOYEE employee ...")
         .addEntity("employee", Employee.class)
Object[] data = l.get(0); 

If the query is only returning scalar values then addScalar is not needed at all; you just call list() on the query.

List l = s.createSQLQuery("SELECT * FROM EMPLOYMENT emp").list();
Object[] data = l.get(0); 

It of course needs some more processing from Hibernate, but it makes experimentation and some data processing problems easier to do.

No redundant column mappings

Previously when you specified native sql in named queries you had to use the return-property element to (redundantly) specify which column aliases you wanted Hibernate to use for your native sql query. It were redundant because in most cases you would simply just be specifying the exact same columns as you had just done in the class mapping.

Thus it could get pretty ugly and verbose when you were starting to have even just mildly complex mappings such as the following which is from our unit tests for a native sql stored procedure call.

<sql-query name="selectAllEmployees" callable="true">
 <return alias="employement" class="Employment">
 <return-property name="employee" column="EMPLOYEE"/>
 <return-property name="employer" column="EMPLOYER"/>                     
 <return-property name="startDate" column="STARTDATE"/>
 <return-property name="endDate" column="ENDDATE"/>                       
   <return-property name="regionCode" column="REGIONCODE"/>                       
   <return-property name="id" column="EMPID"/>                                            
   <return-property name="salary"> 
    <return-column name="VALUE"/>
    <return-column name="CURRENCY"/>                      
 { call selectAllEmployments() }

In the upcoming Hibernate 3.1 you can do the exact same with loss less code:

<sql-query name="selectAllEmployees" callable="true">
 <return class="Employment"/>
 { call selectAllEmployments() }

or in code (for normal sql):

List l = s.createSQLQuery("SELECT * FROM EMPLOYMENT emp")
Object[] data = l.get(0); 

This also removes the need for always using the curly brackets syntax (e.g. {})to handle the aliasing as long as you are not returning the same entity type more than once per row.

Hope you like it, Enjoy :-)

Pluggable Session management in Hibernate 3.1

Posted by    |       |    Tagged as Hibernate ORM

Steve just committed a new interface and extension point to Hibernate Core. We can finally plug-in custom Session context management into Hibernate. For those of you who already know getCurrentSession() in Hibernate 3.0, this new extension enables the same without a JTA environment.

But how does it work? In a J2EE container we can rely on the scope of the current JTA transaction to bind the Hibernate Session. So whenever you call getCurrentSession() you get exactly that. Outside of a container, however, we don't really know when a Session ends (starting it is easy: the first time you request one).

So, a new interface was needed to allow Hibernate users to provide a current Session. In fact, the interface CurrentSessionContext has exactly this single method you can implement. But you don't have to, there are two default implementations distributed with Hibernate 3.1:

  • The usual behavior for JTA environments, binding the current Session to the current system transaction This works without any extra configuration, just set up Hibernate for managed J2EE use (transaction manager, etc.), and call sessionFactory.getCurrentSession(). No need to flush or close the Session. If you use CMT, transaction demarcation is declarative, if you use BMT, call the methods beginTransaction(), getTransaction().commit(), and getTransaction().rollback() on the current Session. If you want to you can enable JTA context management in your Hibernate configuration by setting the new property current_session_context_class to "jta", but again, it is the default if Hibernate is configured for J2EE.
  • An implementation for typical Hibernate deployment in non-managed environments, binding the current Session to the current thread. However, this implementation needs a little help from you to mark the end of a unit of work (we can't wait for the thread to end). You have to call the static method ThreadLocalSessionContext.unbind().close() to remove the current Session from the thread, and to close it. To enable this context handler, set the current_session_context_class configuration property to "thread".

Of course you can implement your own CurrentSessionContext and name the class in configuration, for example, to implement a Long Session (or in new terms: extended persistence context) pattern with EJBs, storing the disconnected Session in a SFSB between requests and JTA transactions.

The traditional HibernateUtil class can now finally be reduced to a simple startup helper, used to initialize a SessionFactory. This is how a typical data access routine now looks like in CMT, BMT, and non-JTA environments:

Session s = HibernateUtil.getSessionFactory().getCurrentSession();
s.beginTransaction();; // or

ThreadLocalSessionContext.unbind().close(); // Only needed for non-JTA

With HibernateUtil we don't really care where the SessionFactory is coming from, either static singleton or from JNDI. The last line is only needed for the built-in thread context handling outside of an application server, as explained above. The rest of the code is the same everywhere, in all deployment situations. Usually you would unbind and close the Session in some kind of interceptor and encapsulate the last line in some system class that knows when request processing completes. The two lines of code saving an item are equivalent, the purpose here is to show you that you can call getCurrentSession() as many times and in as many different (DAO) classes as you like.

For all of you who don't want to build Hibernate from CVS to try this new feature: I've prepared an updated CaveatEmptor release that includes a snapshot of Hibernate 3.1 from CVS, updated HibernateUtil, and updated DAO/unit test classes. The other Hibernate documentation (and the popular Wiki pages about Session handling) will be updated later, when an actual 3.1 release is available.

Hibernate 3.0 presentations

Posted by    |       |    Tagged as Hibernate ORM

I recently spoke at TheServerSideJavaSymposium and at the New England JUG. My presentations, which cover some new ideas implemented in Hibernate 3.0 are now online:

I find it quite hard to talk about HB3, because there is simply so much new stuff, and a lot of it is somewhat obscure. It's really difficult to communicate what a big step forward this is for us.

Hibernate 3.0 released!

Posted by    |       |    Tagged as Hibernate ORM

Hibernate 3.0 is the world's most sophisticated ORXM (Object/Relational/XML Mapping) solution. Hibernate3 makes it easier than ever before for Java applications to interact with persistent data, allowing a single definition of the transformation between various in-memory representations of the entity data and the relational schema, even in the case of very complex legacy schemas and schemas for historical data or data with visibility rules. Hibernate3 also provides the most comprehensive object/relational query functionality, with three full-featured query facilities: Hibernate Query Language, the newly enhanced Hibernate Criteria Query API, and enhanced support for queries expressed in the native SQL dialect of the database.

Compared to Hibernate 2.1 - the most popular object/relational mapping solution in any language - Hibernate 3.0 offers:

  • Much more flexible O/R mapping: support for exotic association and inheritance mappings, and greater flexibility when working with legacy data.
  • Hibernate3 filters: a unique feature for working with temporal (historical), regional or permissioned data.
  • Unprecendented flexibility for mixing handwritten and generated SQL within a single application or even a single entity: full support for derived entities and attributes defined in the mapping document, full support for overriding any generated SQL statement with handwritten SQL, support for stored procedures.
  • Object/Relational/XML mapping: query XML directly from the database for reporting, replicate data between databases via intermediate XML, externalize entity data as XML when interacting with remote systems.
  • Enhanced ease of use: better defaulting, an unchecked exceptions model, simplified natural (and composite) key support, simplified CMT integration.
  • Enhanced Criteria query API: with full support for projection/aggregation and subselects.
  • Runtime performance monitoring: via JMX or local Java API, including a second-level cache browser.
  • Brand new AST-based HQL parser: bulk update/delete enhancement, better syntax validation.
  • JBoss EJB 3.0 preview: support for annotation-based O/R mappings, full support for EJB-QL 3.0, support for EJB 3.0 persist()/merge() lifecycle, JACC-based security model.
  • Hibernate Tools preview: a full suite of Eclipse plugins for working with Hibernate 3.0, including mapping editor, interactive query prototyping, schema reverse engineering tool.
  • Many new extension points: including a new, extensible, event-driven architecture
  • Documentation enhancements.
  • Brand new test suite, including many useful examples of exotic Hibernate mappings.

A full list of new features may be found at:

Download link:

The Hibernate 3.0 core is 68,549 lines of Java code together with 27,948 lines of unit tests, all freely available under the LGPL, and has been in development for well over a year. We would like to thank the many people who contributed to this release!

The Hibernate Team,

  • Max Rydahl Andersen, JBoss Inc
  • Christian Bauer, JBoss Inc
  • Emmanuel Bernard
  • David Channon
  • Joshua Davis
  • Steve Ebersole, JBoss Inc
  • Michael Gloegl
  • Gavin King, JBoss Inc
  • Anthony Patricio

Hibernate 3.0 goes beta

Posted by    |       |    Tagged as Hibernate ORM

We just released Hibernate 3.0 beta 1. I've no time to list all the many changes since the alpha was released four months ago, let alone everything that is new in Hibernate3, which has been in development for over a year now.

The most exciting new thing from our point of view is the new AST-based HQL parser, written by Joshua Davis. It uses 3 ANTLR grammars to transform HQL/EJBQL to SQL. The work on this is not quite finished, but almost all legacy tests pass. You can try out the new query parser by setting


I'll try to get Joshua to blog about the design of the parser (very cool stuff).

Hibernate3 Join Tricks

Posted by    |       |    Tagged as Hibernate ORM

One of the joys of working on an open source project with commercial competitors is having to implement features that our users simply don't ask for, and probably won't use in practice, just because those competitors try to spin their useless features as a competitive advantage. We realized ages ago that it's really hard to tell people that they don't need and shouldn't use a feature if you don't have it.

Multi-table mappings started out as a good example of that kind of features. We have been repeating the your object model should be at /least/ as fine-grained as your relational schema mantra for years now. Unfortunately, we keep hearing this echo back as Hibernate can't do multitable mappings. Nobody has ever once shown me a truly compelling usecase for multitable mappings in a real application, but apparently, if our competitors are to be believed, it is common to find schemas with attributes of the same entity scattered randomly across several different physical tables. I'll have to take their word on that one. I'm not saying you will /never/ run into this kind of thing and, indeed, I've seen a few borderline cases, though nothing that wasn't at least arguably better represented as an association. But certainly, to my mind, valid usecases for multitable mappings are not something you run into commonly enough for this to be an important feature. Perhaps the difference in perception is due to the fact that only /sane/ organizations use Hibernate.

Anyway, we introduced the <join/> mapping, just so we could tell people not to use it. Actually, it was fun to implement, and helped me make some really nice refactorings to the EntityPersister hierarchy.

Then a funny thing happened. I started to think of all kinds of useful things to do with <join/>, none of which had anything much to do with multitable mappings, as usually understood. And I'm pretty certain that these things were not what the other guys were talking about!

The first application I came up with is a mixed inheritance mapping strategy. Before, you had a choice between <subclass/> and <joined-subclass/> (now also <union-subclass/>), and you had to stick with that one strategy for the whole hierarchy.

It's now possible to write a mapping like this:

<class name="Superclass" 
    <id name="id">.....</id>
    <discriminator column="type" type="int"/>
    <property ...../>
    <subclass name="Subclass" 
        <property .... >
    <subclass name="JoinedSubclass" 
        <join table="child">
            <property ...../>

That's /really/ useful.

The next thing that <join/> can be used for required a little tweak. I added an inverse attribute to the join element, to declare that the joined table should not be updated by the owning entity. Now, it's possible to map an association (link) table - which usually represents a many-to-many association - with one-to-many multiplicity in the domain model. First, we have a basic many-to-many mapping, on the Parent side:

<class name="Parent">
    <set name="children" table="ParentChild" lazy="true">
        <key column="parentId"/>
        <many-to-many column="childId" class="Child"/>

Now, we use a <join> mapping, to hide the association table from the Child end:

<class name="Child">
    <join table="ParentChild" inverse="true">
        <key column="childId"/>
        <many-to-one name="parent" column="parentId"/>

Well, I'm not sure really how useful this is, but I was always jealous of the TopLink guys when they bragged how they could do this, and we got it /almost/ for free!

A third trick was also inspired by TopLink. A number of former TopLink users porting code to Hibernate found that Hibernate's table-per-class mapping strategy has significantly different performance characteristics to TopLink's. Hibernate has what seems to be a unique implementation of the table-per-class mapping strategy, in that no discriminator column is required to achieve polymorphism. Instead, Hibernate performs an outer join across all sublass tables, and checks which primary keys values are null in each returned row of results in order to determine the subclass that the row represents. In most circumstances, this offers an excellent performance balance, since it is not vulnerable to the dreaded N+1 selects problem. Furthermore, it does not require the addition of a type discriminator column to the table of the root class, which really feels extremely unnatural and redundant for this relational model.

An alternative approach, that TopLink uses, is to perform an initial query, check the value of a discriminator column, and then issue an extra query if the row represents a subclass instance. This isn't usually very efficient for shallow inheritance trees, but what we've seen is that some ex-TopLink users have created very deep or wide inheritance trees, in which case Hibernate's strategy can result in a single query with simply too many joins.

So, I added the outer-join attribute to <join/>. Its effect is slightly subtle. Consider the following mapping:

<class name="Foo" table="foos" discriminator-value="0">
    <id name="id">...</id>
    <discriminator column="type" type="int"/>
    <property name="name"/>
    <subclass name="Bar" discriminator-value="1">
        <join table="bars">
            <key column="fooId"/>
            <property name="amount"/>

When we execute a HQL query against the subclass Bar, Hibernate will generate SQL with an inner join between foos and bars. If we query against the superclass Foo, Hibernate will use an outer join.

(Note that you would not write the above mapping in practice; instead you would use <joined-subclass/> and eliminate the need for the discriminator.)

Suppose we set outer-join="false":

<class name="Foo" table="foos" discriminator-value="0">
    <id name="id">...</id>
    <discriminator column="type" type="int"/>
    <property name="name"/>
    <subclass name="Bar" discriminator-value="1">
        <join table="bars" outer-join="false">
            <key column="fooId"/>
            <property name="amount"/>

Now, when we query the subclass, the same SQL inner join will be used. But when we query the superclass, Hibernate won't use an outer join. Instead, it will issue an initial query against the foos table, and a sequential select against the bars table, whenever it finds a row with a discriminator value of 1.

Well, that's not such a great idea in this case. But imagine if Foo had a very large number of immediate subclasses. Then we might be avoiding a query with very many outer joins, in favor of several queries with no joins. Well, perhaps some people will find this useful....

Hibernate3 ready for testing after 1000 days

Posted by    |       |    Tagged as Hibernate ORM

Hibernate3 is now ready for a public test, go get it! It has all (well almost all) features we'll ever need for object/relational mapping, and if it doesn't have it, it's easy to subclass, extend, and implement.

We still have some things left on our TODO for the beta (no release date yet on the final), but it's getting better every day and we might have a very stable first beta. If you want to help, we are still looking for documentation translators.

Incidentally, the Hibernate project is now 1000 days old, if you believe the SourceForge stats . We actually had the Hibernate3 alpha finished for the anniversary, but then Gavin's laptop didn't agree with its owner anymore. At least it was an excuse to finish some website redesign.

P.S. The first copies of Hibernate in Action arrived! Mine was sent to an old address (thats the problem if you need years to finish something) and I'm going to hunt it down now. I already received a Thank You! email from the finder...

back to top