DekGenius.com
[ Team LiB ] Previous Section Next Section

2.5 Object-Relational Mapping

You now have your data structured for optimal performance and extensibility in your database. To make use of that data, you need to pull it into applications—in our case, Java applications—that manipulate the data. Java is an object-oriented programming language. In other words, it models its problem domain using object-oriented principles. In general, object-oriented principles can be summed up as:


Encapsulation

Encapsulation is the hiding of the data and behavior of a thing behind a limited and well-described interface. In Java terms, the limited and well-described interface is the set of your public methods and attributes.


Abstraction

Abstraction is the modeling of only the essential characteristics of a thing and ignoring or hiding the details of its nonessential characteristics. A Java interface is an example of an abstraction.


Polymorphism

Polymorphism means that a single interface can be used for a generic class of actions rather than a single specific action. The equals( ) method in Object is an example of a polymorphic interface because it means different specific things in different classes even though it generally means testing for equality.


Inheritance

Inheritance is the ability for one thing to take on the behavior and characteristics of another. Java supports inheritances through extending classes.

Though a relational database is a model of a problem domain, it is a different kind of model. Your Java application models behavior and uses data to support that behavior. The database, however, models the data in your problem domain and its relationships. Java application logic is inefficient at determining what actors who have played the president during their career have appeared in films together. Similarly, a database is a poor tool for determining pricing rules for a set of products.

When a Java application needs to save its state to some sort of data storage, it is said to require persistence. Often, complex Java applications persist against a relational database. The use of a relational database for persistence has several advantages:

  • Relational databases are efficient at storing data for later retrieval using complex criteria. You cannot search on your stored objects nearly as efficiently if they are serialized to a filesystem or stored somewhere in XML.

  • Java's JDBC API is simple to learn. Other persistence mechanisms tend to be much harder. Java's file access APIs, for example, are painful to write cross-platform code with.

  • Most people have easy access to a relational database. MySQL and PostgresSQL are freely available to those with limited budgets, and most organizations already have a huge investment in enterprise database engines like Oracle and DB2.

When you attempt to persist your Java objects to a relational database, however, you run into the problem of the object-relational mismatch. The most basic question facing the object-oriented developer using a relational database is how to map relational data into objects. Your immediate thought might be to simply map object attributes to entity attributes. Though this approach creates a solid starting point, it does not create the perfect mapping for two reasons:

  • Unlike relational attributes, object attributes are multivalued. An object stores within itself attributes with multiple simple values as well as direct references to groups of complex objects.

  • The relational model has no natural way of modeling inheritance relationships.

BEST PRACTICE: Normalize data models based on object-relational mapping just as you would normalized any other data model: to 3NF or 4NF.

Figure 2-11 contains a sample class diagram for a system that should persist against a relational database. In the class diagram, you are modeling a person who can play many roles—including the roles of employee and customer. Each employee has many addresses and phone numbers.

Figure 2-11. A simple class diagram for a persistent system
figs/jdbp_0211.gif

2.5.1 Inheritance Mapping

The biggest difficulty in object-relational mapping arises in inheritance mapping—the mapping of class inheritance hierarchies into a relational database. In our class diagram, this problem appears in the structure related to roles. Do you need separate entities for Role, Employee, and Customer? Or does it make more sense to have Employee and Customer entities? Or perhaps just a Role entity will sufffice?

Depending on the nature of your class diagram, all three solutions can work. Figure 2-12 shows three possible data models for supporting the class diagram from Figure 2-11.

Figure 2-12. Three possible data models for roles
figs/jdbp_0212.gif

As you can see from all three data models, the relational model's inability to support abstraction does some serious damage to the application. While the class diagram enables the addition of new roles to the system without impacting the Person class, you cannot accomplish the same flexibility in the data model. All three data models demand some understanding of all possible roles available.

The simplest data model—the one with only a Role entity—must contain all data associated with all possible roles. This approach to object-relational inheritance mapping when the implementation classes contain mostly the same data and the differences among the roles from a data perspective can be summarized through a role type.

On the other hand, if the commonalities in the different roles are not interesting from a data perspective, you can solve the problem with the second data model. In other words, model each role with its own entity and ignore the entire Role abstraction in the database. This approach fails if you need to ask questions like "What roles does this person have?" without necessarily knowing what possible roles can exist.

The third approach is a sometimes unwieldy compromise between the first and second approaches. When you need to deal with the roles both abstractly and concretely, you have to create a relationally artificial structure to model those nuances. In this data model, the entities for the concrete classes—Employee and Customer—borrow their primary keys from the Role entity. Only the Role entity contains the foreign key of Person with whom the role is associated. You can now deal with the roles in an abstract sense by joining with the Role entity, or you can get specific information about individual roles through a complex join.

In short, the proper way of modeling an inheritance relationship depends heavily on what sort of queries you intend to use to retrieve that data. When in doubt, refer to this set of rules to help you in inheritance mapping:


Model only the superclass

  • If your queries rely heavily on data associated with the superclass and

  • If your queries do not rely on data associated with the subclasses and

  • If the subclasses do not contain a significant number of distinct attributes


Model only the subclasses

  • If your queries rely heavily on data associated with the subclasses and

  • If your queries do not rely on data associated with the superclass


Model the superclass and its subclasses

For all other circumstances.

BEST PRACTICE: When modeling OO (object-oriented) inheritance, consider whether your database will be used by non-OO systems. If it is being used by non-OO systems, focus more on the data model and less on mapping the OO concepts to the relational world.

2.5.2 Multivalued Attributes

Collections such as arrays, HashMaps, and ArrayLists present another problem to object-relational mapping. In relational terms, these kinds of object attributes represent multivalued attributes. The solution to this problem starts with the same solution for multivalued attributes in your relational model: create entities to support the multivalued attributes.

That approach is simple enough for collections of objects like the Phone and Address classes in our class diagram. You create a Phone entity with a foreign key of personID and a primary key of personID and type and you are done. The mapping becomes tricky with attributes like int arrays or String collections.

BEST PRACTICE: If your database engine supports SQL3 data types, map multivalued attributes to the SQL ARRAY type.

In our class diagram, we store a list of favorite colors as a String[ ]. The solution again is to handle this mapping in the same way you would handle the normalization of an entity with multivalued attributes: create a new entity. In this case, we would create a FavoriteColor table with personID and color as a primary key.

    [ Team LiB ] Previous Section Next Section