Chapter 7. Implementing domain models – EJB 3 in Action

Chapter 7. Implementing domain models

This chapter covers

  • Domain modeling concepts
  • Entities and entity identity
  • Relationships between entities

Most of today’s enterprise systems save their data into a relational database of some kind. This is why persistence—the process of saving and retrieving data from permanent storage—has been a major application development concern for many decades. As a matter of fact, some authoritative sources claim that a great majority of enterprise development efforts concentrate on the problem of persistence.

Arguably, after JDBC, EJB 2 entity beans have been the most significant groundbreaking solution to the problem of persistence in Java. Unfortunately, many of us who developed entity beans experienced an API that felt overcomplicated, cumbersome, and unpolished. It is pretty fair to say entity beans were the most weakly conceived part of EJB 2. In the past few years, lightweight persistence solutions like Hibernate and TopLink successfully filled the gap left open by entity beans. The EJB 3 Java Persistence API (JPA) brings the innovative ideas created by these popular solutions into the Java EE standard and leaves behind the entity beans paradigm.

Domain modeling is a concept inseparably linked with persistence. In fact, it is often the domain model that is persisted. As a result, it makes good sense to present JPA by breaking things down into four chapters that might mirror the iterative process of developing the domain model and persistence layer of the Action-Bazaar application. We have decided on four convenient development phases: defining, persisting, manipulating, and querying the domain model. In this chapter, we briefly introduce domain modeling, present the ActionBazaar domain model, and implement part of the domain model using the EJB 3 JPA. In chapter 8 we explain how entities in our domain model are persisted into a database by using object-relational mapping. In chapter 9, we manipulate the entities using the EntityManager API. Finally, in chapter 10, we query the persisted entities using the EJB 3 query API.

7.1. Domain modeling and the JPA

Often the first step to developing an enterprise application is creating the domain model—that is, listing the entities in the domain and defining the relationships between them.

In this section we’ll first present a primer on domain modeling. Then we’ll explore the ActionBazaar problem domain and identify actors in a domain model, such as objects, relationships, and cardinality. We’ll provide a brief overview of how domain modeling is supported with the EJB 3 Java Persistence API and then build a simple domain object as a Java class.

7.1.1. Introducing domain models

Although you may have been warned that domain modeling is complex, the idea behind it is pretty simple. In effect, a domain model is a conceptual image of the problem your system is trying to solve. Literally, it is made up of the objects in the “system universe” and the relationships or associations between them. As you can guess, an object in a domain model need not be a physical object but just a concept used by your system. A relationship, on the other hand, is an imaginary link between objects that need to “know” about one another. The critical thing to remember is that the domain model describes the objects and how the objects might relate to one another, but not how a system acts on the objects.

We like to think of a domain model as a set of interlocking toy blocks. Each uniquely shaped block in the set is an object. The shape of each block determines how they fit with one another. Each such “fit” is a relationship. In the end, though, you put together the blocks into whatever configuration sparks your imagination. The master plan for putting together the final results forms the business rules of the application. The business rules are implemented by the session beans and MDBs we discussed in previous chapters, while the persistence API implements the domain model that the business rules act on.

We won’t talk about domain modeling much further than what is needed for explaining the concepts we just introduced. However, we encourage you to explore the topic further by checking out the excellent books written on the subject of domain modeling, most notably Patterns of Enterprise Applications Architecture by Martin Fowler (Addison-Wesley, 2002). UML class diagrams are the most popular method of creating the initial domain model. However, we are going to avoid using formal class diagrams throughout this chapter and in the rest of the book. Instead, we’ll use the simplest diagrams possible, which might have a shallow resemblance to UML.

7.1.2. The ActionBazaar problem domain

Modeling the entire ActionBazaar domain will introduce complexity that we don’t need in order to explain JPA. To avoid this unnecessary complexity, we are going to develop the core functionality of the ActionBazaar application that is directly related to buying and selling items on bid online.


Note

Admittedly, this is a slightly unoriginal example. We considered using an example tangential to the central theme of ActionBazaar but decided against it and remained true to the ActionBazaar core concept.


As figure 7.1 shows, at the heart of it ActionBazaar centers on the following activities:

  • Sellers post an item on ActionBazaar.
  • Items are organized into searchable and navigable categories.
  • Bidders place bids on items.
  • The highest bidder wins.
Figure 7.1. The core functionality of ActionBazaar. Sellers post items into searchable and navigable categories. Bidders bid on items and the highest bid wins.


Note

If you are familiar with use cases and the list looks a lot like use cases, they really are.


In our artificially simplistic scenario, we can pick out the domain objects by scanning the list of activities and looking for nouns: seller, item, category, bidder, bid, and order. Our goal is to identify the domain objects or entities that we want to persist in the database. In the real world, finding domain objects usually involves hours of work and many iterations spent analyzing the business problem. We’ll make our initial diagram by randomly throwing together our objects into figure 7.2.

Figure 7.2. Entities are objects that can be persisted in the database. In the first step you identify entities—for example, entities in the ActionBazaar domain.

Putting in the links between objects that should know about each other (these are the infamously complex domain relationships) will complete our domain model. We encourage you to spend some time looking at figure 7.2 guessing how the objects might be related before peeking at the finished result in figure 7.3.

Figure 7.3. The ActionBazaar domain model complete with entities and relationships. Entities are related to one another and the relationship can be one-to-one, one-to-many, many-to-one, or many-to-many. Relationships can be either uni- or bidirectional.

We won’t spell out every relationship in figure 7.3, since most are pretty intuitive even with the slightly cryptic arrows and numbers. We’ll explain what is going on with the arrows and numbers in just a bit when we talk about direction and multiplicity of relationships. For now, all you need to note is the text describing how objects are related to one another. For example, an item is sold by a seller, a seller may sell more than one item, the item is in one or more categories, each category may have a parent category, a bidder places a bid on an item, and so on. You should also note that although the domain model describes the possibilities for cobbling objects together, it does not actually describe the way in which the objects are manipulated. For instance, although you can see that an order consists of one or more items and is placed by a bidder, you are not told how or when these relationships are formed. But by applying a bit of common sense, it is easy to figure out that an item won through a winning bid is put into an order placed by the highest bidder. These relationships are probably formed by the business rules after the bidding is over and the winner checks out the item won.

We’ll clarify the concepts behind domain model objects, relationships, and multiplicity next before moving on to the JPA.

7.1.3. Domain model actors

Domain modeling theory identifies four domain model “actors”: objects, relationships, the multiplicity of relationships, and the optionality of relationships. Let’s fill in the details that we have left out so far about all four actors.

Objects

From a Java developer perspective, domain objects are closely related to Java objects. Like Java objects, domain objects can have both behavior (methods in Java terms) and state (instance variables in Java). For example, the category domain object probably has the name, creation date, and modification date as attributes. Similarly, a category probably also has the behavior of being renamed and the modification date updated. There are likely hundreds of instances of category domain objects in the ActionBazaar, such as “Junkyard Cars for Teenagers”, “Psychedelic Home Décor from the Sixties”, “Cheesy Romantic Novels for the Bored,” and so on.

Relationships

In Java terms, a relationship is manifested as one object having a reference to another. If the Bidder and BillingInfo objects are related, there is probably a BillingInfo instance variable in Bidder, a Bidder instance variable in BillingInfo, or both. Where the object reference resides determines the direction of the arrows in figure 7.3. If Bidder has a reference to BillingInfo, the arrow should point from Bidder to BillingInfo. Or suppose Item and Bid have references to each other; an Item has Bids on it and Bids are placed on Items. Signifying this fact, the arrow connecting Bid and Item points in both directions in figure 7.3. This is what is meant by a bidirectional relationship or association between Bid and Item as opposed to a unidirectional relationship or association between Seller and BillingInfo. Typically, objects are nouns and relationships are verbs such as has, is part of, is member of, belongs to, and so on.


Rich vs. anemic domain models

As we mentioned, domain models are eventually persisted into the database. It might already be obvious that it is easy to make the domain model objects look exactly like database tables. As a matter of fact, this is exactly why data modeling is often synonymous to domain modeling and DBAs (or data analysts) are often the domain experts. In this mode of thinking, domain objects contain attributes (mapping to database table columns) but no behavior. This type of model is referred to as the anemic model.

A rich domain model, on the other hand, encapsulates both object attributes and behavior and utilizes objected-oriented design such as inheritance, polymorphism, and encapsulation.

An anemic domain model may not necessarily be a bad thing for some applications. For one thing, it is painless to map objects to the database. As a rule of thumb, the richer the domain model is, the harder it is to map it to the database, particularly while using inheritance.


Multiplicity, or cardinality

As you can probably infer from figure 7.3, not all relationships are one-to-one. That is, there may be more than one object on either side of a relationship. For example, a Category can have more than one Item. Multiplicity or cardinality refers to this multifaceted nature of relationships. The multiplicity of a relationship can be:

  • One-to-one— Each side of the relationship may have at most only one object. An Employee object can have only one ID card and an ID card can only be assigned to one employee.
  • One-to-many— A particular object instance may be related to multiple instances of another. For example, an Item can have more than one Bid. Note that, taken from the point of view of a Bid, the relationship is said to be many-to-one. For example, many Bids can be placed by a Bidder in figure 7.3.
  • Many-to-many— If both sides of the relationship can have more than one object, the relationship is many-to-many. For example, an Item can be in more than one Category and a Category can have multiple Items.
Ordinality, or optionality

Ordinality, or optionality, of a relationship determines whether an associated entity exists. For example, we have a bidirectional one-to-one association between User and BillingInfo, and every user need not always have billing information, so the relationship is optional. However, BillingInfo always belongs to a User and hence the optionality for the BillingInfo-User association is false.

Having established the basic concepts of domain modeling, we can now start discussing how the domain model is persisted using the EJB 3 Java Persistence API and actually begin implementing our domain model.

7.1.4. The EJB 3 Java Persistence API

In contrast to EJB 2 entity beans, the EJB 3 Java Persistence API (JPA) is a metadata-driven POJO technology. That is, to save data held in Java objects into a database, our objects are not required to implement an interface, extend a class, or fit into a framework pattern. In fact, persisted objects need not contain a single inline statement of JPA. All we have to do is code our domain model as plain Java objects and use annotations or the XML to give the persistence provider the following information:

  • What our domain objects are (for example, using the @Entity and @Embedded annotations)
  • How to uniquely identify a persisted domain object (for example, using the @Id annotation)
  • What the relations between objects are (for example, using the @OneToOne, @OneToMany, and @ManyToMany annotations)
  • How the domain object is mapped to database tables (for example, using various object-relational mapping annotations like @Table, @Column, or @JoinColumn)

As you can see, although O/R mapping using the JPA (or any other O/R frameworks like Hibernate) is a great improvement over entity beans or JDBC, automated persistence is still an inherently complex activity. The large number of persistence-related annotations and wide array of possible arrangements is a result of this fact. To make it all as digestible as possible, we’ll only cover the first three items in this chapter, leaving the fourth (how the domain object is mapped to database tables) to chapter 8. Moreover, we’ll stray from our pattern of presenting and then analyzing a complete example, since the wide breadth of the persistence API would not yield to the pattern nicely. Instead, we are going to explore the persistence API by visiting each step in our list using specific cases from the ActionBazaar example, analyzing features and intricacies along the way. Not straying from previous chapters, however, we’ll still focus on using annotations, leaving the description of deployment descriptor equivalents for a brief discussion in chapter 11.


SQL-centric persistence: Spring JdbcTemplate and iBATIS

Like many of us, if you are comfortable with SQL and JDBC and like the control and flexibility offered by do-it-yourself, hands-on approaches, O/R in its full-blown, black magic, automated form may not be for you. As a matter of fact, O/R tools like Hibernate and the EJB 3 Java Persistence API (JPA) might seem like overkill, even despite the long-term benefits offered by a higher-level API.

If this is the case, you should give tools like Spring JdbcTemplate and iBATIS a very close look. Both of these tools do an excellent job abstracting out really low-level, verbose JDBC mechanics while keeping the SQL/database-centric feel of persistence intact.

However, you should give O/R frameworks and JPA a fair chance. You just might find that these options make your life a lot easier and OO-centric, freeing you to use your neuron cycles to solve business problems instead.


7.1.5. Domain objects as Java classes

Let’s now get our feet wet by examining some code for JPA. We’ll pick a representatively complex domain object, Category, and see what it might look like in Java code. The Category class in listing 7.1 is a simple POJO class that is a domain object built using Java. This is a candidate for becoming an entity and being persisted to the database. As we mentioned earlier, the Category domain object may have the category name and modification date as attributes. In addition, there are a number of instance variables in the POJO class that express domain relationships instead of simple attributes of a category. The id attribute also does more than simply serving as a data holder for business logic and identifies an instance of the Category object. You’ll learn about identity in the next section.

Listing 7.1. Category domain object in Java

The Category POJO has a number of protected instance fields , each with corresponding setters and getters that conform to JavaBeans naming conventions . In case you are unfamiliar with them, JavaBeans rules state that all instance variables should be nonpublic and made accessible via methods that follow the getXX and setXX pattern used in listing 7.1, where XX is the name of the property (instance variable). Other than name and modificationDate, all the other properties have a specific role in domain modeling and persistence. The id field is used to store a unique number that identifies the category . The items property stores all the items stored under a category and represents a many-to-many relationship between items and categories. The parentCategory property represents a self-referential many-to-one relationship between parent and child categories. The subCategories property maintains a one-to-many relationship between a category and its subcategories.

The Category class as it stands in listing 7.1 is a perfectly acceptable Java implementation of a domain object. The problem is that the EJB 3 persistence provider has no way of distinguishing the fact that the Category class is a domain object instead of just another random Java object used for business logic, presentation, or some other purpose. Moreover, note that the properties representing relationships do not make direction or multiplicity clear. Lastly, the persistence provider also needs to be told about the special purpose of the id property. We’ll start solving some of these problems by using JPA annotations next, starting with identifying the Category class as a domain object.

7.2. Implementing domain objects with JPA

In the previous few sections you learned about domain modeling concepts and identified part of the ActionBazaar domain model. Also, we briefly introduced some commonly used metadata annotations supported by JPA. In this section, you’ll see some of the JPA annotations in action as we implement part of the domain model using the EJB 3 JPA. We’ll start with the @Entity annotation that converts a POJO to an entity. Then you’ll learn about field- and property-based persistence and entity identity. Finally, we’ll discuss embedded objects.

7.2.1. The @Entity annotation

The @Entity annotation marks a POJO as a domain object that can be uniquely identified. You may think of the annotation as the persistence counterpart of the @Stateless, @Stateful, and @MessageDriven annotations. Mark the Category class as an entity as follows:

@Entity
public class Category {
...
public Category() { ... }
public Category(String name) { ... }
...
}

As the code snippet demonstrates, all nonabstract entities must have either a public or a protected no-argument constructor. The constructor is used to create a new entity instance by using the new operator as follows:

Category category = new Category();

One of the coolest features of JPA is that since entities are POJOs, they support a full range of OO features like inheritance and polymorphism, with a few persistence-related nuances thrown in. You can have an entity extend either another entity or even a nonentity class. For example, it would be good design to extend both the Seller and Bidder domain object classes from a common User class, as shown in figure 7.4.

Figure 7.4. Inheritance support with entities. Bidder and Seller entities extend the User entity class.

As the code snippet that follows shows, this class could store information common to all users like the user ID, username, and email address:

Because the parent User class is declared an entity, all the inherited fields like username and email are persisted when either the Seller or Bidder entity is saved. A slightly counterintuitive nuance you should note is that this would not be the case if the User class were not an entity itself. Rather, the value of the inherited properties would be discarded when either Seller or Bidder is persisted. The preceding code snippet also demonstrates an interesting weakness—the User class could be persisted on its own, which is not necessarily desirable or appropriate application behavior. One way to avoid this problem is to declare the User class abstract, since abstract entities are allowed but cannot be directly instantiated or saved. In any case, this is probably better OO design anyway. Since JPA supports entity inheritance, the relationship between entities and queries may be polymorphic. We discuss handling polymorphic queries in chapter 10.

Obviously the ultimate goal of persistence is to save the properties of the entity into the database (such as the name and modification date for the Category entity in listing 7.1). However, things are not as simple as they seem, and there are a few twists regarding entity data persistence that you need to have a good grasp of.

7.2.2. Persisting entity data

An entity, because it is a persistent object, has some state that is stored into the database. In this section we discuss access types, how to define a transient field, and data types supported by JPA.

Field- vs. property-based persistence

An entity maintains its state by using either fields or properties (via setter and getter methods). Although JavaBeans object property-naming conventions have been widely used in the Java platform for a good number of years, some developers consider these conventions to be overkill and would rather access instance variables directly. The good news is that JPA supports this paradigm (whether it should is an open question—we’ll express our viewpoint in a moment). Defining O/R mapping using fields or instance variables of entity is known as field-based access, and using O/R mapping with properties is known as property-based access.

If you want to use field-based access, you can declare all your POJO persisted data fields public or protected and ask the persistence provider to ignore getters/setters altogether. You would only have to provide some indication on at least one of the instance variables that they are to be used for persistence directly. You can do this by using the @Id annotation that we’ll discuss next that applies to either a property or a field. Depending on your inclination, this transparent flexibility may or may not seem a little counterintuitive. In the early releases of the EJB 3 specification, the @Entity annotation had an element named accessType for explicitly specifying the persistence data storage type as either FIELD or PROPERTY. As you’ll see, O/R mapping using XML provides an element named access to specify the access type. However, many developers do not like this element and want the additional flexibility of having the JPA provider dynamically determine the access type based on entity field usage patterns.

The following snippet shows what field-based persistence might look like:

@Entity
public class Category {
@Id
public Long id;

public String name;
public Date modificationDate;

public Category() {}
}

Here, the persistence provider would infer that the id, name, and modification-Date public fields should be persisted since the @Id annotation is used on the id field. The annotations would have been applied to getters if we did not intend to use fields for persistence instead of properties.


Note

Annotations used with a setter method are ignored by the persistence provider for property-based access.


One caveat in choosing between field- and property-based persistence is that both are one-way streets; you cannot mix and match access types in the same entity or in any entity in the POJO hierarchy. Field-based persistence is a one-way street in another important way: you give up the OO benefits of encapsulation/data hiding that you get from getters and setters if you expose the persistence fields to be directly manipulated by clients. Even if you used field-based access, we recommend that you make the fields private and expose the fields to be modified by getter/setter method.

For example, property setters are often used in nontrivial applications to validate the new data being set or to standardize POJO data in some fashion. In our example, we could automatically convert Category names to uppercase in the setName method:

public void setName(String name) {
this.name = name.toUpperCase();
}

In general, we highly recommend that you use field-based access with accessor methods or property-based access. It is much easier to have it and not need it than to find out that you need it later on and have to engage in a large-scale, painful refactoring effort in the face of deadlines.

By default, the persistence provider saves all entity fields or properties that have JavaBeans-style public or protected setters and getters (for example, getName and setName in listing 7.1). In addition, persisted setters and getters cannot be declared final, as the persistence provider may need to override them.

Defining a transient field

If necessary, you can stop an entity property from being persisted by marking the getter with the @Transient annotation. A transient field is typically useful for caching some data that you do not want to save in the database. For example, the Category entity could have a property named activeUserCount that stores the number of active users currently browsing items under the directory or a generatedName field that is generated by concatenating the category ID and name. However, saving this runtime information into the database would not make much sense. You could avoid saving the property by using @Transient as follows:

@Entity
public class Category {
...
@Transient
protected Long activeUserCount;
transient public String generatedName

...
public Long getActiveUserCount() {
return activeUserCount;
}

public void setActiveUserCount(Long activeUserCount) {
this.activeUserCount = activeUserCount;
}
...
}

You could achieve the same effect by using the @Transient tag on the getter when using property-based access.

Note that defining a field with the transient modifier as we’ve done with generatedName has the same effect as the @Transient annotation.

Persistent data types

Before we move on from the topic of persisted POJO data and start exploring identity and relations, we need to discuss exactly what field data can be persisted. Ultimately, persisted fields/properties wind up in a relational database table and have to go through an extremely powerful, high-level API to get there. Because of this fact, there are some restrictions on what data types can be used in a persisted field/property. In general, these restrictions are not very limiting, but you should be aware of them nonetheless. Table 7.1 lists the data types that can be used in a persisted field/property.

Table 7.1. Data types allowable for a persisted field/property

Types

Examples

Java primitives

int, double, long

Java primitives wrappers

java.lang.Integer, java.lang.Double

String type

java.lang.String

Java API Serializable types

java.math.BigInteger, java.sql.Date

User-defined Serializable types

Class that implements java.io.Serializable

Array types

byte[], char[]

Enumerated type

{SELLER, BIDDER, CSR, ADMIN}

Collection of entity types

Set<Category>

Embeddable class

Classes that are defined @Embeddable

We’ll discuss the @Embeddable annotation after we discuss entity identities. For now, think of an embeddable object as a custom data type for an entity that encapsulates persisted data.

We have already touched on the issue of identity when we talked about uniquely identifying the Category domain object through the id property. Let’s now take up the topic of entity identity in greater detail.

7.2.3. Specifying entity identity

Every entity of the domain model must be uniquely identifiable. This requirement is due partly to the fact that at some point entities must be persisted into a uniquely identifiable row in a database table (or set of rows in multiple tables). If you are familiar with the concept of database table primary keys, this should come as no surprise. Without primary keys, you would never be able to uniquely identify and retrieve the data you put into a record since you would not know which row it went into after performing the save! The concept of being able to distinguish different instances of the same object holding a different set of data is not completely alien to object-oriented programming either. Consider the equals method in java.lang.Object, meant to be overridden by subclasses as necessary. This method is the OO equivalent of comparing the primary keys of two distinct database records. In most cases, the equals method is implemented by comparing the data that uniquely identifies instances of the same object from one another. In the case of the Category object, you might imagine that the equals method would look like this:

public boolean equals (Object other) {
if (other instanceof Category) {
return this.name.equals(((Category)other).name)
} else {
return false;
}
}

In this case, we would be assuming that the name instance variable uniquely identifies a Category. The name field therefore is the identity for the Category object. In listing 7.1, however, we choose the id field as the identity for Category. This choice will be more obvious when we talk about mapping the Category object into a database table. As you’ll see, in effect, we choose this instance variable because we get it free from the database as a unique Category identifier, and it is less resource intensive than comparing the java.lang.String name field since it is a numeric java.lang.Long. Another benefit of using the numeric type key is automatic generation. There are several ways of telling the persistence provider where the identity of an entity is stored. Starting with the simplest and ending with the most complex, these are as follows:

  • Using the @Id annotation
  • Using the @IdClass annotation
  • Using the @EmbeddedId annotation

Let’s look at each of these mechanisms next.

The @Id annotation

Using the javax.persistence.Id annotation is the simplest way of telling the persistence provider where the entity identity is stored. The @Id annotation marks a field or property as identity for an entity. Since we are using property-based persistence for the Category entity, we could let the API know that we are using the id property as the identity by applying the @Id annotation to the getId method as in the following code snippet. In the case of field-based persistence, the @Id annotation would have been applied directly to an instance variable instead.

@Entity
public class Category {
...
protected Long id;
...

@Id
public Long getId() {
return this.id;
}

public void setId(Long id) {
this.id = id;
}
...
}

Because the identity we specify will end up in a database primary key column, there are limitations to what data types an identity might have. EJB 3 supports primitives, primitive wrappers, and Serializable types like java.lang.String, java.util.Date, and java.sql.Date as identities. In addition, when choosing numeric types you should avoid types such as float, Float, double, and so forth because of the indeterminate nature of type precision. For example, let’s assume that we are using float data as the identity, and specify 103.789 and 103.787 as the identity values for two separate entity instances. If the database rounds these values to two-digit decimal precision before storing the record, both of these values would map to 103.79 and we would have a primary key violation! Another type you should avoid choosing as identifier is TimeStamp.

An important consideration to note is that using the @Id annotation on its own works only for identities with just one field or property. In reality, you’ll often have to use more than one property or field (known as composite key) to uniquely identify an entity. For sake of illustration, assume that we changed our minds and decided that a Category is uniquely identified by its name and creation date. There are two ways we can accomplish this: by using either the @IdClass or @EmbeddedId annotation.

The @IdClass annotation

In effect, the @IdClass annotation enables you to use more than one @Id annotation in a sensible way. This is the basic problem with using more than one @Id field or property in an entity class: it is not obvious how to compare two instances in an automated fashion. This is especially true since in cases where composite keys are necessary, one or more of the fields that constitute the primary key are often relationship or association fields (or foreign keys in database terminology). For example, although this is not the case for us, the Bid domain object might have an identity consisting of the item to bid on, the bidder, as well as a bid amount:

public class Bid {
private Item item;
private Bidder bidder;
private Double amount;
...
}

In this snippet, both the item and bidder instance variables represent relationship references to other entities. You could be tempted to combine item, bidder, and amount as a composite key for Bid, but remember that it might be the case that neither of the references is simple enough to compare instances using the equals method, as it would be for a java.lang.String or java.lang.Long. This is where a designated IdClass comes in. The best way to understand how this works is through an example.

For simplicity, we’ll return to our Category object with the name and creation date identity. Listing 7.2 shows how the solution might look.

Listing 7.2. Specifying category identity using IdClass

As shown in listing 7.2, the CategoryPK class is designated as the IdClass for Category . The Category class has two identity fields marked by the @Id annotation: name and creationDate . These two identity fields are mirrored in the CategoryPK class . The constructor is used to create an instance of the primary key object. The equals method implemented in CategoryPK compares the two mirrored identity fields to determine if two given identities are equal . The magic here is that at runtime, the persistence provider determines if two Category objects are equal by copying the marked @Id fields into the corresponding fields of the CategoryPK object and using CategoryPK.equals. Note that any IdClass must be Serializable and must provide a valid hashCode implementation . In effect, all that is happening here is that we are specifying exactly how to compare multiple identity fields using an external IdClass . The disadvantage to using @IdClass is the slight redundancy and associated maintainability problems in repeating the definition of identity fields in the entity and the IdClass. In our case the name and createDate fields are defined in both the Category and CategoryPK classes. However, the IdClass approach keeps your domain model clutter free, especially as opposed to the slightly awkward object model proposed by the third approach, which uses the @EmbeddedId annotation.

The @EmbeddedId annotation

Using the @EmbeddedId annotation is like moving the IdClass right into your entity and using the identity fields nested inside it to store entity data. Take a look at what we mean in listing 7.3, which rewrites listing 7.2 using @EmbeddedId.

Listing 7.3. Specifying category identity using EmbeddedId

In listing 7.3, notice that the identity fields, name and createDate, are absent altogether from the Category class. Instead, an Embeddable object instance, categoryPK , is designated as the identity using the @EmbeddedId annotation . The CategoryPK object itself is almost identical to the IdClass used in listing 7.1 and contains the name and createDate fields. We still need to implement the equals and hashCode methods . The only difference is that the @Embedded object need not be Serializable. In effect, the object designated as @EmbeddedId is expected to be a simple data holder encapsulating only the identity fields. Note that the @Id annotation is missing altogether since it is redundant. As a matter of fact, you are not allowed to use Id or IdClass in conjunction with EmbeddedId. As you can see, although this approach saves typing, it is a little awkward to justify in terms of object modeling (even the variable name, categoryPK, is more reminiscent of relational databases than OO). It is a little unwieldy too. Imagine having to write category.categoryPK.name to use the name field for any other purpose than as a primary key, as opposed to using category.name. However, whatever method you choose is ultimately a matter of personal taste.

Unless you really need a composite primary key because you are stuck with a legacy database, we don’t recommend using it and instead recommend a simple generated key (also known as surrogate key) that you’ll learn about in chapter 8.

One concept is critical: identities can only be defined once in an entire entity hierarchy. Having had a sufficient introduction to the idea of entities and identities, you are now ready to explore the @Embeddable annotation in greater detail.

7.2.4. The @Embeddable annotation

Let’s step back a second from the idea of identities into the world of pure OO domain modeling. Are all domain objects always identifiable on their own? How about objects that are simply used as convenient data holders/groupings inside other objects? An easy example would be an Address object used inside a User object as an elegant OO alternative to listing street address, city, zip, and so forth directly as fields of the User object. It would be overkill for the Address object to have an identity since it is not likely to be used outside a User object. This is exactly the kind of scenario for which the @Embeddable annotation was designed. The @Embeddable annotation is used to designate persistent objects that need not have an identity of their own. This is because Embeddable objects are identified by the entity objects they are nested inside and never persisted or accessed on their own. Put another way, Embeddable objects share their identities with the enclosing entity. An extreme case of this is the @EmbeddedId situation where the Embeddable object is the identity. Listing 7.4 contains a user/address example to help you gain a better understanding of the most commonly used Embeddable object semantic patterns.

Listing 7.4. Using embedded objects

In listing 7.4, the embeddable Address object is embedded inside a User entity and shares the identity marked with the @Id annotation . It is illegal for an @Embeddable object to have an identity. Also, the EJB 3 API does not support nested embedded objects. In most cases, embedded objects are stored in the same database record as the entity and are only materialized in the OO world. We’ll show you how this works in chapter 8.

Our discussion of embedded objects rounds out our coverage of domain objects. We’ll take a look at domain object relationships next.

7.3. Entity relationships

Earlier in the chapter, we explored the concepts of domain relationships, direction, and multiplicity. As a review, we’ll summarize those concepts here before diving into the details of how to specify domain relationships using JPA. As you might have noted in our domain object code samples, a relationship essentially means that one entity holds an object reference to another. For example, the Bid object holds a reference to the Item object the bid was placed on. Therefore, a relationship exists between the Bid and Item domain objects. Recall that relationships can be either unidirectional or bidirectional. The relationship between Bidder and Bid in figure 7.3 is unidirectional, since the Bidder object has a reference to Bid but the Bid object has no reference to the Bidder. The Bid-Item relationship, on the other hand, is bidirectional, meaning both the Bidder and Item objects have references to each other. Relationships can be one-to-one, one-to-many, many-to-one, or many-to-many. Each of these relationship types is expressed in JPA through an annotation. Table 7.2 lists the relationship annotations we’ll discuss in the following sections.

Table 7.2. Domain relation types and corresponding annotations

Type of relationship

Annotation

One-to-one

@OneToOne

One-to-many

@OneToMany

Many-to-one

@ManyToOne

Many-to-many

@ManyToMany

We explore each annotation using examples next.

7.3.1. @OneToOne

The @OneToOne annotation is used to mark uni- and bidirectional one-to-one relationships. Although in most systems one-to-one relationships are rare, they make perfect sense for domain modeling. In fact, our ActionBazaar example in figure 7.3 has no one-to-one relationship. However, we can imagine that the User domain object parent to both Seller and Bidder has a one-to-one relationship with a BillingInfo object. The BillingInfo object might contain billing data on a user’s credit card, bank account, and so on. Let’s start by seeing what a unidirectional relationship would look like.

Unidirectional one-to-one

For the time, being, let’s assume that the User object has a reference to the BillingInfo but not vice versa. In other words, the relationship is unidirectional, as shown in figure 7.5.

Figure 7.5. A one-to-one relationship between the User and BillingInfo entities. A User may have at most one instance of the BillingInfo object and the BillingInfo object cannot exist without a User.

Listing 7.5 illustrates this relationship.

Listing 7.5. Unidirectional one-to-one relationship

In listing 7.5, the User class holds a BillingInfo reference in the persisted billingInfo field. Since the billingInfo variable holds only one instance of the BillingInfo class, the relationship is one-to-one. The @OneToOne annotation indicates that the persistence provider should maintain this relationship in the database . Let’s take a closer look at the definition of the @OneToOne annotation to better understand its features:

@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
boolean optional() default true;
String mappedBy() default "";
}

First, note that this annotation can be applied to either fields or properties since the Target is specified to be METHOD, FIELD. We are using field-based persistence for the examples to keep things simple. The targetEntity element tells the persistence provider what the related entity class is. In most cases, this is redundant since the container can infer the class from the class of the field or the return type of the property getter and setter. However, you can specify it explicitly anyway if you prefer. You’ll see a case in which this element is indispensable when we explore one-to-many relations. The cascade and fetch parameters are best discussed after we introduce object-relational mapping in the next chapter. For now, suffice it to say that cascade controls what happens to related data when the relationship is altered or deleted and fetch specifies when and how the related fields are populated from database tables.

Listing 7.6 shows an example of how the @OneToOne annotation might be applied to a property instead of a field.

Listing 7.6. Property-based unidirectional one-to-one relationship

The optional element tells the persistence provider if the related object must always be present. By default, this is set to true, which means that a corresponding related object need not exist for the entity to exist. In our case, not every user always has billing information (for example if the user just signed up), so the relationship is optional and the billing field can sometimes be null. If the optional parameter is set to false, the entity cannot exist if the relationship or association does not hold. In other words, no User without BillingInfo could ever exist. You’ll see the mappedBy parameter in action in the next section when we discuss bidirectional associations.

Bidirectional one-to-one

The real point of having domain relationships between entities is to be able to reach one entity from another. In our previous example, we can easily reach the billing information through the billingInfo reference when we have an instance of a User. In some cases, you need to be able to access related entities from either side of the relationship (admittedly, this is rare for one-to-one relationships). For example, the ActionBazaar application may periodically check for credit card expiration dates and notify users of imminently expiring credit cards. As a result, the application should be able to access user information from a given BillingInfo entity and the User-BillingInfo relationship should really be bidirectional. In effect, bidirectional one-to-one relationships are implemented using two @OneToOne annotations pointing to each other on either side of the bidirectional relationship. Let’s see how this works in listing 7.7 by refactoring the code from listing 7.5.

Listing 7.7. Bidirectional one-to-one relationship

In listing 7.7, the User class still has a relationship to the BillingInfo class through the billingInfo variable . However, in this case the relationship is bidirectional because the BillingInfo class also has a reference to the User class through the user field . The @OneToOne annotation on the user field has two more interesting things going on. The first is the mappedBy="billingInfo" specification . This tells the container that the “owning” side of the relationship exists in the User class’s billingInfo instance variable. The concept of a relationship owner doesn’t originate from domain modeling. It exists as a convenience to define the database mapping for a relationship only once instead of repeating the same mapping for both directions of a relationship. You’ll see this concept in action in chapter 8 when we describe O/R mapping. For now, simply note the role of the mappedBy attribute.

The second interesting feature of the @OneToOne annotation on the user field is that the optional parameter is set to false this time. This means that a BillingInfo object cannot exist without a related User object. After all, why bother storing credit card or bank account information that is not related to an existing user?

7.3.2. @OneToMany and @ManyToOne

As you might have gathered from the ActionBazaar domain model in figure 7.3, one-to-many and many-to-one relationships are the most common in enterprise systems. In this type of relationship, one entity will have two or more references of another. In the Java world, this usually means that an entity has a collection-type field such as java.util.Set or java.util.List storing multiple instances of another entity. Also, if the association between two entities is bidirectional, one side of the association is one-to-many and the opposite side of the association is many-to-one.

In figure 7.6, the relationship between Bid and Item is one-to-many from the perspective of the Item object, while it is many-to-one from the perspective of the Bid. Similar to the one-to-one case, we can mark the owning side of the relationship by using the mappedBy column on the entity that is not the owner of the relationship. We’ll analyze these relationships further by actually coding the Bid-Item relationship (see listing 7.8).

Figure 7.6. Every Item has one or more Bids where more than one Bid may be placed on an Item. Therefore, the relationship between Item and Bid is one-to-many whereas the relationship between Bid and Item is many-to-one.

Listing 7.8. One-to-many bidirectional relationship

One-to-many relationship

Listing 7.8 shows that the Item domain object has a Set of Bid objects that it has references to. To signify this domain relationship, the bids field is marked with a @OneToMany annotation. There are a few nuances about the @OneToMany annotation we should talk about. To explore them, take a quick look at the definition of the annotation:

@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
String mappedBy() default "";
}

As you’ll notice, this is literally identical to the definition of the @OneToOne annotation, including the mappedBy element. As a matter of fact, the only element we need to discuss further is targetEntity. Remember that this element is used to specify the class of the related entity if it is not immediately obvious. In the @OneToMany annotation used in listing 7.8, this parameter is omitted since we are using Java generics to specify the fact that the bids variable stores a Set of Bid objects:

@OneToMany(mappedBy="item")
protected Set<Bid> bids;

Imagine, however, what would happen if we did not use generics on the Set. In this case, it would be impossible for the persistence provider to determine what entity the Item object has a relation to. This is exactly the situation the targetEntity parameter is designed for. We would use it to specify the entity at the other end of the one-to-many relationship as follows:

@OneToMany(targetEntity=Bid.class,mappedBy="item")
protected Set bids;
Many-to-one as owning-side of relationship

Also, note the mappedBy="item" value on the @OneToMany annotation. This value specifies the owning side of the bidirectional relationship as the items field of the Bid entity.

Because the relationship is bidirectional, the Bid domain object has a reference to an Item through the item variable. The @ManyToOne annotation on the item variable tells the persistence provider that more than one Bid entity could hold references to the same Item instance. For bidirectional one-to-many relationships, ManyToOne is always the owning side of the relationship. Because of this fact, the mappedBy element does not exist in the definition of the @ManyToOne annotation:

@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface ManyToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
boolean optional() default true;
}

Other than this minor difference, all the other elements of the @ManyToOne annotation have the same purpose and functionality as the elements in the @OneToOne and @OneToMany annotations.

The last type of domain relationship is many-to-many, which we’ll discuss next.

7.3.3. @ManyToMany

While not as common as one-to-many, many-to-many relationships occur quite frequently in enterprise applications. In this type of relationship, both sides might have multiple references to related entities. In our ActionBazaar example, the relationship between Category and Item is many-to-many, as shown in figure 7.7.

Figure 7.7. The relationship between Category and Item is many-to-many because every category may have one or more items, whereas each item may belong to more than one category.

That is, a category can contain multiple items and an item can belong to multiple categories. For example, a category named “Sixties Fashion” could contain items like “Bellbottom Pants” and “Platform Shoes.” “Bellbottom Pants” and “Platform Shoes” could also be listed under “Uncomfortable and Outdated Clothing.” Although many-to-many relationships can be unidirectional, they are often bidirectional because of their crossconnecting, mutually independent nature. Not too surprisingly, a bidirectional many-to-many relationship is often represented by @ManyToMany annotations on opposite sides of the relationship. As with the one-to-one and one-many relationships, you can identify the owning side of the relationship by specifying mappedBy on the “subordinate” entity; you may have to use the targetEntity attribute if you’re not using Java generics.

The definition for @ManyToMany is identical to OneToMany and holds no special intricacies beyond those already discussed:

@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface ManyToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
String mappedBy() default "";
}

To round off our discussion of many-to-many relationships, listing 7.9 shows how the Item-Category relationship might look.

Listing 7.9. Many-to-many relationship between Category and Items

In listing 7.9, the Category object’s items variable is marked by the @ManyToMany annotation and is the owning side of the bidirectional association. In contrast, the Item object’s categories variable signifies the subordinate bidirectional many-to-many association. As in the case of one-to-many relationships, the @ManyToMany annotation is missing the optional attribute. This is because an empty Set or List implicitly means an optional relationship, meaning that the entity can exist even if no associations do.

As a handy reference, we summarize the various elements available in the @OneToOne, @OneToMany, @ManyToOne, and @ManyToMany annotations in table 7.3.

Table 7.3. Elements available in the @OneToOne, @OneToMany, @ManyToOne, and @ManyToMany annotations

Element

@OneToOne

@OneToMany

@ManyToOne

@ManyToMany

targetEntity

Yes

Yes

Yes

Yes

cascade

Yes

Yes

Yes

Yes

fetch

Yes

Yes

Yes

Yes

optional

Yes

No

Yes

No

mappedBy

Yes

Yes

No

Yes


RIP: container-managed relationships

If you have used EJB 2, you might be familiar with the container-managed relationship (CMR) feature of entity beans with bidirectional relationships. This feature monitored changes on either side of the relationship and updated the other side automatically. CMR is not supported in this version because entities can possibly be used outside of containers. However, mimicking this feature is not too hard using a few extra lines of code. Let us take the User-BillingInfo one-to-one relationship, for example. The code for changing the BillingInfo object for a User and making sure both sides of the relationship are still accurate would look like this:

user.setBilling(billing);
billing.setUser(user);

7.4. Summary

In this chapter, we discussed basic domain modeling concepts: entities, relationships, and how to define them using JPA. The lightweight API makes creating rich, elegant, object-oriented domain models a simple matter of applying annotations or deployment descriptor settings to plain Java objects. The even greater departure from the heavyweight, framework code–laden approach of EJB 2 is the fact that the new persistence API can be separated altogether from the container, as you’ll see in upcoming chapters.

It is interesting to note that the API doesn’t directly control relationship multiplicity. In the case of one-to-one and many-to-one relationships, the optional element somewhat specifies the multiplicity of the relationship. However, in the case of one-to-many and many-to-many relationships, the API does not enforce multiplicity at all. It is instead the responsibility of the programmer to control the size of the collection holding entity references (java.util.Set objects in our examples) and therefore control multiplicity.

In chapter 8 we move on to the next step in building the ActionBazaar persistence layer and show you how to map the entities and relationships we created to the database using object-relational mapping.