OJB
Downloads
Documentation
Development
Translated (Web)
|
Frequently Asked Questions |
General
Getting started
OJB APIs
Howto
Help! I still have serious problems installing OJB! |
The following answer is quoted from the OJB user-list. It is from a
reply to a user who had serious problems getting started with OJB.
I would say it was stupid not to understand OJB. How can you know what
another programmer wrote. I've been a Java programmer for quite some time
and I could show you stuff I wrote that I know you wouldn't understand.
I'll just break it down the best I can on what, where and why.
OJB is a data persistence layer for Java. I'll just use an example of how I
use it. I have an RDMS. I would like to save Java object states to this
database and I would like to be able to search this information as well. If
you serialize objects it's hard to search and if you use SQL it won't work
with any different database. Plus it's a mess having to work with all that
SQL in your code. And by using SQL you don't get to work with just Java
objects. But, with OJB your separated from having to work outside the
object world and unlike serialization you can preform SQL like searches on
your data. Also, there's things like caching and connection pooling in OJB
that help with performance. After setting up OJB you will us either ODMG or
JDO to access your information in a object centric manner. ODMG is a
standard for the api for accessing your data. That means you can use any
ODMG compliant api if you don't want to use OJB. The JDO part is like ODMG
except it's the SUN JDO standard. I use ODMG because the JDO interface is
not ready yet.
OJB is easy to use. I'll just break it down into two sides. There's the
side your writing your code for your application and there's the side that
you configure to make OJB connect to your database. Starting with your
application side, all that is needed is to use the interface you wish. I
use ODMG because JDO is not complete yet. Here's a link to the ODMG part
with some code for examples. http://jakarta.apache.org/ojb/tutorial2.html
That's all you need on the application side. Next there's the configuration
side. This is the one your fighting with. Here you need to setup the core
tables for OJB and you will define the classes you wish to store in your
database.
First thing to do is to build the cvs's with the default database HSQL,
because you know it will work. If you get past this point you should have a
working OJB compiled. Now if your using JDK 1.4 you will need to set in
build.properties JDBC=+JDBC30 and do a build.sh preprocess first. Next you
will do a build.sh junit and this will build OJB and test everything for
you. If you get a build successful then your in business. Then you will
want to run build.sh jar to create the OJB jar to put in your /lib. You
will need a couple other jars in you /lib directory to make it all work.
See this page for those. http://jakarta.apache.org/ojb/deployment.html
Next you will need some xml and configuration files in your class path for
OJB. You will find those files under {$OJB_base_dir}/target/test/ojb. All
the repository.xml's and OJB.properties for sure. With all these files in
place with your application you should be ready to use OJB and start
writing your application.
Finally you will want to setup your connection to your database and define
your classes you will be storing in your database. In the repository.xml
file you can configure your JDBC parameters so OJB can connect to your
database. You will also need your JDBC jar somewhere in your class path.
Then you will define your classes in the repository_user.xml file. Look
here for examples. http://jakarta.apache.org/ojb/tutorial1.html Note you
will want to comment out the junit part in repository.xml because it's just
for testing.
The final thing to do is to make sure the OJB core tables are in your
database. Look on this page for the core tables.
http://jakarta.apache.org/ojb/platforms.html. These core tables are used by
OJB to store internal data while it's running. It needs these. Then there's
the tables you define. The ones you mapped in the repository_user.xml file.
Sorry if any of this is off. OJB is growing so fast that it's hard to keep
up with all changes. The order I gave the steps in is just how I would
think it's understood better. You can go in any order you want. The steps
I've shown are mostly for deployment. Hope this helps you understand OJB a
little better. I'm not sure if this is what your wanting or not.
|
Why OJB? Why do we need another O/R mapping tool? |
here are some outstanding OJB features:
- It's fully ODMG 3.0 compliant
- It will have a full JDO implementation
- It's higly scalable (Loadbalanced Multiserver scenario)
- It provides multiple APIs:
- The full fledged ODMG-API,
- The JDO API (planned)
-
and the PersistenceBroker API. This API provides a O/R persistence
kernel which can be used to build higher level APIs (like the ODMG and
JDO Implementations)
- It's able to handle multiple RDBMS simultaneously.
-
it has a slick MetaLevel Architecture: By changing the MetaData at runtime
you can change the O/R mapping behaviour. (E.G. turning on/off usage of
Proxies.)
-
It has a simple CacheMechanisms that is fully garbage collectable by usage
of weak references.
- It has a simple and clean pattern based design.
-
It uses a configurable plugin concept. This allows to replace components
(e.g. the ObjectCache) by user defined Replacements.
-
It has a modular architecture (you can quite easily reuse some components
in your own applications if you don't want to use the whole thing:
- The PersistenceBroker (e.g. to build your own PersistenceManager)
- The Query Interface as an abstract query syntax
- The OQL Parser
- The MetaData Layer
- The JDBC Accesslayer
-
It has a very sharp focus: It's concerned with O/R mapping and nothing
else.
Before making OJB an OpenSource project I had a look around at the
emerging OpenSource O/R scene and was asking myself if there is
really a need for yet another O/R tool.
I came to the conclusion that there was a need for OJB because:
- There was no ODMG/JDO compliant opensource tool available
- There was no scalable opensource O/R tool available
-
there was no tool available with the idea of a PersistenceBroker Kernel
that could be easiliy extended
- The tools available had no dynamic MetaData architectures.
-
The tools available were not as clearly designed as I hoped,
thus extending one of them would have been very difficult.
|
Is OJB ready for production environments? |
Depends on your production environment. If you want to program an aeroplane autopilot
system you should not use Java at all. (according to the official disclaimer).
But I assume we are talking about enterprise business applications, aren't we?
And for such applications it's a clear yes.
OJB is used in production application since version 0.5.
We have about 6.000 downloads each month (and growing) and a large user base using it in a wide
spectrum of production scenarios.
We provide a regression test suite for Quality Assurance. You can use this testsuite to check if
OJB works smoothly in your target environment.
(see supported platforms documentation)
We also provide a performance testsuite that compares OJB performance against native JDBC.
This test will give you an impression of the performance impact OJB will have in your target
environment.
(see Performance testsuite documentation)
OJB is also the persistence layer of choice in several books on
programming J2EE based enterprise business systems.
(see our links and references section)
Reference projects and user testimonials are listed
here.
|
How can Collections of primitive typed elements be mapped? |
The first thing to ask is:
How are these primitive typed elements (Strings are also treated as primitive types here)
stored in the database.
1) are they treated as ordinary domain objects and stored in a separate table?
2) are they serialized into a Varchar field?
3) are they stored as a comma separated varchar field?
4) is each element of the vector or array stored in a separate column?
(this solution does only work for a fixed number of elements!)
Follow these steps for solution 3):
a) simply define ordinary collection-descriptors as for every other collection of domain objects.
b) use the Object2ByteArrFieldConversion. See jdbc-types.html for details on conversion strategies.
c) use the StringVector2VarcharFieldConversion. See jdbc-types.html for details on conversion strategies.
d) provide a field-descriptor for each element.
|
How to page and sort? |
Sorting can be configured by
org.apache.ojb.broker.query.Criteria::orderBy(column_name).
There is no paging support in OJB. OJB is concerned with Object/Relational
mapping and not with application specific presentation details like
presenting a scrollable page of items.
OJB returns query results as Collections or Iterators.
You can easily implement your partial display of result data by using an
Iterator as returned by ojb.broker.PersistenceBroker::getIteratorByQuery(...).
|
What about performance and memory usage if thousands of objects matching a query are returned as a Collection? |
You can do two things to enhance performance if you have to process queries
that produce thousands of result objects:
-
Use getIteratorByQuery() rather than getCollectionByQuery().
The returned Iterator is lazy and does not materialize Objects in
advance.Objects are only materialized if you call the Iterators
next() method. Thus you have total control about when and how many
Objects get materialized!
-
You can define Proxy Objects as placeholder for your
persistent business objects. Proxys are lighweight objects that
contain only primary key information. Thus their materialization is
not as expensive as a full object materialization. In your case this
would result in a collection containing 1000 lighweight proxies.
Materialization of the full objects does only occur if the objects
are accessed directly. Thus you can build similar lazy paging as
with the Iterator. You will find examples in the packages
test.ojb.broker
The Perfomance of 1. will be better than 2. This approach will
also work for VERY large resultsets, as there are no references to
result objects that would prevent their garbage collectability.
|
How is OJB related to ODMG and JDO? |
ODMG is a standard API for Object Persistence specified by the ODMG
consortium (www.odmg.org).
JDO is Sun's API specification for Object Persistence. ODMG may
well be regarded as a Precursor to JDO.
In fact JDO incorporates
many ideas from ODMG and several people who have been involved in
the ODMG spec are now in the JDO team.
I assume JDO will have
tremendous influence on OODBMS-, RDBMS-, J2EE-server and O/R-tool-vendors
to provide compliant products.
OJB wants to provide first class support for JDO and ODMG APIs.
OJB currently contains of four main layers, each with its own API:
-
A low-level PersistenceBroker API which serves as the OJB persistence
kernel. The PersistenceBroker also provides a scalable
multi-server architecture that allows to used it in heavy-duty
app-server scenarios.
This API can also be used directly by applications that don't need full
fledged object level transactions (see
tutorial1 for details).
-
An Object Transaction Manager (OTM) layer that contains all features that
JDO and ODMG have in common as Object level transactions, lock-management,
instance lifecyle etc.
(See jdo-proposal.html for details.)
The OTM is work in progress.
-
A full featured ODMG 3.0 compliant API. (See
tutorial2 for an introduction.)
Currently this API is implemented on top the PersistenceBroker. Once the
OTM layer is finished ODMG will be implemented on top of OTM.
-
A JDO compliant API. This is work in progress.
(See
tutorial4 for an introduction.)
Currently this API is implemented on top the PersistenceBroker. Once the
OTM layer is finished JDO will be implemented on top of OTM.
The following graphics shows the layering of these APIs. Please note that the
layers coloured in yellow are not yet implemented.
|
What are the differences between the PersistenceBroker API and the ODMG API? Which one should I use in my applications? |
The PersistenceBroker (PB) provides a minimal API for transparent
persistence:
- O/R mapping
- Retrieval of objects with a simple query interface from RDBMS
- storing (insert, update) of objects to RDBMS
- deleting of objects from RDBMS
This is all you need for simple applications as in tutorial1.
The OJB ODMG implementation uses the PB as its persistence kernel.
But it provides much more functionality to the application developer.
ODMG is a full fledged API for Object Persistence, including:
- OQL Query interface
- real Object Transactions
-
A Locking Mechanism for management of concurrent threads (apps)
accessing the same objects
- predefined persistent capable Collections and Hashtables
Some examples explaining the implications of these functional differences:
-
Say you use the PB to query an object O that has a collection
attribute col with five elements a,b,c,d,e. Next you delete Objects
d and e from col and store O again with
PersistenceBroker.store(O);
PB will store the remaining objects a,b,c. But it will not delete d and
e ! If you then requery object O it will again contain a,b,c,d,e !!!
The PB keeps no transactional state of the persistent Objects, thus it
does not know that d and e have to be deleted. (as a side note: deletion
of d and e could also be an error, as there might be references to them
from other objects !!!)
Using ODMG for the above scenario will eliminate all trouble: Objects
are registered to a transaction so that on commit of the transaction it
knows that d and e do not longer belong to the collection. the ODMG
collection will not delete the objects d and e but only the REFERENCES
from the collection to those objects!
-
Say you have two threads (applications) that try to access
and modify the same object O. The PB has no means to check whether
objects are used by concurrent threads. Thus it has no locking
facilities. You can get all kind of trouble by this situation. The
ODMG implementation has a Lockmanager that is capable of
synchronizing concurrent threads. You can even use four transaction
isolation levels:
read-uncommitted, read-committed,
repeatable-read, serializable.
In my eyes the PB is a persistence kernel that can be used to
build high-level PersistenceManagers like an ODMG or JDO
implementation. It can also be used to write simple applications, but
you have to do all management things (locking, tracking objects
state, object transactions) on your own.
|
When is it helpful to use Proxy Classes? |
Proxy classes can be used for "lazy loading" aka
"lazy materialization". Using Proxy classes can help you in
reducing unneccessary db lookups. Example:
Say you load a ProductGroup object from the db which contains a
collection of 15 Article objects.
Without proxies all 15 Article objects are immediately loaded from
the db, even if you are not interested in them but just want to
lookup the description-attribute of the ProductGroup object.
With a proxy class, the collection is filled with 15 proxy
objects, that implement the same interface as the "real objects"
but contain only an OID and a void reference.
Once you access such a proxy object it loads its "real
subject" by OID and delegates the method call to it.
have a look at tutorial3.html.
This document has a large section on proxies.
|
The OJB JDO implementation is not finished, how can I start using OJB? |
I recommend to not use JDO now, but to use the existing ODMG api
for the time being.
Migrating to JDO later will be smooth if you follow the following steps.
I recommend to first divide your model layer into Activity- (or Process-)
classes and Entity classes.
Entity classes represent classes that must be made persistent at some
point in time, say a "Customer" or a "Order" object.
These persistent classes and the repsective O/R mapping in repository.xml will
remain unchanged.
Activities are classes that perform business tasks and work upon
entities, e.g. "edit a Customer entry", "enter a new Order"...
They implement (parts of) use cases.
Activities are driving transactions against the persistent storage.
I recommend to have a Transaction interface that your Activities can
use. This Transaction interface can be implemented by ODMG or by JDO
Transactions (which are quite similar). The implementation should be made
configurable to allow to switch from ODMG to JDO later.
The most obvious difference between ODMG and JDO are the query
languages: ODMG uses OQL, JDO define JDOQL.
As an OO developer you won't like both of them.
I recommend to use the ojb Query objects that allow an abstract syntax
representation of queries.
It is possible to use these queries within ODMG transactions and it will
also be possible to use them within JDO Transactions. (this is contained in the FAQ too).
Using your own Transaction interface in conjunction with the OJB query
api will provide a simple but powerful abstraction of the underlying
persistence layer.
We are using this concept to provide an abstract layer above OJB-ODMG,
TopLink and LDAP servers in my company.
Making it work with OJB-JDO will be easy!
|
How could class 'myClass' represent a collection of 'myClass' objects |
OJB can handle such recursive associations without problems.
-
add a collection attribute 'myClasses' to the class
myClass
this collection will hold the associated myClass objects.
-
you have to decide wether this assosciation is 1:n or m:n.
for 1:n you just need an additional foreignkey attribute in the MY_CLASS
table. Of course you'll also need a matching attribute in the class myClass .
For a m:n association you'll have to define a intermediary table to hold the
mapping entries.
-
define a
collection-descriptor tag in the class-descriptor
of myClass in repository.xml.
Follow the steps in tutorial3.html on 1:n and m:n.
|
How does OJB handle connection pooling? |
OJB does connection pooling per default, expect for datasources. Datasources never will be pooled.
Responsible for managing the connections in OJB are implementations of the
org.apache.ojb.broker.accesslayer.ConnectionFactory.java
interface. There are several implementations shipped with OJB called
org.apache.ojb.broker.accesslayer.ConnectionFactoryXXXImpl.java .
You can find among other things a none pooling implementation and a implementation
using jakarta-DBCP api.
To manage the connection pooling define in your jdbc-connection-descriptor a
connection-pool element. Here you can specify which ConnectionFactory implementation
should be used. More info see repository section
or repository.dtd.
|
Can OJB handle ternary (or higher) associations? |
Yes, that's possible. Here is an example.
With a ternary relationship there are three (or more) entities
'related' to each other.
An example would be Developer , Language and Project .
Each entity is mapped to one table (DEVELOPER , LANGUAGE and
PROJECT ). To represent the combinations
of these entities we need an additional bridge table
(PROJECTRELATIONSHIP )with three
columns holding the foreign keys to
the other three tables (just like an m:n association is represented
by an intermediary table with 2 columns).
To handle this table with OJB we have to define a class that is mapped on it.
This Relationship class can then be used to perform
queries/updates as with any other persistent class. Here is the layout
of this class:
 |  |  |
 |
public class ProjectRelationship {
Integer developerId;
Integer languageId;
Integer projectId;
Developer developer;
Language lanuage;
Project project;
/** setters and getters not shown for brevity**/
}
|  |
 |  |  |
Here is the respective extract from the repository :
 |  |  |
 |
<class-descriptor
class="ProjectRelationship"
table="PROJECTRELATIONSHIP"
>
<field-descriptor
name="developerId"
column="DEVELOPER_ID"
jdbc-type="INTEGER"
primarykey="true"
/>
<field-descriptor
name="languageId"
column="LANGUAGE_ID"
jdbc-type="INTEGER"
primarykey="true"
/>
<field-descriptor
name="projectId"
column="PROJECT_ID"
jdbc-type="INTEGER"
primarykey="true"
/>
<reference-descriptor
name="developer"
class-ref="Developer"
>
<foreignkey field-id-ref="developerId" />
</reference-descriptor>
<reference-descriptor
name="language"
class-ref="Language"
>
<foreignkey field-id-ref="languageId" />
</reference-descriptor>
<reference-descriptor
name="project"
class-ref="Project"
>
<foreignkey field-ref="projectId" />
</reference-descriptor>
</class-descriptor>
|  |
 |  |  |
Here is some sample code for storing a relationship :
 |  |  |
 |
Developer dev = .... ; // create or retrieve
Project proj = .... ; // create or retrieve
Language lang = .... ; // create or retrieve
ProjectRelationship rel = new ProjectRelationship();
rel.setDeveloper(dev);
rel.setLanguage(lang);
rel.setProject(proj);
broker.store(r);
|  |
 |  |  |
In the next code sample we are looking up all
Projects that Developer "Bob" has done in "Java".
 |  |  |
 |
Criteria criteria = new Criteria();
criteria.addEqualTo("developer.name","Bob");
cirteria.addEquatTo("language.name","Java");
Query q = new QueryByCriteria(ProjectRelationship.class, criteria, true);
Iterator iter = Broker.getIteratorByQuery(q);
// now iterate over the collection and retrieve all projects:
while (iter.hasNext())
{
ProjectRelationship rel = (ProjectRelationship) iter.next();
System.out.println(rel.getProject().toString());
}
|  |
 |  |  |
You could also have on the Project class-descriptor a
collection-descriptor that returns all relationships associated with the
Project. If it was call "projectRelationships" the following would give you
all projects that have a relationship with "bob" and the language "java".
 |  |  |
 |
Criteria criteria = new Criteria();
criteria.addEqualTo("projectRelationships.developer.name","bob");
cirteria.addEquatTo("projectRelationships.language.name","java");
Query q = new QueryByCriteria(Project.class, criteria, true);
Collection projects = Broker.getCollectionByQuery(q);
|  |
 |  |  |
This is the layout of the Project class:
 |  |  |
 |
public class Project {
Integer id;
String name;
Collection projectRelationships;
/** setters and getters not shown for brevity**/
}
|  |
 |  |  |
This is the class-descriptor of the Project class:
 |  |  |
 |
<class-descriptor
class="Project"
table="PROJECT"
>
<field-descriptor
name="id"
column="ID"
jdbc-type="INTEGER"
primarykey="true"
/>
<field-descriptor
name="name"
column="NAME"
jdbc-type="VARCHAR"
/>
<collection-descriptor
name="projectRelationships"
element-class-ref="ProjectRelationship"
>
<inverse-foreignkey field-ref="projectId" />
</collection-descriptor>
</class-descriptor>
|  |
 |  |  |
|
How to map a list of Strings |
You can not map a list of Strings with a collection descriptor. A collection
descriptor can only be used if the element class is a persistent class too.
But element-class-ref="java.lang.String" won't work, because it's no
persistent entity class!
Follow these steps to provide a mapping for an attribute holding alist of Strings.
Let's assume your persistent class has an attribute listOfStrings
holding a list of Strings:
 |  |  |
 |
protected Collection listOfStrings;
|  |
 |  |  |
The database table mapped to the persistent class has a colum LIST_OF_STRINGS of type
VARCHAR that is used to hold all strings.
 |  |  |
 |
<field-descriptor
name="listOfStrings"
column="LIST_OF_STRINGS"
jdbc-type="VARCHAR"
conversion=
"o.a.ojb.broker.accesslayer.conversions.StringVector2VarcharFieldConversion"
/>
|  |
 |  |  |
|
How to set up Optimistic Locking |
-
In your table you need a dedicated column of type
INTEGER
or TIMESTAMP .
Say the column is typed as INTEGER and named
VERSION_MAINTAINED_BY_OJB .
-
You then need a (possibly private) attribute in your java class corresponding to the column.
Say the attribute is defined as:
 |  |  |
 |
private int versionMaintainedByOjb;
|  |
 |  |  |
-
in repository.xml you need a field-descriptor for this attribute.
this field-descriptor must specify
locking="true"
The resulting field-descriptor will look as follows:
 |  |  |
 |
<field-descriptor
name="versionMaintainedByOjb"
column="VERSION_MAINTAINED_BY_OJB"
jdbc-type="INTEGER"
locking="true"
/>
|  |
 |  |  |
For further reference see also
the repository documentation.
|
How to use OJB in a cluster |
Q: I'm running a web site in a load-balanced/cluster environment.
Multiple servlet engines (different VMs/HTTP sessions), each running an OJB
instance, against a single shared database.
How should OJB be configured to get the concurrent
servlet engines synchronized properly?
transactional isolation and locking
If you are using the PersistenceBroker API
use optimistic locking (OL) to let OJB handle write conflicts.
To use OL define a TIMESTAMP or INTEGER column and the respective Java attribute for it.
In the field-descriptor of this attribute set the attribute
locking="true"
If you are working with the ODMG API use the PersistenLockMapImpl,
by setting the respective flag in OJB.properties.
sequence numbers
Use a SequenceManager that is safe across multiple JVMs.
The NextVal based SequenceManagers or any other SequenceManager
based on database mechanisms will be fine.
caching
You could
-
Use the EmptyCacheImpl to avoid any dirty reads. (But: The EmptyCache
cannot handle cyclic structures on load!)
-
Use the PerBrokerCache Implementation. Could result in dirty reads, but
together with Optimistic locking this should be OK.
-
Use the JCS cache implementation (see OJB.properties).
JCS will coordinate the distributed
caches on all clients. JCS will involve some RMI communication between the
caches. Please have a look at the JCS site for configuration details.
There is also a
complete howto document
available that covers these topics.
|
How to work with the ObjectCacheEmptyImpl |
Q: I just tried to turn caching off by using ObjectCacheEmptyImpl setting
in ObjectCacheClass, and it seems to continuously loop through the SQL
statement infinitely. The default works fine though.
Any ideas why this might be?
A: The Problem you see is due to circular references in your data.
Say A references B and B has a backreference to A.
Now we load A from the DB. If autoretrieve="true" for the reference-descriptor
defining the reference to B, OJB will also load B.
If autoretrieve="true" for the B-reference-descriptor describing
the back-reference to A, OJB must retrieve A. And here is the key point.
If we use the defaultcache A will be in the cache already, as it was loaded first.
So OJB will simply lookup A from the cache. No endless recursion!
But if we use the emptycache, A will not be cached. So OJB must load A from the DB.
And then again B is retrieved, etc., etc.
There's you endless recursion.
In other words: A non-empty cache is needed to allow proper loading of circular
references. (Other O/R tools like TopLink work the same way).
If you still want to use the EmptyCacheImpl you should set autoretrieve="false"
and load references explicitely by broker.retrieveReference(...).
|
Why must my persisten class implement javax.jdo.spi.PersistenceCapable |
As specified by JDO all persistent classe must implement the interface
javax.jdo.spi.PersistenceCapable .
If a class does not implement this interface a JDO implementation does not know how to
handle it.
On the other hand the JDO spec claims to provide transaparent persistence.
That is no persistence class is required to implement a specific interface or
to be derived from a special base class.
Sounds like a contradiction? It is! The JDO spec resolves this contradiction by
stating that a JDO implemention is responsible to add the methods required by
javax.jdo.spi.PersistenceCapable to the the user classes.
This "injection" could be achieved by Pre- or Post-processing.
The strategy most implementations use is called "bytecode-enhancement".
This is a postprocesing step that adds the required methods to the .class files
of the persistent user classes.
The JDO Reference implementation also uses bytecode-enhancement.
In order to enhance the Product class to implement the
javax.jdo.spi.PersistenceCapable interface use the ant target
"enhance-jdori" before launching the tutorial5 application.
This is documentated in the first section of tutorial4.html.
|
|
|