Orion Server: Advanced object-relational mapping
This is a Java/J2EE tutorial for Orion Application Server.
When using container-managed-persistence with entity beans, the EJB container can automatically map fields of your bean to a corresponding database table. This is fairly trivial and is explained in most EJB tutorials.
A common problem is that it’s hard to map anything but a simple bean with simple fields to a database without writing custom code to do the mapping. This tends to segment development of entity beans into two categories:
- Plain object models that use CMP (Container Managed Persistence)
- Practical object models the use BMP (Bean Managed Persistence)
The plain object models often end up complicating development and disallowing the use of many advantageous OO techniques. Practical object models on the other hand allow developers to harness the full potential of OO, but creating lots more work for them to map the objects to a database effectively. In reality, most developers will take the easy way out by using CMP and settle for less than satisfactory object models.
However, Orion has a very effective OR (Object/Relational) mapping system built into it, allowing complicated object models to be mapped to database tables with ease, allowing a practical object model that uses CMP.
See Object-relational Mapping.
Fields that can be mapped
The following types of fields can be mapped within entity beans:
Simple objects and primitives
These can be mapped directly to one field in the table.
e.g. int
,
char
, boolean
,
long
,
java.lang.String
,
java.util.Date
Objects
These are compound objects that can have
each individual field or property mapped
(fields are accessed directly, whilst
properties are accessed through the JavaBean
getXxx()
,
setXxx()
methods). Each
field/property is mapped to the same table
using a method suitable (depending on it’s
type). This method can be any of the mapping
types in this list.
Serializable objects
These are compound objects that are
mapped by serializing them and storing the
raw bytes in a BLOB
(or
database equivalent) field. Ideally, these
should be used as little as possible because
they only have meaning to the Java VM - they
could not be used as criteria in an SQL
WHERE
clause, for example.
Entity references
A reference to another entity bean which is mapped by storing its primary-key in a field in the table.
Collections
To map collections to the database, another table is used to store the values of the collection (as per standard database normalization). This table contains the primary-key of the entity containing the collection, and appropriate fields for object stored in the collection. The objects stored in the collection can be mapped using any of the mappings in this list.
The OR mapping can support mapping for
arrays, Java2 collections
(Collection
, List
, Set
, Map
), and
the legacy pre-Java2 collections
(Vector
,
Hashtable
).
With these mappings, complicated object-models can be mapped to a database with ease.
Configuring the mapping
To do this you must get your hands dirty
and play about with the XML Deployment
Descriptor in orion-ejb-jar.xml
.
For every entity bean deployed to Orion
in the EJB module, an
<entity-deployment>
tag
will be added to this file. For every field
to be mapped, a
<cmp-field-mapping>
tag
is added.
This is just a simple explanation of how to use this tag. For a detailed reference, look at the documentation for orion-ejb-jar.xml or the DTD.
Each
<cmp-field-mapping>
tag
must at the very least contain a
name
attribute, stating the
field-name that is to be persisted.
Simple Mappings
If the field is a simple mapping (simple
objects, primitives, or serializable object)
that is to be mapped directly to one field,
then it should contain a
persistence-name
attribute
stating the name of the corresponding
database field to map to. Optionally, a
persistence-type
attribute can
be specified to tell Orion the type of
database field to use when auto-creating
tables (e.g. Typically
java.lang.String
are mapped to
VARCHAR(255)
, however
sometimes it may be required to store values
that have more than 255 characters, in which
case a BLOB
may be used
instead). If no
persistence-type
is specified,
the default type from the database-schema
specified in the data-source shall be used
instead.
e.g.
cmp-field-mapping name="address"
< persistence-name="address" persistence-type="blob" />
Compound Object Mappings
To map a field that is an object
containing its own fields or properties, the
<cmp-field-mapping>
must
contain a body specifying each of its
fields/properties and how to map them. This
is done by enclosing a
<fields>
or
<properties>
tag within
the <cmp-field-mapping>
,
and then placing appropriate
<cmp-field-mapping>
tags
within these for each field to be
mapped.
e.g.
cmp-field-mapping name="fax">
<fields>
<cmp-field-mapping name="number"
< persistence-name="faxNumber" />
cmp-field-mapping name="areaCode"
< persistence-name="faxAreaCode" />
fields>
</cmp-field-mapping> </
Object Model

// PersonEJB.java
public class PersonEJB implements javax.ejb.EntityBean {
public int id;
public PhoneNumber fax;
....
}
// PhoneNumber.java
public class PhoneNumber {
public String number;
public String areaCode;
}
Database Schema

CREATE TABLE Person (
id INTEGER PRIMARY KEY,
VARCHAR(255),
faxNumber VARCHAR(255)
faxAreaCode )
Entity Bean Reference Mappings
If a field is a reference to another
entity bean, the
<cmp-field-mapping>
tag
should contain an
<entity-ref>
tag with a
home
attribute specifying the
JNDI location of the other entity bean’s
home interface. This tag contains a
<cmp-field-mapping>
tag
for mapping the primary-key of the other
entity to the database table.
e.g.
cmp-field-mapping name="favouriteCheese">
<entity-ref home="jndi/for/CheeseHome">
<cmp-field-mapping name="favouriteCheese"
< persistence-name="favouriteCheese" />
entity-ref>
</cmp-field-mapping> </
Object Model

// PersonEJB.java
public class PersonEJB implements javax.ejb.EntityBean {
public int id;
public Cheese favouriteCheese;
...
}
// Cheese.java
import java.rmi.RemoteException;
// EJB Remote interface
public interface Cheese extends javax.ejb.EJBObject {
public int getId() throws RemoteException;
public String getName() throws RemoteException;
public void setName(String name) throws RemoteException;
public int getStrength() throws RemoteException;
public void setStrength(int strength) throws RemoteException;
}
Database Schema

CREATE TABLE Person (
id INTEGER PRIMARY KEY,
INTEGER id
favouriteCheese
)
CREATE TABLE Cheese (
id INTEGER PRIMARY KEY,
VARCHAR(255),
name INTEGER
strength )
Collection Mappings
To map a collection of values, the body
of <cmp-field-mapping>
should contain
<collection-mapping>
,
<list-mapping>
,
<set-mapping>
, or
<map-mapping>
. These
tags all have a table
attribute
which corresponds to the name of the
database table for storing the lookup values
in.
These tags also contain
<primkey-mapping>
and
<value-mapping>
. If the
collection is a map (or hashtable), it must
also contain
<map-key-mapping>
for
mapping the key.
The <value-mapping>
and <map-key-mapping>
must contain a type
attribute
which is the full qualified Java class name
of the object being mapped in the
collection.
The <primkey-mapping>
, <value-mapping>
and
<map-key-mapping>
all
contain a
<cmp-field-mapping>
which
specify how the actual values are
mapped.
e.g.
cmp-field-mapping name="orders">
<list-mapping table="Person_Orders">
<primkey-mapping>
<cmp-field-mapping name="id"
< persistence-name="id" />
primkey-mapping>
</value-mapping type="Order">
<cmp-field-mapping />
<fields>
<cmp-field-mapping name="date"
< persistence-name="date" />
cmp-field-mapping name="amount"
< persistence-name="amount" />
cmp-field-mapping name="description"
< persistence-name="description" />
fields>
</cmp-field-mapping>
</value-mapping>
</list-mapping>
</</cmp-field-mapping>
Object Model

// PersonEJB.java
public class PersonEJB implement javax.ejb.EntityAdapter {
public int id;
public java.util.List orders;
...
}
// Order.java
public class Order {
public java.util.Date date;
public int amount;
public String description;
}
Database Schema

CREATE TABLE Person (
id INTEGER PRIMARY KEY,
)
CREATE TABLE Person_Orders (
id INTEGER PRIMARY KEY,
date DATETIME,
INTEGER,
amount VARCHAR(255)
description )
Shortcuts
Phew! This all seems a bit much - it was
a lot nicer when we didn’t have to touch the
orion-ejb-jar.xml
file at all
and let Orion auto-generate it all for
us.
Thankfully, Orion does its best to make this kind of stuff easier. If you leave something out of the file, Orion will automatically generate what it needs to using defaults (although usually very smart defaults). You only need to specify mappings for fields that you do not want mapped the default way.
Typically, if you have a
<cmp-field-mapping>
with
just a name
attribute, the
persistence-name
and
persistence-type
attributes are
automagically determined by Orion. By
default, each field-mapping shall be mapped
as a simple object (or primitive), an entity
reference, or if neither of those can be
determined, a serialized object.
If one of the fields to be mapped is a
compound object with its own fields or
properities, an entry needs to be add
containing just one empty
<fields>
or
<properties>
tag and all
the individual fields or properties will be
mapped.
If mapping a collection, the
<primkey-mapping>
will
also be auto-generated.