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:

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

UML class diagram
// 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

Database schema diagram
CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    faxNumber VARCHAR(255),
    faxAreaCode VARCHAR(255)
)

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

UML class diagram
// 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

Database schema diagram
CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    favouriteCheese INTEGER id
)

CREATE TABLE Cheese (
    id INTEGER PRIMARY KEY,
    name VARCHAR(255),
    strength INTEGER
)

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

UML class diagram
// 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

Database schema diagram
CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
)

CREATE TABLE Person_Orders (
    id INTEGER PRIMARY KEY,
    date DATETIME,
    amount INTEGER,
    description VARCHAR(255)
)

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.