[ Team LiB ] |
2.5 Object-Relational MappingYou 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:
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:
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:
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 system2.5.1 Inheritance MappingThe 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 rolesAs 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:
2.5.2 Multivalued AttributesCollections 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.
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 ] |