Red Hat

In Relation To Hibernate ORM

In Relation To Hibernate ORM

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)
         .list();
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 ...")
         .addScalar("region")
         .addScalar("id")
         .addEntity("employee", Employee.class)
         .list();
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"/>                      
   </return-property>
 </return>
 { call selectAllEmployments() }
</sql-query>

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() }
</sql-query>

or in code (for normal sql):

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

This also removes the need for always using the curly brackets syntax (e.g. {emp.name})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();
    
s.save(item); // or
HibernateUtil.getSessionFactory().getCurrentSession().save(item);
    
s.getTransaction().commit();

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:

http://hibernate.sourceforge.net/HibernateThreeNEJUG.pdf

http://hibernate.sourceforge.net/HowHibernate3MakesComplexThingsEasy.pdf

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: http://www.hibernate.org/About/RoadMap

Download link: http://www.hibernate.org/Download/DownloadOverview

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

hibernate.query.factory_class=org.hibernate.hql.ast.ASTQueryTranslatorFactor

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" 
        table="parent"
        discriminator-value="0">
    <id name="id">.....</id>
    <discriminator column="type" type="int"/>
    <property ...../>
    ...
    
    <subclass name="Subclass" 
            discriminator-value="1">
        <property .... >
        ...
    </subclass>
    
    <subclass name="JoinedSubclass" 
            discriminator-value="-1">
        <join table="child">
            <property ...../>
            ....
        </join>
    </subclass>
    
</class>

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"/>
    </set>
</class>

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"/>
    </join>
</class>

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"/>
        </join>
    </subclass>
</class>

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"/>
        </join>
    </subclass>
</class>

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

Using Hibernate3 as a JDBC framework

Posted by    |       |    Tagged as Hibernate ORM

There's been a certain amount of noise recently surrounding simple JDBC frameworks like iBATIS. I've liked the idea of iBATIS myself, for use in applications which don't need an object-oriented domain model, and don't work with deep graphs of associated entities in a single transaction. A JDBC framework also makes good sense if you are working with some kind of insane legacy database; ORM solutions tend to assume that associations are represented as nice clean foreign keys with proper referential integrity constraints (Hibernate3 much less so than Hibernate 2.x).

Some people even suggest that JDBC frameworks are a suitable alternative to ORM, even for those systems to which ORM is best suited: object-oriented applications with clean relational schemas. They argue that you are /always/ better off with hand-written SQL than generated SQL. Well, I don't think this is true, not only because the overwhelming bulk of SQL code needed by most applications is of the tedious kind, and simply does not require human intervention, but also because a JDBC framework operates at a different semantic level to ORM. A solution like iBATIS knows a lot less about the semantics of the SQL it is issuing, and of the resulting datasets. This means that there is much less opportunity for performance optimizations such as effficent caching. (By efficient, I am referring mainly to efficient cache /invalidation strategies, which are crucial to the usefulness of the cache/.) Furthermore, whenever we have seen handwritten SQL, we have seen N+1 selects problems. It is extremely tedious to write a new SQL query for each combination of associations I might need to fetch together. HQL helps /significantly/ here, since HQL is much less verbose than SQL. For a JDBC framework to be able to make the kind of optimizations that an ORM can make, it would have to evolve to a similar level of sophistication. Essentially, it would need to become an ORM, minus SQL generation. In fact, we already start to see this evolution taking place in existing JDBC frameworks. This begins to erode one of the stated benefits: the claimed simplicity.

It also raises the following interesting thought: if, by gradually adding stuff, a JDBC framework will eventually end up as ORM, minus SQL generation, why not just take an existing ORM solution like, ooh, um ... Hibernate, maybe ... and subtract the SQL generation?

The Hibernate team has long recognized the need to mix and match generated SQL with the occasional handwritten query. In older versions of Hibernate, our solution was simply to expose the JDBC connection Hibernate is using, so you can execute your own prepared statement. This started to change a while ago, and Max Andersen has recently done a lot of work on this. Now, in Hibernate3, it is possible to write an entire application with no generated SQL, while still taking advantage of all of Hibernate's other features.

Do we really expect or intend people to use Hibernate in this way? Well, not really - I doubt there are many people out there who really enjoy writing tedious INSERT, UPDATE, DELETE statements all day. On the other hand, we do think that quite a few people need to customize the occasional query. But to prove a point, I'll show you how you can do it, if you really want to.

Let's take a simple Person-Employment-Organization domain model. (You can find the code in the org.hibernate.test.sql package, so I'm not going to reproduce it here.) The simplest class is Person; here's the mapping:

<class name="Person" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
    
    <property name="name" not-null="true"/>
    
    <loader query-ref="person"/>
    
    <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
    <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
    <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>

The first thing to notice is the handwritten INSERT, UPDATE and DELETE statements. The ? order of the parameters matches to the order in which properties are listed above (we'll have to eventually support named parameters, I suppose). I guess there is nothing especially interesting there.

More interesting is the <loader> tag: it defines a reference to a named query which is to be used anytime we load a person using get(), load(), or lazy association fetching. In particular, the named query might be a native SQL query, which it is, in this case:

<sql-query name="person">
    <return alias="p" class="Person" lock-mode="upgrade"/>
    SELECT NAME AS {p.name}, ID AS {p.id} FROM PERSON WHERE ID=? FOR UPDATE
</sql-query>

(A native SQL query may return multiple columns of entities; this is the simplest case, where just one entity is returned.)

Employment is a bit more complex, in particular, not all properties are included in the INSERT and UPDATE statements:

<class name="Employment" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
    
    <many-to-one name="employee" not-null="true" update="false"/>
    <many-to-one name="employer" not-null="true" update="false"/>
    <property name="startDate" not-null="true" update="false" 
        insert="false"/>
    <property name="endDate" insert="false"/>
    <property name="regionCode" update="false"/>
    
    <loader query-ref="employment"/>
    
    <sql-insert>
        INSERT INTO EMPLOYMENT 
            (EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, ID) 
            VALUES (?, ?, CURRENT_DATE, UPPER(?), ?)
    </sql-insert>
    <sql-update>UPDATE EMPLOYMENT SET ENDDATE=? WHERE ID=?</sql-update>
    <sql-delete>DELETE FROM EMPLOYMENT WHERE ID=?</sql-delete>
</class>

<sql-query name="employment">
    <return alias="emp" class="Employment"/>
    SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer}, 
        STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
        REGIONCODE as {emp.regionCode}, ID AS {emp.id}
    FROM EMPLOYMENT
    WHERE ID = ?
</sql-query>

The mapping for Organization has a collection of Employments:

<class name="Organization" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
    
    <property name="name" not-null="true"/>
    
    <set name="employments" 
        lazy="true" 
        inverse="true">
        
        <key column="employer"/> <!-- only needed for DDL generation -->
        
        <one-to-many class="Employment"/>
        
        <loader query-ref="organizationEmployments"/>
    </set>
    
    <loader query-ref="organization"/>
    
    <sql-insert>
        INSERT INTO ORGANIZATION (NAME, ID) VALUES ( UPPER(?), ? )
    </sql-insert>
    <sql-update>UPDATE ORGANIZATION SET NAME=UPPER(?) WHERE ID=?</sql-update>
    <sql-delete>DELETE FROM ORGANIZATION WHERE ID=?</sql-delete>
</class>

Not only is there a <loader> query for Organization, but also for its collection of Employments:

<sql-query name="organization">
    <return alias="org" class="Organization"/>
    SELECT NAME AS {org.name}, ID AS {org.id} FROM ORGANIZATION
    WHERE ID=?
</sql-query>

<sql-query name="organizationEmployments">
    <return alias="empcol" collection="Organization.employments"/>
    <return alias="emp" class="Employment"/>
    SELECT {empcol.*}, 
        EMPLOYER AS {emp.employer}, EMPLOYEE AS {emp.employee},
        STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
        REGIONCODE as {emp.regionCode}, ID AS {emp.id}
    FROM EMPLOYMENT empcol
    WHERE EMPLOYER = :id AND DELETED_DATETIME IS NULL
</sql-query>    

When I was writing this code, I really started to feel the advantages of having Hibernate write the SQL for me. In just this simple example, I would have eliminated more than 35 lines of code that I would have to later maintain.

Finally, for ad hoc querying, we can use a native SQL query (a named query, or one embedded in the Java code). For example:

<sql-query name="allOrganizationsWithEmployees">
    <return alias="org" class="Organization"/>
    SELECT DISTINCT NAME AS {org.name}, ID AS {org.id} 
    FROM ORGANIZATION org
    INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ID
</sql-query>

Personally, I prefer to program in Java than in XML, so all this stuff is much too XML-heavy for my liking. I think I'll stick with SQL generation, wherever I can, which is almost everywhere. It's not that I don't like SQL. In fact, I am a great fan of SQL, and just love watching the queries scroll past when I turn Hibernate's logging on. It's just that Hibernate is much better at writing SQL than I am.

Hibernate3 Events

Posted by    |       |    Tagged as Hibernate ORM

Another major change in Hibernate3 is the evolution to use an event and listener paradigm as its core processing model. This allows very fine-grained hooks into Hibernate internal processing in response to external, application initiated requests. It even allows customization or complete over-riding of how Hibernate reacts to these requests. It really serves as an expansion of what Hibernate tried to acheive though the earlier Interceptor, Lifecycle, and Validatable interafaces.

Note: The Lifecycle and Validatable interfaces have been moved to the new classic package in Hibernate3. Their use is not encouraged, as it introduces dependencies on the Hibernate library into the users domain model and can be handled by a custom Interceptor or through the new event model external to the domain classes. This is nothing new, as the same recomendation was made in Hibernate2 usage.

So what types of events does the new Hibernate event model define? Essentially all of the methods of the org.hibernate.Session interface correlate to an event. So you have a LoadEvent, a FlushEvent, etc (consult the configuration DTD or the org.hibernate.event package for the full list of defined event types). When a request is made of one of these methods, the Hibernate session generates an appropriate event and passes it to the configured event listener for that type. Out-of-the-box, these listeners implement the same processing in which those methods always resulted. However, the user is free to implement a customization of one of the listener interfaces (i.e., the LoadEvent is processed by the registered implemenation of the LoadEventListener interface), in which case their implementation would be responsible for processing any load() requests made of the Session.

These listeners should be considered effectively singletons; meaning, they are shared between requests, and thus should not save any state as instance variables. The event objects themselves, however, do hold a lot of the context needed for processing as they are unique to each request. Custom event listeners may also make use of the event's context for storage of any needed processing variables. The context is a simple map, but the default listeners don't use the context map at all, so don't worry about over-writing internally required context variables.

A custom listener should implement the appropriate interface for the event it wants to process and/or extend one of the convenience base classes (or even the default event listeners used by Hibernate out-of-the-box as these are declared non-final for this purpose). Custom listeners can either be registered programatically through the Configuration object, or specified in the Hibernate configuration XML (declarative configuration through the properties file is not supported). Here's an example of a custom load event listener:

public class MyLoadListener extends DefaultLoadEventListener {
    // this is the single method defined by the LoadEventListener interface
    public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType) 
            throws HibernateException {
        if ( !MySecurity.isAuthorized( event.getEntityName(), event.getEntityId() ) ) {
            throw MySecurityException("Unauthorized access");
        }
        return super.onLoad(event, loadType);
    }
}

Then we need a configuration entry telling Hibernate to use our listener instead of the default listener:

<hibernate-configuration>
    <session-factory>
        ...
        <listener type="load" class="MyLoadListener"/>
    </session-factory>
</hibernate-configuration>

Or we could register it programatically:

Configuration cfg = new Configuration();
cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() );
....

Listeners registered declaratively cannot share instances. If the same class name is used in multiple <listener/> elements, each reference will result in a seperate instance of that class. If you need the capability to share listener instances between listener types you must use the programatic registration approach.

Why implement an interface and define the specific type during configuration? Well, a listener implementation could implement multiple event listener interfaces. Having the type additionally defined during registration makes it easier to turn custom listeners on or off during configuration.

Hibernate3 Filters

Posted by    |       |    Tagged as Hibernate ORM

Hibernate3 adds the ability to pre-define filter criteria and attach those filters at both a class and a collection level. What's a pre-defined filter criteria? Well, it's the ability to define a limit clause very similiar to the existing where attribute available on the class and various collection elements. Except these filter conditions can be parameterized! The application can then make the decision at runtime whether given filters should be enabled and what their parameter values should be.

Configuration

In order to use filters, they must first be defined and then attached to the appropriate mapping elements. To define a filter, use the new <filter-def/> element within a <hibernate-mapping/> element:

<filter-def name="myFilter">
    <filter-param name="myFilterParam" type="string"/>
</filter-def>

Then, this filter can be attched to a class:

<class name="myClass" ...>
    ...
    <filter name="myFilter" condition=":myFilterParam = my_filtered_column"/>
</class>

or, to a collection:

<set ...>
    <filter name="myFilter" condition=":myFilterParam = my_filtered_column"/>
</set>

or, even to both (or multiples of each) at the same time!

Usage

In support of this, a new interface was added to Hibernate3, org.hibernate.Filter, and some new methods added to org.hibernate.Session. The new methods on Session are: enableFilter(String filterName), getEnabledFilter(String filterName), and disableFilter(String filterName). By default, filters are not enabled for a given session; they must be explcitly enabled through use of the Session.enabledFilter() method, which returns an instance of the new Filter interface. Using the simple filter defined above, this would look something like:

session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");

Note that methods on the org.hibernate.Filter interface do allow the method-chaining common to much of Hibernate.

Big Deal

This is all functionality that was available in Hibernate before version 3, right? Of course. But before version 3, this was all manual processes by application code. To filter a collection you'd need to load the entity containing the collection and then apply the collection to the Session.filter() method. And for entity filtration you'd have to write stuff that manually modified the HQL string by hand or a custom Interceptor.

This new feature provides a clean and consistent way to apply these types of constraints. The Hibernate team envisions the usefulness of this feature in everything from internationalization to temporal data to security considerations (and even combinations of these at the same time) and much more. Of course it's hard to envision the potential power of this feature given the simple example used so far, so let's look at some slightly more in depth usages.

Temporal Data Example

Say you have an entity that follows the effective record database pattern. This entity has multiple rows each varying based on the date range during which that record was effective (possibly even maintained via a Hibernate Interceptor). An employment record might be a good example of such data, since employees might come and go and come back again. Further, say you are developing a UI which always needs to deal in current records of employment data. To use the new filter feature to acheive these goals, we would first need to define the filter and then attach it to our Employee class:

<filter-def name="effectiveDate">
    <filter-param name="asOfDate" type="date"/>
</filter-def>

<class name="Employee" ...>
    ...
    <many-to-one name="department" column="dept_id" class="Department"/>
    <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
    <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
    ...
    <!--
        Note that this assumes non-terminal records have an eff_end_dt set to a max db date
        for simplicity-sake
    -->
    <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>

<class name="Department" ...>
    ...
    <set name="employees" lazy="true">
        <key column="dept_id"/>
        <one-to-many class="Employee"/>
        <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
    </set>
</class>

Then, in order to ensure that you always get back currently effective records, simply enable the filter on the session prior to retrieving employee data:

Session session = ...;
session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
        .setLong("targetSalary", new Long(1000000))
        .list();

In the HQL above, even though we only explicitly mentioned a salary constraint on the results, because of the enabled filter the query will return only currently active employees who have a salary greater than a million dollars (lucky stiffs).

Even further, if a given department is loaded from a session with the effectiveDate filter enabled, its employee collection will only contain active employees.

Security Example

Imagine we have an application that assigns each user an access level, and that some sensitive entities in the system are assigned access levels (way simplistic, I understand, but this is just illustration). So a user should be able to see anything where their assigned access level is greater than that assigned to the entity they are trying to see. Again, first we need to define the filter and apply it:

<filter-def name="accessLevel">
    <filter-param name="userLevel" type="int"/>
</filter-def>

<class name="Opportunity" ...>
    ...
    <many-to-one name="region" column="region_id" class="Region"/>
    <property name="amount" type="Money">
        <column name="amt"/>
        <cloumn name="currency"/>
    </property>
    <property name="accessLevel" type="int" column="access_lvl"/>
    ...
    <filter name="accessLevel"><![CDATA[:userLevel >= access_lvl]]></filter>
</class>

<class name="Region" ...>
    ...
    <set name="opportunities" lazy="true">
        <key column="region_id"/>
        <one-to-many class="Opportunity"/>
        <filter name="accessLevel"><![CDATA[:userLevel >= access_lvl]]></filter>
    </set>
    ...
</class>

Next, our application code would need to enable the filter:

User user = ...;
Session session = ...;
session.enableFilter("accessLevel").setParameter("userLevel", user.getAccessLevel());

At this point, loading a Region would filter its opportunities collection based on the current user's access level:

Region region = (Region) session.get(Region.class, "EMEA");
region.getOpportunities().size(); // <- limited to those accessible by the user's level

Conclusion

These were some pretty simple examples. But hopefully, they'll give you a glimpse of how powerful these filters can be and maybe sparked some thoughts as to where you might be able to apply such constraints within your own application. This can become even more powerful in combination with various interception methods, like web filters, etc. Also a note: if you plan on using filters with outer joining (either through HQL or load fetching) be careful of the direction of the condition expression. Its safest to set this up for left outer joining; in general, place the parameter first followed by the column name(s) after the operator.

back to top