ODBMS.ORG - Object Database Management Systems
ODBMS.ORG - The Resource Portal for Education and Research

SPONSORED BY

SEARCH

NEWS

EVENTS

Avoiding the Quagmire

Offering solutions to the problems of Object/Relational-Mapping
By Ted Neward, May 21, 2007

This article is available for download as PDF

For almost a full decade now, the Java community has struggled with the Object/Relational impedance mismatch, a fundamental problem for those who would seek to store rich domain objects into a relational database. While this problem is not unique to Java—C++ developers wrestled with the problem long before Java was available, and .NET developers are just beginning to discover the depths of the issues involved—it is in Java that this particular battle has been fought most publicly and extensively. No less than four “standards” have been offered up as solutions (EJB Entity Beans, Java Data Objects, Java Persistence Architecture, and the aborted data-mapping solution proposed for Java6), and countless open-source projects have all attempted their own solutions, the most famous of which include Hibernate and Castor JDO. .NET developers are finding similar kinds of solutions, both from Microsoft (in the form of LINQ and language enhancements to C# and VB) and from the open-source community (in the form of NHibernate) and commercial sector.

As discussed in the summation of the essay "The Vietnam of Computer Science" (Link | Download as PDF), there are several potential solutions regarding the potential quagmire of the object/relational problem:

  • Abandoment. Developers give up on objects entirely and return to programming in a model that's more relational-friendly. Most developers will agree that this is a radical and unattractive approach, particularly given the success of object-oriented languages over the last two decades, as evidenced by the millions, if not billions, of lines of object-oriented source code executing quite successfully today.
  • Manual mapping. Developers issue SQL queries against the relational database, harvest the results, mapping them to objects within the domain model through hand-written code. While this offers the greatest amount of control over the mapping of relational tables and the domain model, it also represents the greatest amount of work and time required, leaving a sour taste. It does, however, provide the greatest degree of "loose coupling" between the domain model and the database, a desired attribute in those situations where the object developer does not control—and has little influence on—the relational schema, a common scenario in many IT shops where multiple languages are used against the same database.
  • Acceptance of O/R-M limitations. Developers simply accept that O/R-M cannot "go the last mile" to completely eliminate the impedance mismatch from the developer's perspective, and use O/R-M tools for that percentage—be it 50%, 75%, 95%, whatever—of their persistence solution to which it can be easily applied. Developers will need to have a good understanding of their O/R-M tool to do this, however, because they will need to make sure that their "out-of-band" direct queries against the database do not confuse or corrupt any sort of caching scheme the O/R-M tool is doing internally. This implies a greater educational investment on the part of developers, who frequently turn to O/R-M as a way to avoid having to learn too much about how the mapping between objects and relational database is handled.
  • Integration of relational concepts into languages. Developers use languages that integrate relational concepts directly, thus eliminating the impedance mismatch. Relational queries return sets, which are then used directly or indirectly within the language or framework, given first-class status from the language. While attractive, unfortunately this option is the least approachable as of this writing, as none of the current tools and technologies in widespread use (C++, C#, Java) provide any practical support today. Future tools and technologies, such as Microsoft’s LINQ project, which looks to integrate new language enhancements and supporting framework classes to the C# and VB languages (scheduled for release in their next-generation Visual Studio product, code-named "Orcas"), may provide viable options for developers seeking this option, but for now, this remains a "future" option for all practical purposes.
  • Integration of relational concepts into frameworks. Developers can choose library classes or frameworks—such as Microsoft’s DataSet, Ruby’s ActiveRecord or Java’s RowSet—that provide a thin wrapper over relational concepts (tables and rows and columns), thus minimizing the mapping between objects and relations by choosing relations as a natural storage mechanism for data even within the programming language. Such an approach brings relations closer to the programming language, rather than historical approaches which attempt to bring objects closer to the database. While some development shops have found this approach somewhat successful, debate rages over its usefulness and applicability.
  • Integration of object concepts into the database. Instead of seeking to bring relations closer to the language, developers choose the opposite approach, that of bringing the objects entirely into the database, using an Object-Oriented DataBase Management System (OODBMS) for storage of their objects, rather than the traditional RDBMS. Using an OODBMS, such as db4objects' db4o product, or Versant's Object Database, or Objectivity's ObjectSpace [1], developers store objects directly, rather than taking them apart into constituent fields and storing those fields into columns and rows, thus eliminating the need for a "mapping" layer entirely. While using an OODBMS will not completely eliminate all of the problems described in the "Vietnam" essay, it does address some of the more egregious problems, and thus frequently provides the developer a better chance of avoiding the quagmire and allowing them to focus more clearly on the problem at hand.

The remainder of this paper discusses the impact of choosing this final approach, using db4o as the database.

The object/relational impedance mismatch

For a more detailed discussion of the object/relational impedance mismatch, readers are encouraged to consult the original essay; in this paper, we examine the issues raised in that paper, and the various ways by which the db4o OODBMS implementation seeks to solve, or at least minimize, the dangers. Recall, however, that the main argument of that essay was that object/relational mapping faces the Law of Diminishing Returns: that early successes lead to a commitment to O/R-M that, if left unchecked and unexamined, leads to greater and greater "pain" as the various O/R-M issues begin to surface. Thus, a large part of the goal of an OODBMS-based persistence scheme will be to minimize those issues down the road, but more importantly, offer developers a greater flexibility under certain scenarios.

Many people, including Chris Date [2], have suggested that there is less of an impedance mismatch between a relational model and the object world than is commonly believed. Date, for example, suggests that objects are simply relations with associated behavior attached, and type inheritance is described in a manner similar to what we see in classic object languages and in any event, becomes an implementation detail in the relational database. (To those who would point out that no "object-relational" database provides any such support whatsoever, presumably Date would also be quick to point out that no commercial RDBMS implements relational theory correctly, either.) In some respects, this view of the impedance mismatch is true, but more practically, the mismatch still stands: the differences between an object approach and the relational theory are somewhat stark. For example, associativity in relational theory and object models stand entirely 180 degrees to one another: in a relation, associations are held by the thing referred, whereas in an object, associations are held by the referrer (via a reference or pointer). This single difference, for example, will result in some significant differences between how each reacts and present some core problems when trying to bridge the gap between the two.

The object-to-table mapping problem

One of the first, and most easily visible, problems with an object/relational mapping tool is that of the mapping itself. Deciding how tables and classes will align against one another can be a difficult prospect. Although not an insurmountable problem, as tools like Hibernate or JPA demonstrate, establishing good mappings between tables and classes can still be tricky, all the more so because frequently the mapping starts out very simply, with a one-to-one alignment between columns of a table and fields of a class, leaving developers to think that all mappings will be this straightforward.

In an OODBMS, there is no mapping present, because the OODBMS infers the schema directly from the class in question. In the case of db4o, when the object is sent to the database, the db4o engine uses VM metadata similar to that revealed by Reflection (java.lang.reflect.* in the JVM, System.Reflection in the CLR) to examine the object’s fields, and makes on-the-spot decisions about its storage at that point. This avoids the need for a post-compilation “schema generation” step to create the relational schema, or, in the more frequent case where the relational schema is fixed, the pre-compilation code-generation step to infer objects out of the schema which then developers are constrained around.

Naturally, this also means that an OODBMS has an intrinsic understanding of two of the mapping problems that pose the greatest dilemma to the object developer: inheritance and associations. In the case of inheritance, the OODBMS understands natively the relationship between parent and child classes, and frequently that relationship is stored firsthand as part of the stored object; this means that no matter how deep the inheritance hierarchy, no costly joins and artificial associations must be made (in the case of table-per-class mapping) or extraneous column fields kept (in the case of table-per-concrete-class or table-per-hierarchy mappings).

The student of relational theory may, at this point, be curious as to where relational integrity constraints, one of the great strengths of the relational model, are kept in an OODBMS. In fact, an OODBMS expresses no integrity constraints directly, except those that are defined in the class hierarchy itself; remember, in an OODBMS, the schema is the object’s class definition, not a standalone artifact, so any integrity constraints desired must be expressed directly inside of the class definition. This has the added advantage of keeping the integrity constraints consistent throughout both the storage and working memory tiers, something that requires some kind of housekeeping, either manual or automated [3], in an O/R-M scenario.

This also means that refactoring an OODBMS is much more straightforward, as db4o can silently handle changes to classes even in those situations where production data for that type already exists. So, for example, adding a field to a class when existing objects (without that new field) are already present in the data store is not a problem; db4o simply “extends” the stored objects to include the new field, setting its value to be the default for that type (typically 0, false, or null, depending on the type) and returns the object when queried. Db4o can also go the other way, allowing classes to remove fields (and those field values will simply not be returned when the object is queried) without penalty, even going so far as to store the old values in case that field removal is ever undone [4].

Internally, the data is stored in similar techniques to that of the world’s favorite RDBMSes, but this is all hidden from the db4o user—db4o never exposes its internal data storage scheme to the developer [5], and thus the developer is left with the perspective that the class definitions she writes are the only schema definition used. (Ironically, or perhaps entirely appropriately, this is precisely in line with Date’s idea of encapsulation from the actual details of data storage, a key cornerstone in his definition of a database.)

Besides an easier refactoring scenario, the OODBMS approach also creates a much tighter coupling between the executing objects and their persistence mechanism, which can be either good or bad, depending on the scenario. In certain situations, the RDBMS is used as a point of interoperability, where differing languages/platforms can exchange data, and thus the loosely-coupled nature of the RDBMS/SQL approach stands to be more useful. In situations such as "silo" applications where a single user interface accesses a single database (the traditional "baby webapp on top of a big database"), or the more leading-edge "service" implementations, however, all interaction will be through that user interface or service interface, and never against the database itself, thereby making persistence truly an implementation concern only. In these situations, an OODBMS back-end can be invaluable in defining and preserving a rich domain model, as now there are no entity definitions in two languages (Java/C# and SQL DDL) to be reconciled.

The dual schema problem

In many respects, the object-table mapping problem is reflective of the larger problem, that of the dual-schema problem, in that in a traditional object/relational world, two sets of entity definitions are in play: one defined by the programming language itself, the other by the relational model using SQL DDL. This sets up an inherent challenge, as now two sets of definitions must be kept up to date as the system grows and evolves, either by "slaving" one to the other (frequently seen by the use of code generation tactics, either from schema to classes or the other way around), or by editing/adjusting the two separately and hand-tuning the mapping between them as necessary. This creates a tension between the two, and frequently developers are forced to make sacrifices in the purity of both models in order to keep the two in sync with one another.

Again, in an OODBMS, the fact that the class definitions are the only schema present means that no such “dual schema” problem exists; the domain model need not be slaved to the storage definitions, and the storage definitions need not be twisted into strange formations just to support the storage of a rich domain model.

The schema-ownership conflict

One issue faced by many developers struggling with the object/relational problem is that relational databases now stand firmly within the borders of the IT kingdom, and her citizens are loath to allow foreigners to define how things are done there. In many companies, IT admins and database administrators have very specific ideas about "the way things should be", and frequently their policies and procedures present obstacles to refactoring. In some companies, in fact, the definition of new database views, stored procedures and triggers are under the control of the DBAs, rather than the developers, which now means that not only does the organization need to sync up the two different code artifacts, but now this is done across two disparate groups entirely.

db4o offers a mixed blessing here: since the schema is owned by the developers, DBAs have nothing to "own", and therefore the political conflict, in theory, doesn’t exist. In practice, when the DBA group hears that "mission-critical" data is being stored outside of their domain, they tend to go ballistic and raise all kinds of grief with upper management, where their complaints will likely be heard with sympathetic ears, since theirs is what's familiar, and the db4o world is not. (Until db4o has at least a decade under its belt, this is likely to remain a problem for adopters, as with any other new technology. Even relational databases went through this, back in the day.)

In practice, this means that one of three things will occur:

  • the db4o-adopting developer group will need to fight the political battle to retain ownership of the entire db4o footprint (which may in fact be counterproductive—developers don't need, or even want, to be the production support team for the db4o database)
  • the db4o-adopting developer group will surrender the runtime footprint for db4o to the IT staff; at times, however, this leads to complaints from the IT staff that the same kind of rich toolset they're used to in the RDBMS world doesn't exist around db4o (some of which is true, some of which is simply that object databases are just "different" than what they're used to), or
  • the db4o-adopting developer group will use db4o as a "staging point" for storing data, from which the data is then imported into a traditional RDBMS

Of course, the fourth option, converting/educating the IT staff to OODBMS technology is always possible, but this is by far and away the least likely option to succeed, particularly given the history of the OODBMS industry's attempts to do exactly that.

Again, this being a political problem, there are always non-technical solutions to this (and non-technical impediments), but if the problem can be avoided entirely, most times it's best to take that approach.

One issue that cannot be avoided, however, is that of reporting: frequently, the IT staff create reports (using tools like Crystal Reports) against the RDBMS, and in a db4o-based scenario this isn't practical or feasible, since no popular tools exist for reporting from an OODBMS. In practice, then, this implies that developers would be "on the hook" to write code to generate reports themselves, or that the data from the db4o data store would need to be exported to an RDBMS for reporting, a scenario not unlike the relationship between OLAP and data warehouse databases in the relational world. Db4o’s Replication tools can assist with the latter approach, but any "real-time" reporting against the db4o database still needs to be done using a developer-authored interface.

Entity identity issues

Part of the impedance mismatch centers around the fundamental discordance about identity: in a relational model, identity is given by the state of the entity, typically a subset of that state to which we give the more recognizable name "primary key". In an object system, however, identity is given by an artificial identifier, known as an "OID", or object identifier. Typically, in Java or other object language, the OID is given simply by the object's location in memory (the "this" pointer, as it was known in C++). This notion of identity-via-OID is also the reason for the distinction between Java's "==" operator and "equals" method, as it turns out—the latter tests for equivalence, the former for identity, which is whether or not two references point in fact to the same object.

This presents a problem to an O/R-M system, since now we have two different, and conflicting, systems of identity, which in turn confuses caching systems (particularly those inside of an O/R-M implementation). An OODBMS persistence implementation helps avoid some of this confusion, since its system of identity is based around OIDs just as the in-memory object representation does. However, one problem an OODBMS frequently faces is that of keeping straight the connection between in-memory object and OID-attached object, and db4o is no exception here. It's not impossible—the db4o API provides a static method to check to see if an object's been attached to an OID. This distinction of "attached" vs "unattached" is subtle, however, and some developers may find it more subtle than managing the distinct forms of identity in an O/R-M.

The partial-object problem and load-time paradox

In a SQL query, developers write precisely which data elements they want retrieved as part of a query, meaning developers can return as much or as little of a table (or collection of tables) as desired. In an object-oriented language, however, this isn't possible—developers cannot work with "pieces" of an object, or individual fields extracted into a new object type. This creates a "partial object" problem, in that the atomic unit of an object database is the object itself, which frequently is more data than necessary.

To the original O/R-M implementors, this problem was solved by creating "on-demand" load triggers, such that if only a few fields of an object were explicitly requested, only those fields would be populated in the object, the object as a whole returned, and if the developer asked for a field not populated in the returned object, a "demand-trigger" would be tripped and the object would go fetch the field in question from the datastore. This typically implied a remote network call (either a JDBC query or some other RPC-like call), with all the negative performance implications that brings. The dangers of this approach were fully exposed by the backlash against the EJB Entity Bean implementation, where each of these retrieval operations were themselves protected (individually) by a full two-phase commit transaction, leading to a general revolt against EJB as a whole that persists to this day, and the generalized advice that clients of an EJB system should never access an entity bean directly, but only through session beans and DTOs, which clearly destroys any attempt to build a rich domain object model and shield developers from the details of persistence.

The root of this problem is that of network traversal—if the persistence implementation lives inside the same process as the client-accessing code, then the demand-trigger approach to the partial-object problem yields a comfortable solution, since the costs of making a disk I/O access (at worst) are far more manageable than those of making a network I/O trip. Both db4o and other relational database systems (HSQL and Derby being two of the popular Java options, Microsoft’s SQL Server Express being another) offer "in-memory" or "local" options, where the database resides in that same process and thereby avoids the network. For most production systems, however, running the database in the same process as the application server or even on the same machine as the application server is an unacceptable solution due to firewall constraints or security concerns.

Here, the db4o system is just as vulnerable as any relational system or O/R-M tool: db4o offers no "partial object" solution, meaning that any object retrieved is retrieved in full, potentially retrieving data fields that are not needed at that point in the client code. More importantly, however, objects often hold references to other objects, and a naïve object database implementation will need to retrieve all of those “dependent” objects that are referenced by the fields of the retrieved object, leading potentially to a huge graph of objects retrieved in response to a simple first name/last name request on the Person class in the database. Db4o solves this problem via a concept they call "activation depth", whereby the db4o developer indicates how "deep" the graph should be retrieved when the object in question is retrieved from the database—an activation depth of 2 tells the db4o system to retrieve not only the object requested, but any objects it in turn references but no further. Objects beyond the activation depth are not populated at all, but their fields are (artificially) set to a null value. Developers can then, if necessary, activate those child objects by calling a db4o API, again activating more child objects from there if necessary or desired.

(Just as this paper was being finished, the db4o team committed a new feature whereby developers can register method callbacks that will be invoked as objects are added, removed, changed, and so on. Interested clients can then receive notifications about object updates, and thus keep local copies in sync with what's being stored in the database.)

A close corollary to the partial-object problem is the load-time paradox, in which the decision to retrieve data "eagerly" or "lazily" from the datastore is often not made on a class-by-class basis, but would rather be made in accordance with the context of the situation in the client code. Most O/R-Ms do not acknowledge this paradox, referring developers to their associated query language (or trusting in their demand-load triggers inside partial objects) to be more specific about what needs to be retrieved in each situation. As with the partial object problem, db4o offers no particular solution, but developers generally will set/reset activation depth in front of each query in order to control the amount of data being retrieved in each scenario, a single API method call in front of the query call itself.

The data-retrieval mechanism concern

Probably the most distinctive difference between an OODBMS-based approach (db4o in particular), and an O/R-M-based approach is in the data-retrieval mechanism. In a traditional relational scenario, developers use SQL to specify exactly the relational values to return, using the relational algebra and predicate calculus to specify exactly what should be retrieved, nothing more and nothing less. This provides a degree of economy in what is pulled across the network rarely matched in any other distributed system.

Object databases have historically proven less efficient at describing the data to be retrieved, despite a variety of approaches: Query-By-Example (QBE), Query Object APIs, and object query languages such as OQL, HQL, EJBQL, and so on. The problems associated with QBE, Query Object APIs, and query languages are not difficult to spot: a QBE approach severely restricts the kinds of queries that can be made against the database, and is usually used only for simple, single-object requests ("identity" requests), or for requests to retrieve a collection of objects that obey simple AND-ed predicates, such as "find all Persons where lastName is 'Powers' and occupation is 'Software Developer'". Query Object APIs provide a greater range of query capability, but are tedious and awkward to manipulate, requiring construction of numerous intermediate objects to hold the query together, such as "predicate" objects designed to hold the queried field and the predicate query value together. Developers generally tire of the obtuse Query Object API quickly, which then leads to the "query language" approach, a new language designed to allow for quick and easy generation of queries such as ODMG's Object Query Language (OQL), or Hibernate's Query Language (HQL), all of which look alarmingly similar to SQL. This in turn often leads developers full circle, asking themselves why they didn't just bother writing SQL in the first place, particularly when these query languages turn out to be missing certain features of the SQL language, such as ORDER BY or GROUP BY or DISTINCT or outer joins and so on.

The db4o database uses an entirely new approach, called "native queries", in which the predicates used to query the database for objects to return from the query are described using the native language itself (Java or C#). This means that developers need not learn a new query language at all, since now all database interactivity is done using the language of their choice. Yet the query can be as expressive as they need it to be, since both Java and C# have the same rich set of comparison operators that SQL (or another query language) does:

List <Pilot> pilots =
  db.query(new Predicate<Pilot>() {
  public boolean match(Pilot pilot) {
    return pilot.getPoints() == 100;
  }
});						
	

This approach has the added benefit, not present when using O/R-Ms (or SQL, for that matter), of being entirely type-safe: the compiler will verify that the fields used in the query are present on the object in question and that all comparisons are entirely type-safe, thus eliminating a source of potential errors and an entire class of unit tests necessary.

One concern developers voice when first seeing this feature is that this implies that every object in the database must be instantiated and passed into the comparison method, clearly a horrible performance hit if thousands—or millions—of objects reside in the database. Therein lies the beauty of the db4o native query feature, as the db4o system will do runtime Reflection and bytecode analysis to understand what the query is doing (what fields on the object are being compared, what values the fields are being compared against, what operations are being used to do the comparison, and so on), and converts that to its internal Query Object API (called SODA). While not all queries will be possible to translate this way, in general a significant percentage of developers' queries can be, leaving developers with an efficient-to-write and efficient-to-execute query approach. Developers can also be notified (via a runtime API call) when native queries cannot be optimized this way, thus providing them with an opportunity to "hand-tune" certain queries using the more fine-grained/low-level SODA query API.

Summary

At no point should this essay be seen as a general call to embrace the OODBMS over the relational database; any attempt to position a technology, any technology, as the one-size-fits-all solution to all problems faces dangers. While obviously applicable to the object database, this truism also works for the relational or even XML/hierarchical database, as well—it's a fair statement to say that "X technology" works well in a particular set of scenarios, less well in others, where "X" is either the relational or object database.

The traditional RDBMS sits at the center of a collective of hardware and software, acting as the central repository for storing mission-critical data, and as such, carries with it a heavy administrative load. Additionally, the traditional RDBMS server presents a sizable security concern, SQL injection attacks being just one of the possible ways an attacker can obtain data (from improperly constructed SQL queries—or HQL queries, or EJBQL queries...). An OODBMS avoids this injection attack entirely so long as it avoids the use of a query language style query, and native queries in db4o neatly avoid that problem while still providing the power of a query language.

While there's no reason that an object database cannot sit at the center of the IT universe next to an RDBMS, in many respects this misses part of the power and beauty of the object database, and db4o itself. Because the object database is much more closely coupled to the programming code, it makes for a much easier “edge” storage solution, pushing data out to the edges of the network, such as client desktop machines, small-device clients, and customer-facing web servers, all of which benefit strongly from the OODBMS's smaller administrative footprint requirements. In fact, db4o markets itself as a "zero-admin" tool, making it attractive for developers looking to create rich-client applications delivered over the Internet (via JavaWebStart or ClickOnce).

In the end, avoiding the quagmire so clearly identified by the name "Vietnam" is a goal that is equal parts technology and politics. For many, the problems of the O/R-M can be managed by strong developer discipline, deep knowledge of how the O/R-M works and the queries it generates against the relational database, and careful adherence to the rules of the O/R-M's caching infrastructure. For those who are seeking to truly "avoid SQL" and having to learn a new language and way of storing data, however, the OODBMS represents a powerful option that eliminates the mismatch entirely. And for those applications, services, or systems where persistence implementation is really just an implementation concern, why not take the path of least resistance, even if it is the road less traveled?

Footnotes

[1] See Vendors for a complete list of products
[2] Introduction to Database Systems, 8th Edition, pp 813-886
[3] It should be fair to point out that in most cases, an automated tool should be able to track these updates without requiring developer intercession; however, the old 80/20 rule does come into play here, in that roughly 20% of the mappings created will need hand-tuning and adjustment and therefore developer time.
[4] The db4o documentation has more on how and when db4o can track these old values; suffice it to say, db4o will not track these values indefinitely, as that would be a tremendous waste of storage and time.
[5] Although there is a Reflection-like API for examining the structure of stored objects, for those situations where a lower-level view is desired.

This paper has been made possible through the collaboration of Neward&Associates, db4objects Inc. and ODBMS.ORG, the most up-to-date collection of free materials on object database technology on the Internet.

SUPPORTERS

MEDIA PARTNER

SOCIAL

PERSISTENT PATTERNS AWARDS

Share |