Distributed by db4objects under the GPL or commercial license based on your needs. Also available for. NET CompactFramework and Java, with the same feature set and database file format, enabling cross-platform development. The class schema of your application classes is analysed and adjusted in real time when objects are stored. Object-oriented querying functionality is provided through Native Queries NQ , the ability to query the database using.
|Country:||Republic of Macedonia|
|Published (Last):||16 July 2009|
|PDF File Size:||12.25 Mb|
|ePub File Size:||13.10 Mb|
|Price:||Free* [*Free Regsitration Required]|
As I explained, an object database like db4o simply has more to offer to object-oriented developers, in today's object-oriented world, than its relational cousins. Information storage and retrieval has been nearly synonymous with RDBMS for about a decade now, but recently that has begun to change. Java developers in particular are frustrated with the so-called object-relational impedance mismatch, and impatient with the solutions that attempt to resolve it.
This, along with the emergence of a viable alternative, has led to a renaissance of interest in object persistence and retrieval. This series is a working introduction to db4o, an open source database that leverages today's object-oriented languages, systems, and mindset. In this and future articles, I'll continue making the case for the object database.
I'll use examples to demonstrate the power of a storage system optimized for the same "shape" of entities that you work with in your object-oriented programming language of choice -- in this case, the Java language.
In particular, I'll introduce the various mechanisms available for retrieving, modifying, and restoring objects back into db4o. As you'll learn, it's actually quite amazing what you can do once you're freed from the constraints of SQL. If you haven't already done so, you may want to download db4o now. You need it to compile the examples. Query by Example QBE is a database query language that allows you to create queries by designing a "template" against which to do comparisons, rather than a language using predicate criteria as in SQL.
I demonstrated data retrieval using db4o's QBE engine last time, but I'll quickly recap here. Start with a look at my admittedly primitive database. It consists of one type, whose definition appears in Listing It consists of three fields and some basic methods to support POJO-like activities, namely toString and equals. Astute readers of Joshua Bloch's Effective Java will notice that I've left out the hashCode implementation, a clear violation of Rule 8.
In the classic parlance of authors everywhere, I leave hashCode as "an exercise to the reader," which typically means the author either doesn't want to bother with it or doesn't think it's necessary to the example at hand. I also leave it as an exercise to the reader to decide which is the case here. In Listing 2, I create a half dozen of objects, place them into a file, and then use QBE to call up the two objects whose first names match the pattern "Brian.
Because QBE uses a prototype object as its template to search for data, there are a few simple rules regarding its usage. When db4o is searching all the objects of the Person type for a given target an oversimplification of what happens, but conceptually accurate , to determine if a particular object in the data store meets the criteria, the field values are compared one by one.
If a field in the prototype is "null," then that value matches against any value in the data store; otherwise, the values must match exactly. For primitive types, because primitive types cannot really hold a value of "null," zero is used as the wildcard value.
This also points out a limitation of the QBE approach -- zero cannot effectively be used as a value to search on. Should multiple field values be specified, then all the field values must be met by the object in the database for the candidate object to meet the query criteria; in essence, this means that the fields are "AND"-ed together to form the query predicate.
In the previous example, the query is looking for all Person types where the firstName field is equal to "Brian," and the lastName and age fields are effectively ignored. Be careful, though, about trying to map OODBMS queries to SQL: the analogy isn't perfect and can lead to misunderstanding about the nature and performance of particular queries. Walking the results is a simple exercise in using the Iterator interface implemented by ObjectSet.
Using the particular methods of Person would require a downcast on the objects returned by next. While a simple display of data is interesting in itself, most objects will also need to be modified and restored back to the database. This is probably the trickiest part of working with an OODBMS because an object database uses a different notion of identity from a relational database. Practically speaking, this means you have to be more careful about objects-in-memory-versus-objects-in-storage when working with an object database.
When you run the query in Listing 3, the database reports three Brian s, two of them Brian Goetz. A similar effect would occur if the persons. The db4o development team occasionally finds that certain APIs are less frequently used, or represent "experiments" on the API that the team isn't sure should be a part of the core ObjectContainer API. In those cases, the methods are provided on the ExtObjectContainer instance returned by the ext method.
Methods available on this class vary from release to release as they are introduced, removed, or moved to the core ObjectContainer class itself. As always, see the db4o documentation for the complete details of the ExtObjectContainer class.
Clearly, the old rules regarding primary keys aren't in force here; so how does an object database deal with notions of uniqueness?
When an object is stored into an object database, a unique key is created, called an Object identifier or OID pronounced similarly to the last syllable of avoid , which uniquely identifies that object. In db4o, the OID for a given object can be found through a call to db. You can also use the db. Calling this method has some implications too complex to discuss here, but it remains an option.
In practice, all this means is that it falls to the developer to determine whether an object previously exists in the system, usually by querying the container for that object before inserting it, as shown in Listing In this particular case, let's assume that the uniqueness of a Person in the system is its first name-last name combination.
When searching for Brian in the database, you therefore need only look for those attributes on the Person instances. Maybe Brian was added a couple of years ago, before he turned If you want to modify the object in the database, it's simple to take the object retrieved from the container, modify it in some way, and then store it back, as shown in Listing The db4o container doesn't run into problems of identity here because the object in question has already been identified as one coming from the database, meaning its OID is already stored inside the db4o bookkeeping infrastructure.
Accordingly, when you call set , db4o knows to update the existing object rather than insert a new one. The notion of a primary key that is application-specific is worth keeping around, even though not inherent to QBE.
What you need is a utility method to simplify identity-based searches. This section shows you a solution based on using the Reflection APIs to poke the right values into the right fields, as well as suggesting ways to tune the solution for various preferences and aesthetics. Let's start with a basic premise: I have a db4o database in which I have a type Person that I want to query based on a set of fields that have certain values in them.
Within this method, I use the Reflection APIs on Class to create a new instance of that type invoking its default constructor. I then iterate through an array of Strings that have the fields in them, getting back each Field object in the Class.
Following that, I iterate through the array of objects that correspond to the values for each of those fields and then call Field. Once that's all done, I call get on the db4o database and check to see if the ObjectSet returned contains any objects. This gives me a basic method outline that looks like the one shown in Listing What is apparent from Listing 7, however, is that its usage is arguably not much simpler than the basic QBE version shown already:.
Actually, much of the utility method's utility become apparent when placed onto the stored class itself, as shown in Listing Or, again, you could tweak the method to return the instance found, so that the Person instance had its OID appropriately associated, and so on. The key thing to remember is that you can build convenience methods on top of the db4o infrastructure to make it easier to use.
Note that there is a more efficient way to perform this style of query against the underlying objects stored on disk using the db4o SODA query API, but it's slightly out of scope for this article, so I'll leave it for a later discussion. So far you've seen how to query for individual objects, or objects that meet a particular criteria. Although this makes for a fairly easy way to issue queries, it also makes for somewhat limited options; for example, what if you needed to retrieve all Person s whose last name started with G , or all Person s of an age greater than 21?
Clearly the first option above is only viable for the most trivial of databases because it puts an obvious upper-bound on the size of the database that you can practically use. Fetching a million objects is not something even the hardiest hardware will shrug off easily, particularly if it's across a network connection. The second option pollutes the simplicity of the QBE approach and leads to monstrosities like the one shown in Listing It's fairly easy to see how any moderately complicated query will rapidly become unworkable using this technique, particularly when compared against the simplicity of a query language like SQL.
The third option is to create a query language that can then be used to query the database's object model. The drawback of OQL is that it wants to return A language so similar to SQL would seem to want to return column sets tuples , like SQL does, but an object database doesn't work that way -- it wants to return objects, not arbitrary sets.
Especially in a strongly typed language like C or Java programming, these object types have to be known a priori , unlike the set-based notion of SQL. Rather than force a complex query API onto developers or introduce a new "something-QL," db4o offers a facility called native queries , which is both powerful and remarkably easy to use, as you can begin to see in Listing As you'll see in a second, however, SODA is generally only necessary for hand-optimizing queries.
The "native" part of the query is the fact that it is written in the programming language itself -- in this case the Java language -- rather than in some arbitrary language that must then be translated into something else. A non-generics version of the Predicate API is available for versions prior to Java 5, though it isn't quite as easy to use.
Thinking about this for a moment, you will probably begin to wonder how exactly this particular approach is being implemented. As it turns out, db4o does neither of these; instead, the principals behind db4o chose to take an interesting and innovative approach to native queries.
Loosely put, the db4o system sends a predicate to the database, where it performs bytecode analysis at runtime on the bytecode for the match method. If the bytecode is easy enough to understand, db4o will turn that query into a SODA query for efficiency, in which case there is no need to instantiate all the objects to pass into the match method. In this way, programmers can continue to write queries in the language they're comfortable with, but the query itself can be translated into something the database can understand and execute efficiently.
But please don't repeat that name to the db4o developers; you'll get me in trouble. The db4o Java distribution includes several jar files, including a core db4o implementation for each of the JDK 1.
Despite its name, this is a Java bytecode optimizer developed at Purdue University that must be present, along with the db4o Failing to include these libraries will not generate an error of any kind but will simply cause every native query to be unoptimized. Developers can find this out, but only in a passive fashion, using the listeners described in this section.
The native queries approach isn't perfect. For example, it is entirely possible to write a native query complex enough to defeat the bytecode analyzer, thus requiring the worst-case execution model to take place.
In this worst-case scenario, db4o would have to instantiate every object of the queried type in the database and pass each one through the match implementation. Predictably, this would kill query performance, but you can work around it by installing listeners where you need them.
Intuition isn't always sufficient for anticipating a failure to optimize because the reasons can be entirely different from what a code review would imply.
For example, including a console print statement System. WriteLine in C causes the optimizer to fail in the. NET version of db4o, whereas the Java version optimizes the statement away. You can't really anticipate variations of this type although you can learn about them by experience , so it's always a good idea to Let The System Tell You, as they say in extreme programming. Simply register a listener Db4oQueryExecutionListener against the ObjectContainer itself to inform you if a native query cannot be optimized, as shown in Listing
Queries, updates, and identity
As I explained, an object database like db4o simply has more to offer to object-oriented developers, in today's object-oriented world, than its relational cousins. Information storage and retrieval has been nearly synonymous with RDBMS for about a decade now, but recently that has begun to change. Java developers in particular are frustrated with the so-called object-relational impedance mismatch, and impatient with the solutions that attempt to resolve it. This, along with the emergence of a viable alternative, has led to a renaissance of interest in object persistence and retrieval.
Getting Started With db4o
NET developers. It was developed, commercially licensed and supported by Actian. In October , Actian declined to continue to actively pursue and promote the commercial db4o product offering for new customers. The term object-oriented database system dates back to around , though the first research developments in this area started during the mids. The first commercial object database management systems were created in the early s; these added the concept of native database driven persistence into the field of object-oriented development. The second wave of growth was observed in the first decade of the 21st century, when object-oriented databases written completely in an object-oriented language appeared on the market. The db4o project was started in by chief architect Carl Rosenberger, shipping in