On Java Development

All things related to Java development, from the perspective of a caveman.

Identity Fields, Composite Keys, Hibernate Queries and the Session Cache

without comments

This page is devoted to tips related to the creation and retrieval of Hibernate entities in an environment having an AS/400, iSeries, i5, etc. using DB/400.

 

Creating entities with composite keys

In an iSeries environment incorporating Hibernate in web application design, odds are very good that physical files will not have identity fields that can be used by Hibernate to identify a unique record. As a consequence normal data fields will have to be identified and then mapped as composite keys. The trick is to identify the fields that make up a unique record. This task will usually begin by identifying a logical file to reveal the key fields. If there are a lot of logical views over the physical the task can get drawn out. Knowledge of the underlying file design and how it relates to other files will help.

While select/omits are often included into logical views along with any additional key fields to order the records, remember that SQL statements use the “where” and “order by” clause to perform the same tasks. These fields will not be candidates for the composite key.

Each HQL/JPQL statement is constructed with the fields making up the composite key along with any “where” and “order by” clause and then incorporated into its own DAO method. Keep in mind the statement refers to the named entity, not the file that may or may not share the entity’s name.

 

Natural IDs

Used concurrently with identity fields, since Hibernate 4.1, natural-id fields can be defined to describe a unique record with which to load entities. Natural-id fields are immutable; they cannot be changed. Objects loaded using natural-id are cached by default. Implement the equals() and hashCode() to compare the natural key properties of the entity.

To support loading using natural id fields as well as by the identity field, the following methods were added to the Session API.

With the new byId() methods, instead of using session.load(Person.class, id), the session.byId(Person.class).getReference(id) can be used. Also, instead of session.get(Person.class, id) the session.byId(Person.class).load(id) can be used. Keep in mind that by using natural-id fields or the id field a single entity will be returned.

 

The Session Cache

The session cache is also known as the first-level cache. It caches objects within the current session and is enabled by default in Hibernate.

Multiple SQL executions (via query.list()) for the same record results in a new execution. However, when loading objects the cache will always be used for subsequent retrieval of the same object.

As the Hibernate docs so state,

Whenever you pass an object to save(), update() or saveOrUpdate(), and whenever you retrieve an object using load(), get(), list(), iterate() or scroll(), that object is added to the internal cache of the Session.

When flush() is subsequently called, the state of that object will be synchronized with the database. If you do not want this synchronization to occur, or if you are processing a huge number of objects and need to manage memory efficiently, the evict() method can be used to remove the object and its collections from the first-level cache.

Ref: 20.3 Managing the Caches

Hibernate requires a key to load objects from the session cache. When a discrete key is available i.e. an identity field or composite key, the preferred method is to load by key instead of a HQL query.
hibernate_cache

 

HQL vs Criteria Query

Shown below is a custom audit file that contains the records for fields having PSNO03=”WA0001415″ and PSFLDN=”CD58″. There are 9 records. The field names mentioned are the field that make up the file’s composite key.
PSPTAUDWA0001415

Below is a simple HQL statement designed to include all records for the supplied billing control value and field name supplied. These fields represent the composite key. An identity field is not available for this file. The only active cache is the session cache.

When not using Hibernate, when a SQL statement executes it is expected that the result set will have nine discrete records for the supplied arguments. However, because Hibernate and its session cache is in control, the first record is returned nine times. The reason is due to the way Hibernate caches entities within the session cache. It does this not by storing each record, but by storing the values of the composite key. In this case, there are two and the values are the same for all records.

When time comes to resolve the full records using the composite keys that have been stored into the session-cache, the first record in the database is then returned for each of the composite keys resulting in 9 duplicate records. This means that for files that do not have identity fields, using HQL to load entities will not give the desired results.

 
The results of the HQL statement above is shown below. Note the duplicate records.
HQLDuplicateEntities

 
Below is an example of a criteria query used for the same file with the same composite key fields. Again, the only active cache is the session cache. The goal is the same; to retrieve all records having the value of the composite key field for which there are nine records. When the criteria query below executes each of the nine individual records are returned.

The results of the Criteria Query is shown below.
CriteriaNonDuplicateEntities
There may be a bit more code, but the results are worth it.

 

SQL Query

While SQL queries can be used to load entities, Hibernate SQL Query bypasses the Hibernate Session cache and queries directly against the database. Therefore, if a SQL query is executed in the same transaction in which a save and/or update has been executed, the saved/updated objects in the Hibernate Session Cache will not be included in the SQL result. Careful consideration should be given about why a SQL query should be used.

 

The Query Cache

Queries that are run frequently using the same parameters can benefit by implementing the Query Cache that will cache the result sets. However, as Hibernate’s documentation indicates,

Caching of query results introduces some overhead in terms of your applications normal transactional processing. For example, if you cache results of a query against Person Hibernate will need to keep track of when those results should be invalidated because changes have been committed against Person. That, coupled with the fact that most applications simply gain no benefit from caching query results, leads Hibernate to disable caching of query results by default. To use query caching, you will first need to enable the query cache.

…by default, individual queries are not cached even after enabling query caching. To enable results caching for a particular query, call org.hibernate.Query.setCacheable(true). This call allows the query to look for existing cache results or add its results to the cache when it is executed.

The query cache does not cache the state of the actual entities in the cache; it caches only identifier values and results of value type. For this reason, the query cache should always be used in conjunction with the second-level cache for those entities expected to be cached as part of a query result cache (just as with collection caching).

Once enabled via the configuration of Hibernate, it is simply a matter of calling setCacheable(true) on your Query or Criteria object. Be aware that caches are not aware of changes made to the persistent store by another application. They can, however, be configured to regularly expire cached data.

 
References
Hibernate Docs-Chapter 11, HQL and JPQL Docs
Hibernate Docs-Section 20.4, The Query Cache
Java Persistence Query Language

Written by admin

February 12th, 2014 at 6:51 pm

Posted in

Leave a Reply

You must be logged in to post a comment.