Webservices  «Prev  Next»

EJB Metadata in WebSphere 5.0 | Part 2

Let's begin by looking at what changed in the EJB-JAR.xml file.
The part of the file below shows what changed:

<ejb-jar id="ejb-jar_ID"> 
  <enterprise-beans> 
     <entity id="ContainerManagedEntity_1"> 
        <ejb-name>PersonEJB</ejb-name> 
        <home>com.ibm.demo.ejbs.PersonHome</home> 
        <remote>com.ibm.demo.ejbs.Person</remote> 
        <ejb-class>com.ibm.demo.ejbs.PersonBean</ejb-class> 
        <persistence-type>Container</persistence-type> 
        <prim-key-class>java.lang.Integer</prim-key-class> 
        <reentrant>False</reentrant> 
        <cmp-field id="CMPAttribute_1"> 
           <field-name>id</field-name> 
        </cmp-field> 
        <cmp-field id="CMPAttribute_2"> 
           <field-name>name</field-name> 
        </cmp-field> 
        <cmp-field id="CMPAttribute_3"> 
           <field-name>age</field-name> 
        </cmp-field> 
        <cmp-field id="CMPAttribute_4"> 
           <field-name>educationLevel</field-name> 
        </cmp-field> 
        <primkey-field>id</primkey-field> 
     </entity> 
  </enterprise-beans> 
  ... 
</ejb-jar>

As you can see, a few things have been added.
AAT has added an id attribute to the following tags:
  1. Ejb-jar
  2. Entity
  3. Cmp-field

These id tags uniquely identify each CMP field within each Entity EJB contained in the JAR. As we will see in a moment, this unique identification is crucial for WebSphere to operate correctly on the other metadata files.
The next file to become familiar with is not really a metadata file, but a file that WebSphere generates for your convenience. This is the Table.ddl file, which contains the SQL to create the table for the top-down mapping:

CREATE TABLE PERSONEJB 
  (ID INTEGER NOT NULL, 
   NAME VARCHAR(250), 
   AGE INTEGER, 
   EDUCATIONLEVEL INTEGER); 
 
ALTER TABLE PERSONEJB 
  ADD CONSTRAINT PERSONEJBPK PRIMARY KEY (ID);

If you carefully compare this file to the EJB deployment descriptor above, you will see that the table that corresponds to this EJB has the same name specified in the <ejb-name> tag in the deployment descriptor, and that the columns of the table match the names in the <cmp-field> tags above.
Finally, the column corresponding to the value of the <primkey-field> tag has been declared NOT NULL (since it will be the key for this table), and a primary key constraint has been added for this column as well.
You may be wondering how WebSphere knows what datatypes to use to create this table.
There is a fixed mapping of datatypes in the database to the Java language types of the container-managed attributes defined in the code of your EJB Bean class. This mapping varies from database to database, which is why you must select the database type in either the AAT or the WebSphere Administration Console when you deploy the EJB to WebSphere.
Now that you have seen the Table.ddl file and understand how WebSphere derived it from the code of your CMP EJB and the metadata in the EJB deployment descriptor, the next file to investigate is the schema.dbxmi file held in the Schema subdirectory of the META-INF directory:

  <xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" 
  xmlns:RDBSchema="RDBSchema.xmi">   
  <RDBSchema:RDBDatabase xmi:id="RDBDatabase_1" name="TopDownDB"   
  tableGroup="RDBTable_1">     
  <dataTypeSet href="UDBV7_Primitives.xmi#SQLPrimitives_1"/>   
  </RDBSchema:RDBDatabase> 
  <RDBSchema:RDBTable xmi:id="RDBTable_1" name="PERSONEJB" 
  primaryKey="SQLReference_1" database="RDBDatabase_1"> 
    <columns xmi:id="RDBColumn_1" name="ID" allowNull="false" 
    group="SQLReference_1"> 
      <type xmi:type="RDBSchema:SQLExactNumeric" 
      xmi:id="SQLExactNumeric_1"> 
        <originatingType xmi:type="RDBSchema:SQLExactNumeric" 
        href="UDBV7_Primitives.xmi#SQLExactNumeric_1"/> 
      </type> 
    </columns> 
    <columns xmi:id="RDBColumn_2" name="NAME"> 
      <type xmi:type="RDBSchema:SQLCharacterStringType" 
      xmi:id="SQLCharacterStringType_1" length="250"> 
        <originatingType xmi:type="RDBSchema:SQLCharacterStringType" 
        href="JavatoDB2UDBNT_V71TypeMaps.xmi#SQLCharacterStringType_250"/> 
      </type> 
    </columns> 
    <columns xmi:id="RDBColumn_3" name="AGE"> 
      <type xmi:type="RDBSchema:SQLExactNumeric" 
      xmi:id="SQLExactNumeric_2"> 
        <originatingType xmi:type="RDBSchema:SQLExactNumeric" 
        href="UDBV7_Primitives.xmi#SQLExactNumeric_1"/> 
      </type> 
    </columns> 
    <columns xmi:id="RDBColumn_4" name="EDUCATIONLEVEL"> 
      <type xmi:type="RDBSchema:SQLExactNumeric" 
      xmi:id="SQLExactNumeric_3"> 
        <originatingType xmi:type="RDBSchema:SQLExactNumeric" 
        href="UDBV7_Primitives.xmi#SQLExactNumeric_1"/> 
      </type> 
    </columns> 
    <namedGroup xmi:type="RDBSchema:SQLReference" 
    xmi:id="SQLReference_1" 
    name="PERSONEJBPK" members="RDBColumn_1" table="RDBTable_1" 
    constraint="Constraint_PERSONEJBPK"/> 
    <constraints xmi:id="Constraint_PERSONEJBPK" name="PERSONEJBPK" 
    type="PRIMARYKEY" primaryKey="SQLReference_1"/> 
  </RDBSchema:RDBTable> 
</xmi:XMI>

This file is more complex than the other files we've seen, because it uses an XML standard called XMI, which represents information about an object design or object model in XML.
In fact, what it's describing is WebSphere's internal means of representing the database schema for this EJB.
It is not intended to be as easily readable as the EJB deployment descriptor.
However, it's not that hard to understand once you study it for a few minutes. Immediately after the opening XMI tag that describes the version and namespaces used by this file, you see the following tags:

<RDBSchema:RDBDatabase xmi:id="RDBDatabase_1" name="TopDownDB" tableGroup="RDBTable_1"> <dataTypeSet href="UDBV7_Primitives.xmi#SQLPrimitives_1"/>
</RDBSchema:RDBDatabase>

The only important thing about this group of tags is that it specifies that this particular schema uses the DB2® UDB 7 mapping to map Java types to database types.
The next segment gets more interesting. Notice that these tags have the following structure as shown in Figure 1 below.

Activity Diagram
Activity Diagram

As you can see, there is a <RDBSchema:RDBtable> tag that corresponds to the table defined in the CREATE TABLE SQL above. There are <columns> tags for each of the columns defined in the table as well. Finally, each <column> tag contains type information that describes both the originating type and the type of the column.
The originating type provides information on the primitive database type (numeric, etc.), while the type tag shows how the originating type is extended for this particular column (by providing length, scale, or precision information).

Here we have an XML definition of the table. At first glance, this doesn't seem useful, since it is very similar to the information in the Table.ddl file. However, the next file, the map.mapxmi file, brings everything together and helps all this make sense:

<ejbrdbmapping:EjbRdbDocumentRoot xmi:version="2.0" 
xmlns:xmi="http://www.omg.org/XMI" 
xmlns:ejbrdbmapping="ejbrdbmapping.xmi" xmlns:ejb="ejb.xmi" 
xmlns:RDBSchema="RDBSchema.xmi" xmlns:Mapping="Mapping.xmi" 
xmi:id="EjbRdbDocumentRoot_1" outputReadOnly="false" topToBottom="true"> 
  <helper xmi:type="ejbrdbmapping:RdbSchemaProperies" 
  xmi:id="RdbSchemaProperies_1" primitivesDocument="DB2UDBNT_V71"> 
    <vendorConfiguration 
    href="RdbVendorConfigurations.xmi#DB2UDBNT_V71_Config"/> 
  </helper> 
  <inputs xmi:type="ejb:EJBJar" href="META-INF/ejb-jar.xml#ejb-jar_ID"/> 
  <outputs xmi:type="RDBSchema:RDBDatabase" 
  href="META-INF/Schema/Schema.dbxmi#RDBDatabase_1"/> 
  <nested xmi:type="ejbrdbmapping:RDBEjbMapper" xmi:id="RDBEjbMapper_1"> 
    <helper xmi:type="ejbrdbmapping:PrimaryTableStrategy" 
    xmi:id="PrimaryTableStrategy_1"> 
      <table href="META-INF/Schema/Schema.dbxmi#RDBTable_1"/> 
    </helper> 
    <inputs xmi:type="ejb:ContainerManagedEntity" 
    href="META-INF/ejb-jar.xml#ContainerManagedEntity_1"/> 
    <outputs xmi:type="RDBSchema:RDBTable" 
    href="META-INF/Schema/Schema.dbxmi#RDBTable_1"/> 
    <nested xmi:id="PersonEJB_id---PERSONEJB_ID"> 
      <inputs xmi:type="ejb:CMPAttribute" 
      href="META-INF/ejb-jar.xml#CMPAttribute_1"/> 
      <outputs xmi:type="RDBSchema:RDBColumn" 
      href="META-INF/Schema/Schema.dbxmi#RDBColumn_1"/> 
      <typeMapping 
      href="JavatoDB2UDBNT_V71TypeMaps.xmi#Integer-INTEGER"/> 
    </nested> 
    <nested xmi:id="PersonEJB_name---PERSONEJB_NAME"> 
      <inputs xmi:type="ejb:CMPAttribute" 
      href="META-INF/ejb-jar.xml#CMPAttribute_2"/> 
      <outputs xmi:type="RDBSchema:RDBColumn" 
      href="META-INF/Schema/Schema.dbxmi#RDBColumn_2"/> 
      <typeMapping 
      href="JavatoDB2UDBNT_V71TypeMaps.xmi#String-VARCHAR"/> 
    </nested> 
    <nested xmi:id="PersonEJB_age---PERSONEJB_AGE"> 
      <inputs xmi:type="ejb:CMPAttribute" 
      href="META-INF/ejb-jar.xml#CMPAttribute_3"/> 
      <outputs xmi:type="RDBSchema:RDBColumn" 
      href="META-INF/Schema/Schema.dbxmi#RDBColumn_3"/> 
      <typeMapping 
      href="JavatoDB2UDBNT_V71TypeMaps.xmi#int-INTEGER"/> 
    </nested> 
    <nested xmi:id="PersonEJB_educationLevel---PERSONEJB_EDUCATIONLEVEL"> 
      <inputs xmi:type="ejb:CMPAttribute" 
      href="META-INF/ejb-jar.xml#CMPAttribute_4"/> 
      <outputs xmi:type="RDBSchema:RDBColumn" 
      href="META-INF/Schema/Schema.dbxmi#RDBColumn_4"/> 
      <typeMapping 
      href="JavatoDB2UDBNT_V71TypeMaps.xmi#int-INTEGER"/> 
    </nested> 
  </nested> 
  <typeMapping xmi:type="Mapping:MappingRoot" 
  href="JavatoDB2UDBNT_V71TypeMaps.xmi#Java_to_DB2UDBNT_V71_TypeMaps"/> 
</ejbrdbmapping:EjbRdbDocumentRoot>

<inputs xmi:type="ejb:ContainerManagedEntity" href="META-INF/ejb-jar.xml#ContainerManagedEntity_1"/>
<outputs xmi:type="RDBSchema:RDBTable" href="META-INF/Schema/Schema.dbxmi#RDBTable_1"/>

As you can see, these two lines link together a specific EJB reference in the ejb-jar.xml file (ContainerManagedEntity_1, which was the id of the "PersonEJB" we saw earlier), with a particular database table defined in the schema (RDBTable_1, which is the PERSONEJB table previously seen in the schema file). In fact, if this were a multiple-table mapping (one where some columns came from two or more tables), you'd see multiple <outputs> tags, each referring to a different schema file and table within that file.
This same principle continues throughout the rest of the file, as the next section indicates:

<nested xmi:id="PersonEJB_id---PERSONEJB_ID">
<inputs xmi:type="ejb:CMPAttribute" href="META-INF/ejb-jar.xml#CMPAttribute_1">
<outputs xmi:type="RDBSchema:RDBColumn" href="META-INF/Schema/Schema.dbxmi#RDBColumn_1">
<typeMapping href="JavatoDB2UDBNT_V71TypeMaps.xmi#Integer-INTEGER">

In this segment you see the connection between a particular container-managed field defined in the ejb-jar.xml file (CMPAttribute_1, which is the field id) and a particular database column defined in the schema (RDBColumn_1, which is the ID column). After the input and output mappings are defined, the final piece to this puzzle is the type mapping -- which (as you can see) maps a Java type (Integer) to a relational database type (INTEGER). This kind of mapping is repeated for all of the CMP fields in the EJB.
If you're familiar with Converters in VisualAge® for Java EJB Support, you'll be relieved to know that the <typeMapping> tag is used to pick the default converter. If you need a different conversion than what is specified (say a specialized converter that knows how to convert the special Strings "Yes" and "No" to a boolean), you can specify this through a <helper> tag at this point.

Figure 2 below shows the interaction between these three primary XML files and their constituent parts.

Figure 2. Metadata file relationships

xml-schema-instance
1) Ejb-jar.xml 2) map.mapxmi 3) Schema.dbxmi

Simple Metadata Tricks

Now that you know about the existence, structure, and interrelationships of these XML files, the question is, what do you do with them?
You should not try to create these files in order to perform your own bottom-up or complex meet-in-the-middle mapping.
The reason is that the underlying schemas aren't fully documented in the WebSphere documentation, because these files are intended to be generated and edited by the WebSphere toolset. WebSphere Studio Application Developer .
In fact, the Application Developer documentation contains the best description of the internal representation of the XMI object model that these files use.
If you are a tool builder who wants to generate your own entity EJBs using this information, consider using the documented Application Developer tool APIs to construct these files, rather than trying to reverse-engineer an object model from the XML.
On the other hand, there are a couple of instances where directly changing the XML can be the easiest way of updating your EJBs.
For example, many corporate environments have different database tables set up to support development, test, and production.
In some cases, these databases may be hosted on the same instance of DB2 or Oracle, and only differ by schema name (you might have DEV.PERSONEJB, TEST.PERSONEJB and PROD.PERSONEJB).
How would you write your code so that it doesn't have any dependencies on what environment? In the case of CMP Entity EJBs, WebSphere makes it simple.
All you need to do is change the name of the schema in the schema tag, and then deploy the EJB JAR file to the different WebSphere instances used for the three environments. For example, for DEV, your tag might look like this:

<RDBSchema:RDBDatabase xmi:id="RDBDatabase_1" name="DEV" tableGroup="RDBTable_1">

While for PROD your tag might look like this:
The great thing about this simple substitution is that you can automate it with tools like AWK, SED, or even ANT, which could also be used to invoke the appropriate WebSphere command-line tool (SEAppInstall on Advanced Single Server Edition, or WSCP on Advanced Edition) to generate the deployment code and install the resulting application.
In this case, you'd start with an undeployed EJB JAR file, deploy it once, and then copy the metadata files described above back into the build tree of your project so that they become part of the undeployed JAR file.
When you deploy the JAR, WebSphere picks up the metadata files and generates the deployment code appropriately.

Another simple change you can make is to update the XML to perform a minimal meet-in-the-middle mapping when either the EJB definition or the database schema changes. For instance, suppose you decide later in the project to change the name of the educationLevel CMP field to edLevel. You'd only need to update the ejb-jar.xml file to change the field like this:

<cmp-field id="CMPAttribute_4" >
<field-name>edLevel>/field-name > 
</cmp-field>

Keep the id the same, because (as we saw earlier) the id is actually used to map the CMP field to the corresponding column in the schema. As you can imagine, a corresponding change in the database would involve keeping the ejb-jar.xml the same, while updating the schema.dbxmi file appropriately. Again, in either case, redeploy the EJB jar file after editing the XML.

Summary

This article has examined some of the hidden parts of CMP EJB mapping to relational databases in WebSphere 4.0 and Application Developer. It described a little bit about how the ejb-jar, schema, and map files interoperate, and how the tools that operate on these files function. This information can help you make better use of the WebSphere tools for CMPs, and plan the best way to handle automated configuration and deployment issues involving CMPs.
AAT can generate these documents for a top-down mapping, but you cannot edit them or perform the other mapping types directly in AAT.