Hibernate, Understanding entities and value types


4.1 - Understanding entities and value types

When you look at your domain model, you’ll notice a difference between classes: some of the types seem more important, representing first-class business objects (the term object is used here in its natural sense). Examples are the Item, Category, and User classes: these are entities in the real world you’re trying to represent (refer back to figure 3.3 for a view of the example domain model). Other types present in your domain model, such as Address, String, and Integer, seem less important. In this section, we look at what it means to use fine-grained domain models and making the distinction between entity and value types.

4.1.1 - Fine-grained domain models

A major objective of Hibernate is support for fine-grained and rich domain models. It’s one reason we work with POJOs. In crude terms, fine-grained means more classes than tables.
For example, a user may have a home address in your domain model. In the database, you may have a single USERS table with the columns HOME_STREET, HOME_CITY, and HOME_ZIPCODE. (Remember the problem of SQL types we discussed in section 1.2.1?)
In the domain model, you could use the same approach, representing the address as three string-valued properties of the User class. But it’s much better to model this using an Address class, where User has a homeAddress property. This domain model achieves improved cohesion and greater code reuse, and it’s more understandable than SQL with inflexible type systems.
JPA emphasizes the usefulness of fine-grained classes for implementing type safety and behavior. For example, many people model an email address as a string-valued property of User. A more sophisticated approach is to define an EmailAddress class, which adds higher-level semantics and behavior—it may provide a prepareMail() method (it shouldn’t have a sendMail() method, because you don’t want your domain model classes to depend on the mail subsystem).
This granularity problem leads us to a distinction of central importance in ORM. In Java, all classes are of equal standing—all instances have their own identity and life cycle. When you introduce persistence, some instances may not have their own identity and life cycle but depend on others. Let’s walk through an example.

4.1.2 - Defining application concepts

Two people live in the same house, and they both register user accounts in Caveat-Emptor. Let’s call them John and Jane.
An instance of User represents each account. Because you want to load, save, and delete these User instances independently, User is an entity class and not a value type. Finding entity classes is easy.
The User class has a homeAddress property; it’s an association with the Address class. Do both User instances have a runtime reference to the same Address instance,
or does each User instance have a reference to its own Address? Does it matter that John and Jane live in the same house?
In figure 4.1, you can see how two User instances share a single Address instance (this is a UML object diagram, not a class diagram). If Address is supposed to support shared runtime references, it’s an entity type. The Address instance has its own life, you can’t delete it when John removes his User account—Jane might still have a reference to the Address.
Now let’s look at the alternative model where each User has a reference to its own homeAddress instance, as shown in figure 4.2. In this case, you can make an instance of Address dependent on an instance of User: you make it a value type. When John removes his User account, you can safely delete his Address instance. Nobody else will hold a reference.
Hence, we make the following essential distinction:
■    You can retrieve an instance of entity type using its persistent identity: for example, a User, Item, or Category instance. A reference to an entity instance (a pointer in the JVM) is persisted as a reference in the database (a foreign key-constrained value). An entity instance has its own life cycle; it may exist independently of any other entity. You map selected classes of your domain model as entity types.
■    An instance of value type has no persistent identifier property; it belongs to an entity instance. Its lifespan is bound to the owning entity instance. A value type instance doesn’t support shared references. The most obvious value types are all JDK-defined classes such as String, Integer, and even primitives. You can also map your own domain model classes as value types: for example, Address and MonetaryAmount.
Figure 4.2 Two User instances each have their own dependent Address.

If you read the JPA specification, you’ll find the same concept. But value types in JPA are called basic property types or embeddable classes.
Identifying entities and value types in your domain model isn’t an ad hoc task but follows a certain procedure.

4.1.3 - Distinguishing entities and value types

You may find it helpful to add stereotype (a UML extensibility mechanism) information to your UML class diagrams so you can immediately recognize entities and value types. This practice also forces you to think about this distinction for all your classes, which is a first step to an optimal mapping and well-performing persistence layer. Figure 4.3 shows an example.
The Item and User classes are obvious entities. They each have their own identity, their instances have references from many other instances (shared references), and they have independent lifespans.
Marking the Address as a value type is also easy: a single User instance references a particular Address instance. You know this because the association has been created as a composition, where the User instance has been made fully responsible for the life cycle of the referenced Address instance. Therefore, Address instances can’t be referenced by anyone else and don’t need their own identity.
The Bid class could be a problem. In object-oriented modeling, this is marked as a composition (the association between Item and Bid with the diamond). Thus, an Item is the owner of its Bid instances and holds a collection of references. At first, this seems reasonable, because bids in an auction system are useless when the item they were made for is gone.
But what if a future extension of the domain model requires a User#bids collection, containing all bids made by a particular User? Right now, the association between Bid and User is unidirectional; a Bid has a bidder reference. What if this was bidirectional?
In that case, you have to deal with possible shared references to Bid instances, so the Bid class needs to be an entity. It has a dependent life cycle, but it must have its own identity to support (future) shared references.
You’ll often find this kind of mixed behavior; but your first reaction should be to make everything a value typed class and promote it to an entity only when absolutely necessary. Try to simplify your associations: persistent collections, for example, frequently add complexity without offering any advantages. Instead of mapping


Figure 4.3 Diagramming stereotypes for entities and value types

Item#bids and User#bids collections, you can write queries to obtain all the bids for an Item and those made by a particular User. The associations in the UML diagram would point from the Bid to the Item and User, unidirectionally, and not the other way. The stereotype on the Bid class would then be <<Value type>>. 
Next, take your domain model diagram and implement POJOs for all entities and value types. You’ll have to take care of three things:
■    Shared references—Avoid shared references to value type instances when you write your POJO classes. For example, make sure only one User can reference an Address. You can make Address immutable with no public setUser() method and enforce the relationship with a public constructor that has a User argument. Of course, you still need a no-argument, probably protected constructor, so Hibernate can also create an instance.
■    Life cycle dependencies—If a User is deleted, its Address dependency has to be deleted as well. Persistence metadata will include the cascading rules for all such dependencies, so Hibernate (or the database) can take care of removing the obsolete Address. You must design your application procedures and user interface to respect and expect such dependencies—write your domain model POJOs accordingly.
■    Identity—Entity classes need an identifier property in almost all cases. Value type classes (and of course JDK classes such as String and Integer) don’t have an identifier property, because instances are identified through the owning entity.

Comments