Member Menu
 
 Monthly JBoss newsletter:
 
Hibernate Books
CaveatEmptor

Root Persistent Class

Hibernate doesn't require that persistent classes extend a base class. That is not meant to imply that its necessarily a bad thing. You might like to consider defining a base class for your application's persistent classes. For example, the following base class implements the "Active Record" pattern:

public class Persistent implements Lifecycle, Validatable, Serializable {
   
   protected Serializable _id;
   protected int _version;

   public Serializable getIdentifier() {
      return _id;
   }
   public void setIdentifier(Serializable id) {
      _id = id;
   }
   public int getVersion() {
      return _version;
   }
   public void setVersion(int version) {
      _version = version;
   }

   public Long persist() throws HibernateException, SQLException {
      HibernateSession.currentSession().saveOrUpdate(this);
      return _id;
   }
   public void delete() throws HibernateException, SQLException {
      HibernateSession.currentSession().delete(this);
   }
   public void refresh() throws HibernateException, SQLException {
      HibernateSession.currentSession().load(this, _id);
   }
   public void lock() throws HibernateException, SQLException {
      HibernateSession.currentSession().lock(this, LockMode.UPGRADE);
   }

   public boolean onSave(Session s) throws CallbackException {
      return NO_VETO;
   }
   public boolean onDelete(Session s) throws CallbackException {
      return NO_VETO;
   }
   public boolean onUpdate(Session s) throws CallbackException {
      return NO_VETO;
   }
   public void onLoad(Session s, Serializable id) {
      _id = id;
   }

   public void validate() throws ValidationFailure {
   }
}

This implementation uses the HibernateSession class described in Sessions and transactions.

Now Lizard inherits Persistent:

public class Lizard extends Persistent {

    private float _bodyTemperature;
    private float _weight;

    public float getBodyTemperature() { return _bodyTemperature; }
    public void setBodyTemperature(float bodyTemperature) {
        _bodyTemperature = bodyTemperature;
    }
    public float getWeight() { return _weight; }
    public void setWeight(float weight{
        _weight = weight
    }

   public void validate() throws ValidationFailure {
       if (_weight<0) throw new ValidationFailure("negative weight");
   }
}

And is mapped as follows

<class name="Lizard" table="LIZARDS"/>
    <id name="id" unsaved-value="null">
        <generator class="hilo"/>
    </id>
    <version name="version"/>
    <property name="bodyTemperature"/>
    <property name="weight"/>
</class>

Here's a variation that forces the developer to perform a version check before updating an object. This implementation might be useful with the application version checking approach described in the documentation. (As opposed to Hibernate's automatic version checking.)

The "application version checking" approach is where we

  • Load an object in the first transaction and copy its properties (but not the object itself) into the UI layer (using a Data Transfer Object, for example)
  • Modify the copied properties from the UI
  • Reload the object in a new transaction and copy the modified properties "back" to the object

This is typical of any application that manages critical data, such as online banking transactions, where data integrity is vital. Hibernate's built-in version checking is obviously a must, but this additional check ensures the integrity of any data that may have been updated while the end-user was entering changes.

public class Persistent implements Lifecycle, Validatable, Serializable {
   
   protected Long _id;
   protected int _version;
   protected boolean _versionCheckRequired;

   public Long getIdentifier() {
      return _id;
   }
   public void setIdentifier(Long id) {
      _id = id;
   }
   public int getVersion() {
      return _version;
   }
   public void setVersion(int version) {
      _version = version;
   }

   public Long persist() throws HibernateException, SQLException {
      HibernateSession.currentSession().saveOrUpdate(this);
      return _id;
   }
   public void delete() throws HibernateException, SQLException {
      HibernateSession.currentSession().delete(this);
   }
   public void refresh() throws HibernateException, SQLException {
      HibernateSession.currentSession().load(this, _id);
   }
   public void lock() throws HibernateException, SQLException {
      HibernateSession.currentSession().lock(this, LockMode.UPGRADE);
   }
   public void checkVersion(int version) throws StaleObjectException {
      if (version != _version) {
         throw new StaleObjectException();
      }
      _versionCheckRequired = false;
   }

   public boolean onSave(Session s) throws CallbackException {
      return NO_VETO;
   }
   public boolean onDelete(Session s) throws CallbackException {
      return NO_VETO;
   }
   public boolean onUpdate(Session s) throws CallbackException {
      return NO_VETO;
   }
   public void onLoad(Session s, Serializable id) throws CallbackException {
      _versionCheckRequired = true;
      onLoad(s, (Long) id);
   }
   protected void onLoad(Session s, Long id) throws CallbackException {
   }

   public void validate() throws ValidationFailure {
      if (_versionCheckReqired) {
         throw new ValidationFailure("version check is required");
      }
   }
}

One consequence of this pattern is that all of your Persistent subclasses, and all of the business logic that uses them, are tightly coupled to Hibernate via the HibernateException thrown throughout. This can make it very difficult to move away from Hibernate to a different persistence framework.


  NEW COMMENT

refresh method implmementation 07 Nov 2003, 10:57 drj
Why

public void refresh() throws HibernateException, SQLException {
    HibernateSession.currentSession().load(this, _id);
}

instead of 

public void refresh() throws HibernateException, SQLException {
    HibernateSession.currentSession().refresh(this);
}

?
 
This pattern can be used instead of the DAO pattern 14 Jul 2005, 00:50 jweiskotten
The "Root Persistent Class" pattern is a nice alternative to the DAO
pattern. It can be extended by other abstract classes that add more
layers of behavior (such as one that has name and description for config
objects).

You are making the persistence transparent by putting the
Hibernate-specific code in the base class, and you don't have to write
an extra DAO for each persistent object.

Additional transparency could be achieved by delegating all calls to
Hibernate to another class. This way you would only need to change the
delegate if you want to use a different persistence layer that has
similar semantics (like EJB3). Your base class could probably stay the same.

The downside is that your objects need to extend from a base class, and
given Java's single-inheritance "limitation", you lose some flexibility
in architecture. You can always delegate or forward to another object if
you need to, though.
 
Good approach 07 Aug 2005, 12:01 orlando
It's a good approach to have a root class with hibernate "stuff" on it,
but i rather use DAO. The benefit of the DAO pattern is that nothing on
the business layer knows about Hibernate. The disadvantege of the DAO is
that you have to write lot of classes (generally 1 business class, 1 DAO
class). 

Both approaches are good (root class, and DAO), it just depend on the
project and what you like.
 
It's not a conflict with DAO pattern 10 Aug 2005, 01:15 floe
We're using Root Persistent Class pattern and DAO pattern 
together,while our root persistent class have only define some common 
attribute such as id, version, createTime, removed, etc, without 
persistent method.

"why load() instead of refresh()" -- please check hibernate 
documentation for detail. In most usecases, refresh() should be avoid, 
load() is better. load() will load data from db and refresh the 
persistent object with same id in session, too.
 
© Copyright 2006, Red Hat Middleware, LLC. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc. [Privacy Policy]