Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Banish Your Resistance to Persistence with the EJB 3.0 Persistence API : Page 4

With the 3.0 version of EJB, Java's guardians have endeavored to make persistence a gentler beast that borrows the best from other ORM frameworks that have long curried favor with the community. Now, learn how to use the new spec, along with JBoss and Maven, to persist Java objects to a relational database.

More Advanced Mapping
The previous example showed how a simple POJO could be persisted to a single table. However, in most real-life scenarios you will be dealing with much more complex domain objects. For example, Figure 3 below shows a class diagram of the domain objects from the example music store application.

Figure 3. Music Store Domain. The music store domain illustrates more complex mapping scenarios including entity references, collections, and inheritance.
Figure 4. Music Store Database. The music store domain uses metadata annotation to provide a mapping to this relational schema.

The examples in this section will illustrate how to map this domain model into the relational schema illustrated in Figure 4.

In comparing these two illustrations, you will notice several fundamental differences. For example, Java objects express multiplicity via collections and a relational database by foreign keys and link tables. These differences are often collectively referred to as the object-relational mismatch and are the problem ORM tools are meant to address.

Relationships between entities come in several flavors. These are one-to-one, one-to-many, many-to-one, and many-to-many. In Java, a one-to-one relationship is simply a reference to an entity. A one-to-many, many-to-one, or many-to-many relationship is a collection of entity references. In order to be properly mapped to its corresponding representation in the database, each relationship must be properly annotated as: @OneToOne, @OneToMany, @ManyToOne, or @ManyToMany. As an example, the many-to-many relationship between genres and artists is illustrated below:

@Entity(access = AccessType.FIELD) public class Genre implements Serializable { @Id(generate = GeneratorType.AUTO) private int id; private String name; private String description; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(table = @Table(name = "genre_artist"), joinColumns = { @JoinColumn(name = "genre_id") }, inverseJoinColumns = { @JoinColumn(name = "artist_id") }) private Set<Artist> artists = new HashSet<Artist>(); ... }

Notice the @JoinTable annotation is used to specify which table and columns are used at a database level to store this many-to-many relationship. Also notice that the example above specifies a fetch type of eager. With eager fetching the elements contained by the entity are fetched immediately with the containing entity. With lazy fetching these elements are not fetched until they are needed. Although lazy fetching is often desirable, it won’t work if the entity is already detached from its persistence context the first time the elements are needed. This is the case in the music store example because the genres are retrieved from the artist in a JSP page executing after control has left the EJB where the transaction is demarcated.

Additionally, each type of relationship can be either unidirectional or bidirectional. In a unidirectional relationship one entity has a reference to another, but the second entity does not have a corresponding reference back to the first. In a bidirectional relationship both entities have references to each other.

All relationships have an owning side. The owning side is used to determine what updates need to be applied to the database. Additionally, bidirectional relationships also have an inverse side. In a one-to-one relationship the owning side is the side with the reference. In a one-to-many relationship the many side must be the owning side. And in a many-to-many relationship either side can be the owning side. Regardless, it is the developer's responsibility to keep both sides of the relationship consistent with one another.

In a bidirectional relationship the inverse side must refer back to the owning side through the mappedBy attribute of the annotation used to define the relationship. This is illustrated below in the Artist-to-Genre relationship, which is the inverse of the previously illustrated Genre-to-Artist relationship.

@Entity(access = AccessType.FIELD) public class Artist implements Serializable { @Id(generate = GeneratorType.AUTO) private int id; private String name; private String biography; @ManyToMany(mappedBy = "artists", fetch = FetchType.EAGER) private Set<Genre> genres = new HashSet<Genre>(); ... }

Notice that the mappedBy attribute refers to the property name of the owning collection, in this case "artists." The type of the related class is determined through the use of generics.

In order to keep both sides of the relationship consistent with each other it is common to assign the owning side the responsibility of keeping the relationship consistent. For example, you could create an addProduct method on the Artist class that would maintain both ends of the relationship.

@Entity(access = AccessType.FIELD) public class Artist implements Serializable { ... @OneToMany(mappedBy = "artist", fetch = FetchType.EAGER) private Set<Product> products = new HashSet<Product>(); public void addProduct(Product product) { products.add(product); product.setArtist(this); } ... }

Notice that in this example the addProduct method not only stores the new product in the Artist object but also sets a reference to itself in the corresponding product. This approach is preferred to calling anArtist.getProducts().add(aProduct) because it properly encapsulates management of the collection within the artist class. This is also consistent with Law of Demeter ("don't talk to strangers"), which would discourage directly manipulating elements within a collection contained in another object.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.