DatabaseTen Tips to Working with Hibernate

Ten Tips to Working with Hibernate

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

1. Hibernate Configuration

There are two ways to go in configuring Hibernate in your application. You can go for XML or a complete, annotation-based configuration. Although the annotation method is the most preferable way, XML has its own advantage as well. Annotation-based configurations are hard coded into Java and transformed into bytecode once compiled. XML helps isolate POJO from configuration, so to reflect any changes made in the configuration there is no need to recompile POJOs. Programmers love annotation because they do not obstruct the flow of coding by jumping from Java to XML back to Java. Moreover, annotation is best suited for a static configuration whereas XML is more dynamic and powerful. So, use your judicious choice to determine which way to go; preferably, the data will flow with the requirement. A typical example of XML configuration is as follows.

<?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE hibernate-configuration PUBLIC
      "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
      "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
   <session-factory>
      <!-- Database connection settings -->
      <property name="connection.driver_class">
         com.mysql.jdbc.Driver
      </property>
      <property name="connection.url">
         jdbc:mysql://localhost:3306/mydatabase
      </property>
      <property name="connection.username">user1</property>
      <property name="connection.password">secret</property>
      <!-- JDBC connection pool (use the built-in) -->
      <property name="connection.pool_size">1</property>
      <!-- SQL dialect -->
      <property name="dialect">
         org.hibernate.dialect.MySQLDialect
      </property>
      <!-- Echo all executed SQL to stdout -->
      <property name="show_sql">true</property>
      <!-- <property name="hbm2ddl.auto">create</property> -->
      <!-- Mapping files -->
      <mapping class="org.mano.entity.Product" />
      <mapping class="org.mano.entity.Supplier" />
      <property name="hibernate.cache.use_second_level_cache">
         true
      </property>
      <property name="hibernate.cache.use_query_cache">
         true
      </property>
      <property name="hibernate.cache.region.factory_class">
         net.sf.ehcache.hibernate.EhCacheRegionFactory
      </property>
   </session-factory>
</hibernate-configuration>

2. Use Fine Grained Classes

This idea is more helpful towards designing entity classes than anything specific to the Hibernate framework. But still, they provide some important clues when working with Hibernate. The idea is to fine grain your classes as much as possible. The class may not be directly eligible to be an entity class, yet can be used as an embedded object. For example, we can use the Address class to encapsulate streetNumber, province, country, zipCode, and so forth, or use the Phone class to encapsulate code, and number properties. These classes can be embedded into other classes as and when required. This idea simplifies refactoring and encourages code reuse.

3. Compound Primary Keys

Creating single column keys is simple, but sometime we have to create a key containing multiple columns. This requires a different strategy. In this case, we must define a class that will not have a primary key of its own but must be a public class with a default constructor, be serializable, and must implement hashCode() and equals() methods to allow Hibernate to test for primary key collision. There are three strategies to achieve this: using @Embeddable and @Id or @EmbeddableId or @IdClass and @id annotation appropriately. An example with @Embeddable and @Id annotation is given below.

public class Account {
   @Id
   private AccountNumber accountNumber;
   ...
}

@Embeddable
public class AccountNumber {
   private String code;
   private Integer number;

   public AccountNumber() {
   }

   @Override
   public int hashCode() {
      int hashCode = 0;
      if (code != null)
         hashCode ^= code.hashCode();
      if (number != null)
         hashCode ^= number = hashCode();
      return hashCode;
   }

   @Override
   public boolean equals(Object obj) {

      if (!(obj instanceof AccountNumber))
         return false;
      AccountNumber acno = (AccountNumber) obj;
      return ((this.code == null) ?
         (acno.code == null) : this.code
      .equals(acno.code))&& ((this.number == null) ? 
      (acno.number == null) : this.number
      .equals(acno.number));
   }

}

4. Bidirectional Association

When creating an association between two or more entity classes in Hibernate, preference should be given to create a bidirectional relationship between them because it is not only difficult to query unidirectional associations, but also, it is de facto in business application to make an association navigable in both directions. This small improvement can save a lot of code down the line. For example:

@Entity
public class Supplier{
   @OneToMany
   private List<Product> products = new ArrayList<>();
   ...
}

@Entity
public class Product{
   @ManyToOne
   private Supplier supplier;
   ...
}

5. Choice of Queries

Hibernate provides several different ways to query for objects stored in the database; for example, native SQL queries, HQL queries, and Criteria API. HQL is an object-oriented query language, similar to native SQL, which is used to retrieve objects that match the query. It is the most used type of query in Hibernate because it makes the typical SQL query smaller and easier to understand. Criteria Query API is a Java API for constructing a query object. It is typically suitable for dynamic query building, such as we may find in building queries for a multi-parameter search form. Almost like a Lego construction, the Criteria API helps us build a complex query block by block. Native SQL is a good fit for some stored procedure work, batch jobs, or for some legacy applications that use SQL features only. It provides a way to execute SQL directly against the database to retrieve objects. However, in terms of SQL generation overhead, Criteria API is the heaviest whereas HQL is lighter, and native SQL is the lightest. This overhead, however, does not make much of a difference in most cases except where tuning to the fraction of time matters. Although all of them do the same job, one may suit better than another in a given situation. The choice rests on its judicious use by the programmer.

6. Caching and Flushing

A Hibernate Session object tries to load an entity by default from the first-level cache, uniquely associated with a session. If the entity cannot be found in the first level, the second level cache is looked up. Now, if the entity is found, it is moved back to the first-level cache before returning the object to the caller so that in a subsequent invocation this particular entity is returned back from the first level. In case the entity is still not found in the second level, the database query is executed and the result is simultaneously stored in both caches before responding back to the caller. However, the problem is that any changes made to the database directly is not reflected in the cached entity, at least, until timeToLiveSeconds has passed. In such a situation, existing caches need to be discarded and rebuily afresh. This invalidation is done with the help of flushing. There are many third-party, second-level cache providers; one of them, EhCache, comes with Hibernate. To use the second level, it needs to be enabled explicitly in the configuration file.

<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.region.factory_class">
   net.sf.ehcache.hibernate.EhCacheRegionFactory
</property>

Now, for example, to enable cache entries for the domain object, we can use the annotation @Cache as follows:

@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class Product implements Serializable {
   ...
}

7. Session Management

A session should be used as per request; that is, before handling any transaction request, a session object should be explicitly opened, and then perform the required operation and close it explicitly. Having one session for an entire application is not a good idea. Because Hibernate inherently uses a first-level cache, a lot of entities get clogged up there. This used-up memory can create havoc and, worse, crash the entire application. Moreover, Hibernate sessions are not thread safe, so passing a session object into a new thread is not a good idea either. The rule of thumb is to treat this hypersensitive Hibernate object in isolation within s try…catch, with explicit opening and closing statements.

8. Proxies and Lazy Loading for Associations

Lazy loading should be the preferred loading strategy for associations in most occasions for performance reasons. Lazy loading is an object fetching strategy from the database provided by Hibernate. If we use XML configuration mapping, this is enabled by default but not when we use annotation-based mapping. Lazy loading ensures that an entity’s associated entities will be loaded only when they are requested. For example, the following code loads only a single entity from the database.

Product product=(Product)session.get(Product.class, new Integer(101));

But, with the lazy loading effect, the associations are pulled from the database only when they are explicitly referenced, as shown below.

Product product=(Product)session.get(Product.class, new Integer(101));
Supplier supplier=product.getSupplier();

This is accomplished with the help of proxy implementation where calls to the entity are intercepted by substituting for it a proxy derived from the entity’s class. The entity class is loaded from the database before the control is delivered to the parent entity’s implementation.

9. Using SQL Formula-based Properties

Use aggregate functions provided by Hibernate rather than re-inventing the code. Often, we find that a property of an entity class persisted in the database not directly as a data but as a function performed on that data. In such a case, instead of managing the operation directly by the Java logic, maintain it as an aggregate function of some other property.

10. Glimpse of Locking

Locking is one of the fundamental concepts of database programming. It is a mechanism to prevent simultaneous access to data to ensure its integrity and isolation. In Hibernate programming, the underlying database takes the responsibility of this heavy lifting and conforms to various levels of isolation in a number of ways. Hibernate simply provides some support for this feature and takes it a little farther by including some additional degrees of isolation obtainable from its own cache. The LockMode object is the key object for accomplishing this isolation. There are several lock modes which can be applied with the help of the session.lock() method.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories