persistent classes
4.1 Understanding entities and value types
Entities are persistent types that represent first-class business objects (the term object is used here in its natural sense). In other words, some of the classes and types you have to deal with in an application are more important, which naturally makes others less important. You probably agree that in CaveatEmptor, Item is a more important class than String. User is probably more important than Address. What makes something important? Let’s look at the issue from a differ-ent perspective.
4.1.1 Fine-grained domain models
A major objective of Hibernate is support for fine-grained domain models, which we isolated as the most important requirement for a rich domain model. It’s one reason why we work with POJOs. In crude terms, fine-grained means more classes than tables.
For example, a user may have both a billing address and a home address. In the database, you may have a single USERS table with the columns BILLING_STREET, BILLING_CITY, and BILLING_ZIPCODE, along with HOME_STREET, HOME_CITY, and HOME_ZIPCODE. (Remember the problem of SQL types we discussed in chapter 1?) In the domain model, you could use the same approach, representing the two addresses as six string-valued properties of the User class. But it’s much better to model this using an Address class, where User has the billingAddress and homeAddress properties, thus using three classes for one table.
This domain model achieves improved cohesion and greater code reuse, and it’s more understandable than SQL systems with inflexible type systems. In
the past, many ORM solutions didn’t provide especially good support for this kind of mapping.
Hibernate 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 pro-vide a sendEmail() method.
This granularity problem leads us to a distinction of central importance in ORM. In Java, all classes are of equal standing—all objects have their own identity and lifecycle.
Let’s walk through an example.
4.1.2 Defining the concept
Two people live in the same apartment, and they both register user accounts in CaveatEmptor. Naturally, each account is represented by one instance of User, so you have two entity instances. In the CaveatEmptor model, the User class has a homeAddress 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? If Address is supposed to support shared runtime references, it’s an entity type. If not, it’s likely a value type and hence is dependent on a single reference by an owning entity instance, which also provides identity.
We advocate a design with more classes than tables: One row represents multi-ple instances. Because database identity is immulti-plemented by primary key value, some persistent objects won’t have their own identity. In effect, the persistence mechanism implements pass-by-value semantics for some classes! One of the objects represented in the row has its own identity, and others depend on that. In the previous example, the columns in the USERS table that contain address infor-mation are dependent on the identifier of the user, the primary key of the table.
An instance of Address is dependent on an instance of User. Hibernate makes the following essential distinction:
■ An object of entity type has its own database identity (primary key value).
An object reference to an entity instance is persisted as a reference in the database (a foreign key value). An entity has its own lifecycle; it may exist independently of any other entity. Examples in CaveatEmptor are User, Item, and Category.
■ An object of value type has no database identity; it belongs to an entity instance and its persistent state is embedded in the table row of the owning
entity. Value types don’t have identifiers or identifier properties. The lifespan of a value type instance is bounded by the lifespan of the owning entity instance. A value type doesn’t support shared references: If two users live in the same apartment, they each have a reference to their own homeAd-dress instance. The most obvious value types are classes like Strings and Integers, but all JDK classes are considered value types. User-defined classes can also be mapped as value types; for example, CaveatEmptor has Address and MonetaryAmount.
Identification of entities and value types in your domain model isn’t an ad hoc task but follows a certain procedure.
4.1.3 Identifying entities and value types
You may find it helpful to add stereotype information to your UML class diagrams so you can immediately see and distinguish 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. See figure 4.1 for an example.
The Item and User classes are obvious entities. They each have their own iden-tity, their instances have references from many other instances (shared refer-ences), and they have independent lifecycles.
Identifying the Address as a value type is also easy: A particular Address instance is referenced by only a single User instance. You know this because the association has been created as a composition, where the User instance has been made fully responsible for the lifecycle of the referenced Address instance.
Therefore, Address objects can’t be referenced by anyone else and don’t need their own identity.
The Bid class is a problem. In object-oriented modeling, you express a compo-sition (the association between Item and Bid with the diamond), and an Item manages the lifecycles of all the Bid objects to which it has a reference (it’s a col-lection of references). This seems reasonable, because the bids would be useless if
Figure 4.1 Stereotypes for entities and value types have been added to the diagram.
an Item no longer existed. But at the same time, there is another association to Bid: An Item may hold a reference to its successfulBid. The successful bid must also be one of the bids referenced by the collection, but this isn’t expressed in the UML. In any 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 lifecycle, but it must have its own identity to support shared references.
You’ll often find this kind of mixed behavior; however, 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: Collections, for exam-ple, sometimes add complexity without offering any advantages. Instead of map-ping a persistent collection of Bid references, you can write a query to obtain all the bids for an Item (we’ll come back to this point again in chapter 7).
As the next step, take your domain model diagram and implement POJOs for all entities and value types. You have to take care of three things:
■ Shared references—Write your POJO classes in a way that avoids shared refer-ences to value type instances. For example, make sure an Address object can be referenced by only one User. For example, make it immutable and enforce the relationship with the Address constructor.
■ Lifecycle dependencies—As discussed, the lifecycle of a value-type instance is bound to that of its owning entity instance. If a User object is deleted, its Address dependent object(s) have to be deleted as well. There is no notion or keyword for this in Java, but your application workflow and user interface must be designed to respect and expect lifecycle dependencies. Persistence metadata includes the cascading rules for all dependencies.
■ Identity—Entity classes need an identifier property in almost all cases. User-defined value-type classes (and JDK classes) don’t have an identifier prop-erty, because instances are identified through the owning entity.
We’ll come back to class associations and lifecycle rules when we discuss more advanced mappings later in the book. However, object identity is a subject you have to understand at this point.