CMP Fields

CMP Fields are used to define the persistent attributes of a Entity. These are the basic properties that will be saved in the store. The EJB specification limits these to being:

  • primitive types, such as int or double
  • Serializable objects, which includes core classes such as Integer and String

The specification does not define how these properties are mapped to an underlying store; this was done deliberately to provide flexibility for Container providers in allowing Entities to be persisted in a wide variety of stores, including relational databases, XML databases, LDAP directories, or even raw files. It specifically does not define how Serializable types such as BigDecimal or Date are stored, leading to different levels of support by different vendors (although most implement some form of mapping for common Java types).

Defining a CMP Field in ejb-jar.xml

The CMP fields for an entity are declared inside its definition:

  
    Entity representing a Customer of ours
    Customer
    ...
    
      The customer's name
      name
    
    ...
  

The EJB specification is strict in defining that the name of the cmp-field must begin with a lower case letter. This has the consequence of making the use of abbreviations for field names awkard. For example, a field intended to hold a Social Security Number could be defined as "ssn" which would result in an accessor method "getSsn()"; alternatively, an accessor "getSSN()" would require the field to be defined as "sSN". It is often simpler to avoid abbreviations entirely.

Accessing a CMP Field value

To allow the Container to remain in control of loading and storing CMP field values, the EJB does not define members for CMP fields. Instead, they are defined as 'virtual' properties that can be accessed using JavaBean style accessors. For example, the cmp-field 'name' would be defined using the methods:

    public abstract String getName();
    public abstract void setName(String name);

The methods must be declared abstract; the implementation will be provided by CMP. Both methods must be defined.

Field types directly supported by JBoss CMP-JDBC

The standard mappings for field types directly supported by JBoss is listed in the following table. In addition to these, mappings can be defined for specific classes using Dependent Value Classes.

Java Type JDBC Type Set Method Get Method Notes
boolean BIT setObject getBoolean  
byte TINYINT setObject getByte  
short SMALLINT setObject getShort  
int INTEGER setObject getInt  
long BIGINT setObject getLong  
float FLOAT setObject getFloat  
double DOUBLE setObject getDouble  
char VARCHAR setObject getString Only the first character is returned.
java.lang.Boolean BIT setObject getBoolean  
java.lang.Byte TINYINT setObject getByte  
java.lang.Short SMALLINT setObject getShort  
java.lang.Integer INTEGER setObject getInt  
java.lang.Long BIGINT setObject getLong  
java.lang.Float FLOAT setObject getFloat  
java.lang.Double DOUBLE setObject getDouble  
java.lang.Character VARCHAR setObject getString Only the first character is returned.
java.lang.String VARCHAR setObject getString  
CLOB setCharacterStream getCharacterStream  
LONGVARCHAR setCharacterStream getCharacterStream  
java.sql.Date DATE setObject getDate  
java.sql.Time TIME setObject getTime  
java.sql.Timestamp TIMESTAMP setObject getTimestamp  
java.util.Date DATE setObject getTimestamp  
TIME setObject getTimestamp  
TIMESTAMP setObject getTimestamp  
java.math.BigDecimal DECIMAL setBigDecimal getBigDecimal  
NUMERIC setBigDecimal getBigDecimal  
Any BINARY setBytes getBytes Object is Serialized
VARBINARY setBytes getBytes Object is Serialized
BLOB setBinaryStream getBinaryStream Object is Serialized
LONGVARBINARY setBinaryStream getBinaryStream Object is Serialized

Issues with serialized fields

In the absence of a defined mapping, JBoss will attempt to serialize the field value into a byte array and then store that in a database column. Whilst this provides a fallback mechanism, care must be taken regarding the following issues:

  • The stored object will be treated as an opaque array of bytes by the database and can not be used for queries either via JDBC or via other database access tools.
  • The serialized form of the object may be considerably larger than the in-memory representation.
  • The use of BLOB type columns may result in different physical storage of the object; for example, the data may be stored on different physical disks.
  • The use of BLOB type columns may affect transactional or recovery behaviour; some databases do not maintain multiple versions of large objects to support isolation or rollback. Also large objects may not be logged preventing recovery of committed transactions.
  • The values stored in the database may be from multiple versions of the application; the class being serialized should override readObject and allow unmarshalling of older versions.

The cmp accessor for a Serialized object returns a reference to the value loaded by JBoss. Whether modifications made to this object will be persisted is undefined. To be sure, applications should call the set accessor to indicate that the cmp field has been modified. They should also take a deep copy of the object before returning to a client.

The following code snippet illustrates a way of handling a serialized object:

  public void addAddress(String type, Address address) {
      Map addresses = getAddresses();
      addresses.put(type, address.clone());
      setAddresses(addresses);  // indicate map has been modified
  }


  public Address getAddress(String type) {
      return (Address) getAddresses().get(type).clone();
  }


  public Map getAllAddresses() {
      Map addresses = getAddresses();
      addresses = addresses.clone();
      for (Iterator i = addresses.entrySet().iterator; i.hasNext();) {
          Map.Entry entry = (Map.Entry) i.next();

          Object value = entry.getValue();
          entry.setValue(value.clone());
      }
      return addresses;
  }



  public abstract Map getAddresses();
  public abstract void setAddresses(Map addresses);

This assumes that the Address class has overridden clone() to perform a deep copy.

Definition using XDoclet

A CMP field can be declared in XDoclet using the @ejb-persistence tag:

    /**
     * The Customer's name
     * @return this Customer's name
     * @ejb.persistence column-name="NAME"
     */
    public abstract String getName();

The column-name attribute is required.

Older versions of XDoclet used an @ejb.persistent-field tag to designate an accessor but as of V1.2 this has been deprecated.