OJB
Downloads
Documentation
Development
Translated (Web)
|
Using OJB as a persistence layer in your applications |
Introduction |
This document demonstrates how to use the ObJectRelationalBridge
(OJB) object/relational mapping in a simple application scenario. The
tutorial application implements a product catalog database with some
basic use cases. The source code for the tutorial application is
shipped with the OJB source distribution and resides in the
src/test/org/apache/ojb/tutorial1/ directory.
This document explains the architecture of a simple application which
uses the ObJectRelationalBridge PersistenceBroker API to
implement five use cases which include persistence operations (retrieval,
storage, and deletion of objects). This document will also demonstrate how a
simple persistence object Product is mapped to a table in
a relational database.
|
The Tutorial Application |
The sample application is a console based data entry application --
a program to manipulate a product catalog database with five simple
use cases:
-
List all Products -
display all products from the persistent store
-
Enter a new Product -
create a new product in the persistent store
-
Edit an existing Product record -
update a product in the persistent store
-
Delete a Product record -
remove a product from the persistent store
-
Quit the application -
for consistency we implement this as a use case
Only these five basic use cases have been implemented in the
interest of keeping this tutorial focused on conveying only the basic
concepts of OJB. This tutorial does not explain the necessary steps
to model or implement 1:1, 1:n, or m:n mapping. For information about
this please refer to Tutorial 3 Advanced O/R.
|
Running the Tutorial Application |
To execute the application described in this tutorial,
you must prepare the tutorials from the source distribution of
OJB. This is accomplished via Ant build scripts which are executed
by a script in the "bin" directory of the OJB distribution. (Note:
This tutorial assumes that you have already downloaded the OJB
source distribution.) There is no need to configure a
database in order to run these applications. The tutorial applications
ship with HSQLDB integration, all you need to do is run the
prepare-tutorials Ant target.
Unpack the OJB source distribution, make sure that your current
working directory is the top level directory of the OJB
distribution and run the following script:
- On Windows 9x/NT/2000/XP:
-
Prepare the Tutorials:
bin\build.bat prepare-tutorials
-
Execute this Tutorial:
bin\tutorial1.bat
- On Unix/Linux:
-
Prepare the Tutorials:
bin/build.sh prepare-tutorials
-
Execute this Tutorial:
bin/tutorial1.sh
|
|
Using the OJB PersistenceBroker API in the UseCase Implementations |
The code example above was trivial - there are no persistence operations
involved in quitting the application. Let's move on to a more relevant
example - UCListAllProducts .
This use case must retreive a Collection containing all
products from the database. It must then iterate over this collection
and print each product.To retreive the collection from the database we need a method from the
OJB API.
OJB provides three major APIs.
- The PersistenceBroker
- An ODMG implementation
- A JDO implementation
In this first tutorial only the PersistenceBroker API is used as it
is the most straightforward of the three options. Tutorial 2:
Using the ODMG API and
Tutorial 4: Using the JDO API will
implement the same application using these different methods of
database access.
You will find the source for the PersistenceBroker
API in the package org.apache.ojb.broker . The key
component in this package is the interface PersistenceBroker .
It provides methods for retrieval, storage, and deletion of objects. To use
it in your application you must know how to retrieve a
PersistenceBroker instance, how to configure its O/R mapping
repository and how to use the available functions.
Retrieving Collections and Iterators |
The next thing we need to know is how to use this broker instance to
implement our persistence operations. In this use case, we have to retrieve
a collection containing all product entries from the database. To retrieve a
collection containing objects matching some criteria we can use
PersistenceBroker.getCollectionByQuery(Query query) . Where
Query is a class that allows specification of criteria such as
price > 100 or userId == 3 . In this case we
want to select all of the persistence objects stored in the
Product table. We need no (or null) filtering
criteria. According to the ODMG the Collection containing all instances of
a persistent class is called an Extent.
Here is the code of the UCListAllProducts.apply() method:
 |  |  |
 |
public void apply()
{
System.out.println("The list of available products:");
// build a query which selects all objects of class Product,
// without any further criteria
Query query = new QueryByCriteria(Product.class, null);
try
{
// ask the broker to retrieve the Extent collection
Collection allProducts = broker.getCollectionByQuery(query);
// now iterate over the result to print each product
java.util.Iterator iter = allProducts.iterator();
while (iter.hasNext())
{
System.out.println(iter.next());
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}
|  |
 |  |  |
Imagine that there are 10,000 products in the product table:
Retrieving a collection of all Products from the persistent store
would be a very expensive operation - new objects are created and an
entire table is loaded into memory. In this tutorial application,
performance is not a major issue, but in a real-world application OJB
provides a more efficient mechanism for iterating through all the
available products. If your application does not need to maintain a
collection of product objects in memory, you should call
getIteratorByQuery() which returns an Iterator instead
of a Collection .
This method is extremely useful if you write applications that
need to iterate over large resultsets. Instances are not created
all at once, they are created on an "as needed" basis as you
iterate through the result. Instances of persistent objects which
are no longer referenced by the application can be reclaimed by
the garbage collector. Using this method the code would resemble
the following:
 |  |  |
 |
public void apply()
{
System.out.println("The list of available products:");
// build a query that select all objects of Class Product,
// without any further criteria.
Query query = new QueryByCriteria(Product.class, null);
try
{
// ask the broker to retrieve an Iterator
java.util.Iterator iter = broker.getIteratorByQuery(query);
// now iterate over the result to print each product
while (iter.hasNext())
{
System.out.println(iter.next());
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}
|  |
 |  |  |
For further information you may have a look at the
PersistenceBroker JavaDoc
and at the Query JavaDoc.
|
Deleting Objects |
The UseCase UCDeleteProduct allows the user to select one of the
existing products and to delete it from the persistent storage. The
user enters the product's unique id and the PersistenceBroker tries to lookup the
respective object. This lookup is necessary as our application does
not hold a list of all products. The found object must then be
deleted by the broker. Here is the code:
 |  |  |
 |
public void apply()
{
String in = readLineWithMessage("Delete Product with id:");
int id = Integer.parseInt(in);
// We do not have a reference to the selected Product.
// So first we have to lookup the object,
// we do this by a query by Criteria
// 1. build an example object with matching primary key values:
Product example = new Product();
example.setId(id);
// 2. build a QueryByCriteria from this sample instance:
Query query = new QueryByCriteria(example);
try
{
// start broker transaction
broker.beginTransaction();
// lookup the product specified by the Criteria
Product toBeDeleted = (Product) broker.getObjectByQuery(query);
// now ask broker to delete the object
broker.delete(toBeDeleted);
// commit transaction
broker.commitTransaction();
}
catch (Throwable t)
{
// rollback in case of errors
broker.abortTransaction();
t.printStackTrace();
}
}
|  |
 |  |  |
A QueryByCriteria was used to simplify this tutorial and reduce the
amount of code necessary to demonstrate the essential components of OJB.
It is important to note that one can also build a query based on
filter criteria through the Criteria object. The following code demonstrates
the construction of a same query using a Criteria object:
 |  |  |
 |
// build filter criteria:
Criteria criteria = new Criteria();
criteria.addEqualTo("_id", new Integer(id));
// build a query for the class Product with these filter criteria:
Query query = new QueryByCriteria(Product.class, criteria);
...
|  |
 |  |  |
An arbitrary number of criteria can be added in order to produce very complex
queries. The following code demonstrates a more complex use of the Criteria
object, one that is not present in this tutorial application. This code
retrieves all products which are priced less than 5.40 (USD, Yen, Euros, etc.)
and of which we have at least 2 million in stock:
 |  |  |
 |
// build filter criteria:
Criteria criteria = new Criteria();
criteria.addLessThan("price", new Double( 5.40 ));
criteria.addGreaterThan("stock", new Integer( 2000000 ));
// build a query for the class Product with these filter criteria:
Query query = new QueryByCriteria(Product.class, criteria);
...
|  |
 |  |  |
|
Completing the Tutorial Application |
The completion of this tutorial program is left as an exercise for
the tutorial reader.
Now you are familiar with the basic functionalities of the OJB
PersistenceBroker API. To learn more you might consider implementing the
following additional use cases:
-
List all products with a price > 1000 (or let the user enter a
criteria)
- Delete all products that have a stock of 0
-
increase the price of all products that cost less then 500 by 11%
(and make the changes persistent)
|
|
Defining the Object/Relational Mapping |
After looking at the code of the tutorial application and at the
sample database (bin\build browse-db will
start a browser on the InstantDB database) you will probably ask:
How is it possible for the OJB Broker to store objects of class
Product in the table PRODUCT
without any traces in the sourcecode? or How does OJB know that the
database column NAME is mapped onto
the attribute name ?.
The answer is: It's done in the OJB Metadata Repository. This
repository consists of a set of classes describing the O/R mapping
(have a look at the package org.apache.ojb.broker.metadata ).
The repository consists of ordinary Java objects that can be created
and modified at runtime. This brings a lot flexibility for special
situations where it is neccessary to change the mapping
dynamically.Keeping the mapping dynamic has several advantages:
- No preprocessing of Java sourcecode, no modifying compiled classes
-
Mapping can be inspected and changed at runtime, allowing maximum flexibility
in changing persistence behaviour or in building your own persistence layers
on top of OJB.
But it has also at least one disadvantage: Performance. Due to the
dynamic approach OJB uses either Java Reflection or JavaBeans compliant
access to inspect and modify business objects.
OJB takes great care to reduce the dynamic access overhead to a minimum.
The following sections dmeonstrate the O/R mapping for the tutorial application.
|
Conclusion |
In this tutorial you learned to build an OJB Object/Relational
mapping for a simple class layout and to use the OJB
PersistenceBroker API for persistence operations on instances of this
class.
there are three additional tutorials on using the OJB
ODMG API, on using the OJB
JDO API and on building advanced O/R
mappings (including 1-1, 1-n relations, proxy techniques,
supporting polymorphism and mapping inheritance hierarchies).
I hope you found this tutorial helpful. Any comments are welcome.
|
|