Hibernate Objects

A Hibernate object, suitable for mapping into a database, is a normal java bean with a number of extra requirements.

There must be a default constructor for the class.

There must be accessors and mutators for all the instance variables of the class. Actually this is overstating the requirement but is a good base rule: read the Hibernate documentation for the full details.

The class should implement Serializable. Strictly speaking, this is not a requirement. However, in practice you will normally want your Hibernate objects to be serializable so that they can be (potentially) migrated around a multiprocessor cluster or saved and restored across a web server reboot etc.

The class should have an id instance variable, usually of type Long. Again this is not a true requirement but it is recommended to use automatically generated surrogate keys and, if so, to use an instance variable called id to hold it. Certainly, alternatives are possible.

The mutator for the id property should be private, not public. Again not a requirement but good practice. You should never update the id property directly but rather rely on Hibernate updating it for you. In practice, it is the value of this field that Hibernate uses to decide if an object has been mapped to a database record or not. Change the property yourself and you could seriously confuse Hibernate.

You should decide on a business key for the object and implement the equals and hashCode methods for it.

You should add any extra type specific constructors (which should leave the id field null) and business rule methods you like.

You should not make the class final if you want to be able to use lazy loading for objects of the class.

The Session

Once you have a Session object, and are executing inside a Transaction, there are a number of ways you can interact with the database.

session.get is used to create a new persistent object by id from the database. It returns null if there was no such object in the database. session.load is similar except that if there was no such object in the database it throws an exception.

Important

Conceptually, these methods do not just get the object requested but also all objects that it refers to through its properties, and, transitively, all objects that they refer to as well, and so on. If the database is large, and there is a path of associations from every object to every other object, then fetching one object could try to load in the entire database. There are two issues to consider:

Controlling what really gets loaded while still making everything work as if everything referred to has been loaded. This is done by a technique of lazy loading using proxies. To specify the use of proxies for a class, the attribute lazy=”true” must be specified for that class in the mapping file.

Using lazy fetching, however, now means that such lazy associations can only be turned into eager associations (by a process called initialisation) within a session. Within a session, simply accessing a non-id property of a persistent (although possibly lazy) object initialises it. But once the object is detached, any request for a lazy and uninitialised property will throw an exception. There is a static method, Hibernate.initialise, which can be used to ensure that a lazy or proxy object is materialised before closing the session, and another Hibernate.isInitialised, to test its initialisation state. However, these methods do not work recursively over the whole object graph. Therefore, the programmer must do one of the following:

Recursively walk over the object graph, initialising objects on the way.

Re-attach the object to a session before de-referencing potentially uninitialised objects.

Load the objects in a query in which the fetch strategy has been changed to an eager one. This runtime mechanism can override the lazy setting for the objects in the mapping file.

Keep the session open until any possible chain of dereferences of the properties of the object has been completed.

The standard advice ([BK05]) is to make all associations lazy by default in the mapping files and override this at runtime, where necessary, with queries that force eager fetching.

Loading parts of the object graph efficiently. The naïve approach would be for Hibernate to simply load the object requested, get the ids of the objects referred to, load them and so on, with each load being a separate SQL query. Hibernate provides that strategy as an option, but a possibly significantly more efficient strategy is also available: that of executing an outer join to get the first object and the objects it refers to in one query. This is specified in the mapping file with the attribute outer-join=”true” on the association elements, i.e., one-to-one many-to-one, one-to-many and many-to-many. For detailed semantics and other parameters of this feature, see the manual http://www.hibernate.org/5.html:Hibernate On Line Documentation.

session.delete will cause the database row corresponding to a persistent (or even a detached!) object to be deleted and the object will become transient. What happens to persistent objects it refers to depends on the cascade properties of the mapping configuration for that reference.

session.save on a transient item will assign it a id and make the object persistent: i.e., ensure it, and any other objects it refers to, get saved to the database. This operation essentially calls an SQL INSERT to be executed. Any further calls to the mutators of the object within the transaction will cause an SQL UPDATE to be invoked.

session.lock and session.update are both intended for reattaching a detached object. Normally you should use session.update which triggers an SQL UPDATE to the database row with id equal to that of the object. Thus if the database and the object disagreed on the values contained, then the object overrides the database. session.lock simply reattaches the object (to the session) without checking or updating the database on the assumption that the database is still fully in synch with the object. Generally, do not use this method unless you are absolutely sure that nothing has changed the database state of the object since it was detached or if it does not matter because you will be overwriting all columns that could have changed anyway later on in the transaction.

session.saveOrUpdate is a convenience method that checks whether the object is transient, in which case it acts like session.save, or detached, in which case it acts like session.update.

Querying

So far, our discussions above show how to fetch an object from the database if we know its id. Obviously we need more powerful querying facilities.

As describe above, one of the simplest ways of querying is simply by invoking a chain of accessor methods on a persistent object:

X x = z.getY().getX()

The most critical type of access that is not covered above is finding an object (or collection of objects) when you have some information about them but not the identifier and you do not have a persistent object available that refers to them. For example, if we want to find the customers whose email address match a given one. Here we can use the session.find method. This method always returns a (possibly empty) list of the appropriate result objects. There are three forms of find:

session.find(queryString) where queryString is a String containing no “?” parameter placeholders. In this case the query takes no parameters and can be executed directly.

session.find(queryString, pObject, pType) where queryString is a String containing one “?” parameter placeholder, pObject is an object of the required type to use as the indicated parameter and pType is the corresponding Hibernate type constant (e.g. Hibernate.STRING, Hibernate.DATE, etc.). In this case the object is substituted into the query in much the same way that JDBC preparedStatement queries have their parameters substituted in.

session.find(queryString, pObjectArray, pTypeArray) where queryString is a String containing one or more “?” parameter placeholders, pObjectArray is an array of objects of the required size and types to use as the indicated parameters and pTypeArray is the corresponding array of Hibernate type constants, which again must match. In this case the objects from the object array are substituted into the query in much the same way that JDBC preparedStatement queries have their parameters substituted in.

Finally, the query string format is not SQL but HQL. HQL is Hibernate’s query language which looks very similar to SQL but where, instead of names of tables and columns, the query uses names of java objects and properties.