When deploying enterprise beans with container managed persistence (CMP), non-primitive fields do not have a natural Java™Database Connectivity (JDBC™) mapping. The top-down mapping approach maps these CMP fields to binary types in the database, where they are stored as serialized data. For example, in DB2 Universal Database™ (DB2® UDB) they will be mapped to the varchar format for bit data. This puts a restriction on using these fields in Enterprise JavaBeans™ Query Language (EJB®-QL) queries. Also, they cannot be used as object fields directly in your reports.
To work around these restrictions, IBM provides a feature called EJB composers in IBM® Rational® Application Developer (IRAD). Using the EJB composers, you can map a single complex EJB CMP field to multiple relational database (RDB) columns. For example, if the CMP type is a Java object X with an int field and a string field, then you can map X to two RDB fields, INTEGER and VARCHAR, using a composer.
With supporting examples, design, and code, this article proposes a scenario that shows you how to use composers. Using the IRAD Create Composer wizard, you will create a composer and use it in your EJB-RDB mapping. This article will also discuss various classes involved in the process.
This article will first describe a simple Customer-Address scenario to show the advantages of using multiple RDB fields. Figure 1 shows two enterprise beans, Customer and NewCustomer. Each of these EJB components uses the Java™ object ejbs.Address as its attribute.
Figure 1. Customer-Address scenario
Address is a simple Java object and stores address information like street, apartment, city, state, and zip code.
If you use the default top-down EJB-RDB schema mapping, the Address attribute will be mapped to Varchar(1000) for bit data. It means the Address object will be streamed as binary data to the varchar RDB column.
Consider a situation where a company using the above scenario requires a report on all its customer addresses. This will not be a straightforward task, like selecting data from an RDB table using a SQL query. Instead, it will require parsing binary data by reverse engineering (that is, by creating a Java object from binary data and using it to split the attributes of the Address Java object).
Another way you could solve this is by redesigning the above scenario in such a way that Address is not a Java object, but instead is another CMP entity bean with a container-managed relationship (CMR) between Address and the Customer entity bean. This method will have disadvantages associated with a high number of unnecessary entity beans. In fact, the cost associated with entity beans will always be high, compared to non-frequent tasks, like reporting.
EJB-QL is another place where default mapping for the previous scenario will cause problems. If your application requires you to use a query on an entity bean, where the query needs to check the value of one of the attributes of the CMP field of the entity bean, it wonât be possible with default mapping. For example, again in the previous scenario, your application requires you to find all the customers who stay in or around a particular zip code. The first answer to this problem that comes to mind is to add the findByZip ejb query as shown in Listing 1.
Listing 1. Adding a query
<query>
<description></description>
<query-method>
<method-name>findByZip</method-name>
<method-params>
</method-params>
</query-method>
<ejb-ql>select object(o) from Customer o where o.address.zip is null</ejb-ql>
</query>
|
This will not solve the problem in default mapping. This is because o.address.zip is not a valid EJB attribute, so it cannot be directly used in EJB queries in the select clause.
Here is where the composer feature comes into the picture; composers enable one CMP field to be mapped to multiple columns. The composer class is assigned to a CMP field and used to map the CMP field attributes to correct columns in the newly created table. In the Customer-Address scenario -- in order to map the Address attribute of the EJB component Customer to multiple columns in a table -- you need to create a new compose class that defines which fields in the composed type class map to which columns in the database.
IRAD provides a Create Composer or Converter wizard to help you create a composer. This wizard helps you to create a definition of your composer, but a few additional manual steps are required to define composer mapping.
- Open the Java™2 Platform, Enterprise Edition (J2EE™) perspective: select Window -> Open perspective -> Other, then select J2EE and click OK.
- Select File -> New -> Other, which will open the New wizard window, shown in Figure 2.
Figure 2. The New wizard window
- In the New wizard window, select EJB -> Converter or Composer, then click Next.
- Your next view will be the New EJB Converter or Composer wizard. Select the Composer option button as shown in Figure 3.
Figure 3. New EJB Composer or Converter wizard
- Select an EJB project.
- Type a fully-qualified composer name. It is always recommended to give your composer a name that suits its usage. This should be the name of the class for the instance instantiated as a result of the objectFrom in stub class, which is discussed in the Manual code change section. For example, in your scenario you are going to use this composer to map the Address class, so name it AddressComposer.
- Select a fully qualified supertype, which is the composer from which a newly created composer is inherited. In IRAD it could be:
- com.ibm.vap.composers.Namecomposer
- com.ibm.vap.composers.VapUSPhoneNumberComposer
- com.ibm.vap.composers.VapAttributeComposer
Select VapAttributeComposer in this scenario.
- Type or select a fully qualified target type. This is the class, which has to be mapped using the newly created composer.
- Add the New composed field type. Composer fields represent the actual mapping of object attributes. You must add all the required composed field types, which is to say one composer field for each column in the RDB column to be used. The new composer field type can be added by typing or selecting New Composer field type and clicking the Add button.
- After adding the New composed field type, it is recommended that you rename it to an appropriate name. In your scenario, you added the following composed field types:
- java.lang.String -- street
- java.lang.String -- apt
- java.lang.String -- city
- java.lang.String -- state
- java.lang.Integer -- zip
- If required, you can remove an added composed field type by selecting it and clicking the Remove selected field button
- Optional: Select the Generate a composer stub class checkbox to generate a new composer class template for the new composer definition. The composer stub class is discussed in detail in the Manual code change section.
- Click Finish. If you selected Generate a composer stub class in Step 12, you should see a composer class generated in the proper package. When you open the Mapping editor, the composer name that you defined appears in the Outline view.
The next step is to create a composer stub class, or complete the composer stub class generated by the Create Composer wizard. This class is responsible for mapping attributes to the desired RDB columns. If the Generate a composer stub class checkbox was selected in Step 12 of the Using the wizard section, the AddressComposer class shown in Listing 2 is automatically generated.
Listing 2. AddressComposer class
package composers;
/** .. */
public class AddressComposer extends com.ibm.vap.composers.VapAttributeComposer {
static AddressComposer singleton = null;
public static String getTargetClassName() {
return "ejbs.Address";
}
public Object[] dataFrom(Object anObject) {
// Default shown is for a Name composer with title, first name, and last name.
/* Name name = (Name) anObject;
Object[] anArray = new Object[3];
if (anObject == null) {
Object[] anArray2 = { null, null, null };
return anArray2;
} else {
anArray[0] = name.getTitle();
anArray[1] = name.getFirstName();
anArray[2] = name.getLastName();
}
return anArray; */
}
public static String[] getAttributeNames() {
String[] attributes = { "street", "apt", "city", "state", "zip" };
return attributes;
}
public static String[] getSourceDatatype() {
String[] types = { "java.lang.String", "java.lang.String",
"java.lang.String", "java.lang.String", "java.lang.Integer" };
return types;
}
public Object objectFrom(Object[] anArray) {
// Default shown is for a Name composer with title, first name, and last name.
/* String first, last, title;
title = (String) anArray[0];
first = (String) anArray[1];
last = (String) anArray[2];
return new Name(title, first, last); */
}
public static void reset() {
singleton = null;
}
public static AddressComposer singleton() {
if (singleton == null)
singleton = new AddressComposer();
return singleton;
}
}
|
If you look closely, you will see that the AddressComposer class is a singleton class, and it extends the class com.ibm.vap.composers.VapAttributeComposer as desired in Step 7 of the Using the wizard section. It also contains two methods -- dataFrom and objectFrom -- that have to be implemented manually.
public Object[] dataFrom(Object anObject)
The dataFrom method is responsible for object-to-object collection conversion, which is used while storing object-to-multiple datastore fields. This scenario required ejb.Address to be mapped to 5 columns of type String, String, String, String and Integer, so you implement the dataFrom() method, which takes Object as its parameter.
The first step is to typecast Object to a TargetClass like ejbs.Address:
- Create an Object array of the desired size (5 in this case)
- Now fill this object array with data from ejbs.Address Object
The dataFrom method will look like Listing 3.
Listing 3. The dataFrom method
public Object[] dataFrom(Object anObject) {
Address name = (Address) anObject;
Object[] anArray = new Object[5];
if (anObject == null) {
Object[] anArray2 = { null, null, null, null, null };
return anArray2;
} else {
anArray[0] = name.getStreet();
anArray[1] = name.getApt();
anArray[2] = name.getCity();
anArray[3] = name.getState();
anArray[4] = new Integer(name.getZip());
}
return anArray;
}
|
public Object objectFrom(Object[] anArray)
The objectFrom method is a mirror image of the dataFrom method, so it converts the data returned from the datastore to a target object. It takes the object array as an input and returns an object as a result. The implementation of this method in your scenario will look like Listing 4.
Listing 4. The dataFrom method
public Object objectFrom(Object[] anArray) {
Address address=new Address();
address.setStreet((String) anArray[0]);
address.setApt((String) anArray[1]);
address.setCity((String) anArray[2]);
address.setState((String) anArray[3]);
address.setZip(((Integer)) anArray[4]).intValue());
return address;
}
|
Once you've created the EJB composer, it immediately becomes available in the Mapping editor. To use the EJB composer for mapping a field that has a mapping to a single column, follow the steps below:
- Open Map.mapxmi in Mapping editor.
- Delete any existing mapping for the desired cmp field.
- Open the Table editor for the table mapped for your entity bean. This scenario used NewCustomer EJB, so open its table (NewCustomer table) in the Table editor.
- Delete the column which was un-mapped in Step 1. To do so, go to the Columns tab, right-click the column name and select Delete.
- Add the desired columns by right-clicking and selecting Add. This scenario added the following columns in the NewCustomer table.
- ADDRESS_STREET : VARCHAR
- ADDRESS_APT : VARCHAR
- ADDRESS_CITY : VARCHAR
- ADDRESS_STATE : VARCHAR
- ADDRESS_ZIP : INTEGER
- Save and close the Table Editor.
- In the Mapping editor, select all of the newly added columns in the Table pane (and the cmp field in the Enterprise Bean pane), right-click and select Create Mapping. This scenario selected address in the Enterprise Beans pane and ADDRESS_STREET, ADDRESS_APT, ADDRESS_CITY, ADDRESS_STATE, and ADDRESS_ZIP in the table pane. This will open the EJB Composed Mapping window shown in Figure 4.
Figure 4. The EJB Composed Mapping window
- In the EJB Composed Mapping window, select the required composer, which in this case is ejbs.AddressComposer.
- Map each attribute to a column by selecting the column corresponding to each attribute.
- Click Finish.
- Recreate the table and deploy the project by right-clicking EJBProject and selecting Deploy.
Now composer will automatically use the mapped table column with the appropriate composed attribute of the composed type.
- IRAD composer support requires that all fields mapped to database columns must have corresponding instance variables defined in the target class.
- The Java field names in the composed class must exactly match the attribute names in the getAttributes method of the composer class. If these field names do not match, the Tasks view displays a warning.
- After you create a composer, you can use it in other projects by copying the userDefinedComposer.xmi file, the composer class, and the composed type class to another EJB project.
- Nested composers are not supported by the Composer wizard. In other words, the wizard does not support composing fields that are already composed types.
- Composer maps do not support converters. If converters are required between two types, this must be handled in the dataFrom and ObjectFrom methods of the composer by creating an instance of the converter.
Rational Application Developer can be used to create and use the EJB composer, which maps a single complex EJB CMP field to multiple RDB columns. This concept can be quite useful for scenarios where you have EJB attributes that do not have a direct one-to-one database mapping.
Learn
- Mapping CMP entity bean fields to CLOB columns using WebSphere Studio (developerWorks, May 2004) provides a guide to using conversion in ejb-rdb mapping in WebSphere Studio Application developer.
-
Eclipse.org: For information about Eclipse development.
-
IBM Rational Application Developer product page: Find technical documentation, how-to articles, education, downloads, and product information about Rational Application Developer.
-
Excellent guided tours on using IRAD are in the book
An Introduction
to IBM Rational Application Developer.
Get products and technologies
-
IBM Rational Application Developer: Download a trial version from developerWorks.
Discuss
- Participate in the discussion forum.
-
Rational Software Architect, Software Modeler, Application Developer and Web Developer forum: Ask questions about Rational Application Developer.

Rakesh Midha is a staff software engineer with IBM Software Labs in Bangalore. He is currently working on Express Design and Architecture. He has 5+ years of technical experience in Java and C++ server-side programming on multiple platforms, and various relational database systems like DB2 UDB, Oracle, MySQL, and Microsoft SQLServer. He holds a Bachelor's degree in Electronics Engineering from the Punjab University in Chandigarh.
Comments (Undergoing maintenance)





