Hibernate.orgCommunity Documentation

Chapter 2. Domain Model

Table of Contents

2.1. POJO Domain Models
2.1.1. Implement a no-argument constructor
2.1.2. Provide identifier attribute(s)
2.1.3. Prefer non-final classes
2.1.4. Declare getters and setters for persistent attributes
2.1.5. Implementing equals() and hashCode()
2.2. Dynamic models

The term domain model comes from the realm of data modeling. It is the model that ultimately describes the problem domain you are working in. Sometimes you will also hear the term "persistent classes".

Ultimately the application's domain model is the central character in an ORM. They make up the classes you wish to map. Hibernate works best if these classes follow the Plain Old Java Object (POJO) / JavaBean programming model. However, none of these rules are hard requirements. Indeed, Hibernate assumes very little about the nature of your persistent objects. You can express a domain model in other ways (using trees of java.util.Map instances, for example).

Note

Even though Hibernate does not consider all of these rules as hard requirements, JPA does specify some of them. Therefore, if you are concerned about JPA provider portability it is best to stick to the strict POJO model. We will point out these concerns where applicable.

This chapter will describe the characteristics of a persistable domain model. However, it will not discuss defining the mapping for the domain model. That is a massive topic in its own right and is the subject of an entire dedicated manual. See the Hibernate Domain Model Mapping Guide from the documentation site.

This section explores domain models defined as POJOs.

Whether to implement equals() and hashCode() methods in your domain model, let alone how to implement them, is a surprisingly tricky discussion when it comes to ORM.

There is really just one absolute case: a class that acts as an identifier must implement equals/hashCode based on the id value(s). Generally this is pertinent for user classes used as composite identifiers. Beyond this one absolute case and the few others we will discuss below, you may want to consider not implementing equals/hashCode.

So what's all the fuss? Normally, most Java objects provide a built-in equals() and hashCode() based on the object's identity, so each new object will be different from all others. This is generally what you want in ordinary Java programming. Conceptually however this starts to break down when you start to think about the possibility multiple instances of a class representing the same data which is in fact the case when we start dealing with data from a database. Every time we load a specific Person from the database we would naturally get a unique instance. Hibernate, however, works hard to make sure that does not happen within a given Session. In fact Hibernate guarantees equivalence of persistent identity (database row) and Java identity inside a particular session scope. So if we ask a Hibernate Session to load that specific Person multiple times we will actually get back the same instance:


Consider another example using a persistent java.util.Set:


However, the semantic changes when we mix instances loaded from different Sessions:


Specifically the outcome in this last example will depend on whether the Person class implemented equals/hashCode, and if so how.

Consider yet another case:


In cases where you will be dealing with entities outside of a Session (whether they be transient or detached), especially in cases where you will be using them in Java collections, you should consider implementing equals/hashCode.

A common initial approach is to use the entity's identifier attribute as the basis for equals/hashCode calculations:


It turns out that this still breaks when adding transient instance of Person to a set as we saw in the last example:


The issue here is a conflict between (1) the use of generated identifier and (2) the contract of Set and (3) the equals/hashCode implementations. Set says that the equals/hashCode value for an object should not change while it is part of the Set. But that is exactly what happened here because the equals/hasCode are based on the (generated) id, which was not set until the session.getTransaction().commit() call.

Note that this is just a concern when using generated identifiers. If you are using assigned identifiers this will not be a problem, assuming the identifier value is assigned prior to adding to the Set.

Another option is to force the identifier to be generated and set prior to adding to the Set:


But this is often not feasible.

The final approach is to use a "better" equals/hashCode implementation, making use of a natural-id or business-key.


As you can see the question of equals/hashCode is not trivial. Nor is there a one-size-fits-all solution.

Persistent entities do not necessarily have to be represented as POJO/JavaBean classes. Hibernate also supports dynamic models (using Maps of Maps at runtime). With this approach, you do not write persistent classes, only mapping files.

Note

The mapping of dynamic models is beyond the scope of this document. We will discuss using such models with Hibernate, but for mapping see the Hibernate Domain Model Mapping documentation.

A given entity has just one entity mode within a given SessionFactory. This is a change from previous versions which allowed to define multiple entity modes for an entity and to select which to load. Entity modes can now also be mixed within a domain model; a dynamic entity might reference a POJO entity, and vice versa.


The main advantage of dynamic models is quick turnaround time for prototyping without the need for entity class implementation. The main down-fall is that you lose compile-time type checking and will likely deal with many exceptions at runtime. However, as a result of the Hibernate mapping, the database schema can easily be normalized and sound, allowing to add a proper domain model implementation on top later on.

It is also interesting to note that dynamic models are great for certain integration use cases as well. Envers, for example, makes extensive use of dynamic models to represent the historical data.