Skip Headers

Oracle® Containers for J2EE Orion CMP Developer's Guide 10g Release 3 (10.1.3) B19177-01 |
| Home | Solution Area | Contents | Index

Previous | Next |

# Configuring Primary Key

Each entity bean instance has a primary key that uniquely identifies it from other instances. You must declare the primary key (or the fields contained within a complex primary key) as a container-managed persistent field in the deployment descriptor.

This section describes the following aspects of the primary key configurations:

- Configuring Primary Key Field

- Configuring Primary Key Class

- Configuring Automatic Primary Key Generation

## Configuring Primary Key Field

All fields within the primary key are restricted to either primitive, serializable, or types that can be mapped to SQL types. You can define your primary key in one of the following ways:

- Define the type of the primary key to be a well-known type. The type is defined in the `<prim-key-class>` element in the deployment descriptor. The data field that is identified as the persistent primary key is identified in the `<primkey-field>` element in the deployment descriptor. The primary key variable that is declared within the bean class must be declared as `public`.

  The advanced option of defining the primary key is to define its type as a serializable object within a serializable `<NAME>PK` class. This class is declared in the `<prim-key-class>` element in the deployment descriptor. See "Configuring Primary Key Class" for more information.

- Specify an automatically generated primary key: if you specify a `java.lang.Object` as the primary key class type in `<prim-key-class>`, but do not specify the primary key name in `<primkey-field>`, then the primary key is automatically generated by the container. See "Configuring Automatic Primary Key Generation" for more information.

Example 5-1 Defining a Primary Key of a Well-Known Type Within the Deployment Descriptor

For a simple CMP, you can define your primary key to be a well-known type by defining the data type of the primary key within the deployment descriptor. Example 5-1 shows how to define the primary key (employee number) as a `java.lang.Integer`:

    <enterprise-beans>
        <entity>
            <display-name>Employee</display-name>
            <ejb-name>EmployeeBean</ejb-name>
            <local-home>employee.EmployeeLocalHome</local-home>
            <local>employee.EmployeeLocal</local>
            <ejb-class>employee.EmployeeBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Integer</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-version>2.x</cmp-version>
            <abstract-schema-name>Employee</abstract-schema-name>
            <cmp-field><field-name>empNumber</field-name></cmp-field>
            <cmp-field><field-name>empName</field-name></cmp-field>
            <cmp-field><field-name>salary</field-name></cmp-field>
            <primkey-field>empNumber</primkey-field>
        </entity>
    ...
    </enterprise-beans>

Once defined, the container creates a column in the entity bean table for the primary key and maps the primary key defined in the deployment descriptor to this column.

Within the `orion-ejb-jar.xml` file, the primary key is mapped to the underlying database persistence storage by mapping the container-managed persistent field or primary key field defined in the `ejb-jar.xml` file to the database column name. In the following `orion-ejb-jar.xml` fragment, the `EmpBean` persistence storage is defined as the `EMP` table in the database that is defined in the `jdbc/OracleDS` data source. Following the `<entity-deployment>` element definition (see Table A-1, "Attributes of the <entity-deployment> Element"), the primary key, `empNumber`, is mapped to the `EMPNUMBER` column in the `EMP` table, and the `empName` and `salary` persistent fields are mapped to `EMPNAME` and `SALARY` columns respectively in the `EMP` table:

    <entity-deployment name="EmpBean" ...table="EMP" data-source="jdbc/OracleDS"...>
        <primkey-mapping>
            <cmp-field-mapping name="empNumber" persistence-name="EMPNUMBER" />
        </primkey-mapping>
        <cmp-field-mapping name="empName" persistence-name="EMPNAME" />
        <cmp-field-mapping name="salary" persistence-name="SALARY" />
    ...

## Configuring Primary Key Class

If your primary key is more complex than a simple data type, your primary key must be a class that is serializable of the name `<NAME>PK`. You define the primary key class within the `<prim-key-class>` element in the deployment descriptor.

The primary key variables must adhere to the following:

- Be defined within a `<cmp-field><field-name>` element in the deployment descriptor. This enables the container to manage the primary key fields.

- Be declared within the bean class as `public` and restricted to be either primitive, serializable, or types that can be mapped to SQL types.

- The names of the variables that make up the primary key must be the same in both the `<cmp-field><field-name>` elements and in the primary key class.

Within the primary key class, you implement a constructor for creating a primary key instance. Once the primary key class is defined in this manner, the container manages the class.

Example 5-2 Defining the Primary Key Class

Example 5-2 places the employee number within a primary key class.

    package employee;

    public class EmployeePK implements java.io.Serializable {

        public Integer empNumber;

        public EmployeePK() {
            this.empNumber = null;
        }

        public EmployeePK(Integer newEmpNumber) {
            this.empNumber = newEmpNumber;
        }

    }

Example 5-3 Declaring the Primary Key Class in the Deployment Descriptor

The primary key class is declared in the deployment descriptor within the `<prim-key-class>` element, and each of its variables are declared within a `<cmp-field><field-name>` element, as Example 5-3 shows:

    <enterprise-beans>
        <entity>
            <description>no description</description>
            <display-name>EmployeeBean</display-name>
            <ejb-name>EmployeeBean</ejb-name>
            <local-home>employee.EmployeeLocalHome</local-home>
            <local>employee.EmployeeLocal</local>
            <ejb-class>employee.EmployeeBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>employee.EmployeePK</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-version>2.x</cmp-version>
            <abstract-schema-name>Employee</abstract-schema-name>
            <cmp-field><field-name>empNumber</field-name></cmp-field>
            <cmp-field><field-name>empName</field-name></cmp-field>
            <cmp-field><field-name>salary</field-name></cmp-field>
        </entity>
    ...
    </enterprise-beans>

Once you define the primary key, the container creates a column in the entity bean table for the primary key and maps the primary key class declared in the deployment descriptor to this column.

The persistent fields are mapped in the `orion-ejb-jar.xml` in the same manner as ``described in the "Configuring Primary Key" section. With a complex primary key, the mapping contains more than a single field; thus, the `<cmp-field-mapping>` element of the `<primkey-mapping>` element contains another `<fields>` subelement. Every field of the primary key is defined in a separate `<cmp-field-mapping>` element within the `<fields>` element, similar to the following:

    <primkey-mapping>
        <cmp-field-mapping>
            <fields>
                <cmp-field-mapping name="empNumber" persistence-name="EMPNUMBER" />
            </fields>
        </cmp-field-mapping>
    </primkey-mapping>

Note: If you have a complex primary key that contains a foreign key, you need to apply a special mapping. See "Configuring Foreign Key in a Composite Primary Key" for more information.

### Configuring Foreign Key in a Composite Primary Key

In the EJB 2.0 specification, the primary key for an entity bean must be initialized within the `ejbCreate` method. However, in this method you cannot set any relationship that this bean has to another bean. The earliest when you can set this relationship in a foreign key is in the `ejbPostCreate` method.

That said, if you have a foreign key within a composite primary key, you face the following problem: you must set all fields within the composite primary key in the `ejbCreate` method, but you cannot set the foreign key in this method.

The following hypothetical scenario models the way around this problem: an order can contain one or more items; the order bean has many items in it; each item belongs to an order. The primary key for the item is a composite primary key consisting of the item identifier and the order identifier. The order identifier is a foreign key that points to the order.

You would have to modify the deployment descriptors and bean implementation to add a placeholder persistent field that mimics the actual foreign key field. This field is set during the `ejbCreate` method. However, both the placeholder persistent field and the foreign key point to the same database column. The actual foreign key is updated during the `ejbPostCreate` method.

Note: Modify the `ejb-jar.xml` file with the placeholder persistent field and the foreign key. Deploy the application with `<autocreate-tables>` element in the `orion-application.xml` file set to `false` to automatically generate the `orion-ejb-jar.xml` file, without creating any tables. Then modify the `orion-ejb-jar.xml` file to point to the correct database columns, set`<autocreate-tables>` element to `true`, and redeploy.

Example 5-4 Modifying the Deployment Descriptor and the Bean Code to Accommodate a Foreign Key Within a Primary Key

Example 5-4 demonstrates how to modify both deployment descriptors and the bean implementation.

In the order scenario, each order contains one or more items. The `OrderBean` represents the order, and the `OrderItemBean` represents the items in the order. Each item has a primary key that consists of the item number and the order number to which it belongs. Thus, the primary key for the item contains a foreign key that points to an order bean.

To adjust for a composite primary key, modify the `ejb-jar.xml` file in the following way:

1. Define a persistent field in the primary key as a placeholder for the foreign key. This placeholder should be used in the composite primary key class definition.

   In the Example 5-4, an `orderId` persistent field is defined in a `<cmp-field>` element. The `orderId` and `itemId` persistent fields are used to identify the composite primary key in the `OrderItemPK.java`.

2. Define the foreign key outside the primary key definition in its own `<cmr-field>` element in the `<relationships>` section.

   In the Example 5-4, the `belongToOrder` foreign key is defined in a `<cmr-field>` element for the `OrderItemBean`, defining the relationship from the item to the order.

    <entity>
        <ejb-name>OrderItemBean</ejb-name>
        <local-home>OrderItemLocalHome</local-home>
        <local>OrderItemLocal</local>
        <ejb-class>OrderItemBean</ejb-class>
        ...
        <cmp-field><field-name>itemId</field-name></cmp-field>
        <cmp-field><field-name>orderId</field-name></cmp-field>
        <cmp-field><field-name>price</field-name></cmp-field>
        <prim-key-class>OrderItemPK</prim-key-class>
        ...
    </entity>
    <relationships>
        <ejb-relation>
            <ejb-relation-name>Order-OrderItem</ejb-relation-name>
            <ejb-relationship-role>
                <ejb-relationship-role-name>
                    Order-Has-OrderItems
                </ejb-relationship-role-name>
                <multiplicity>One</multiplicity>
                <relationship-role-source>
                    <ejb-name>OrderBean</ejb-name>
                </relationship-role-source>
                <cmr-field>
                    <cmr-field-name>items</cmr-field-name>
                    <cmr-field-type>java.util.Collection</cmr-field-type>
                </cmr-field>
            </ejb-relationship-role>
            <ejb-relationship-role>
                <ejb-relationship-role-name>
                    OrderItems-from-Order
                </ejb-relationship-role-name>
                <multiplicity>Many</multiplicity>
                <cascade-delete/>
                <relationship-role-source>
                    <ejb-name>OrderItemBean</ejb-name>
                </relationship-role-source>
                <cmr-field>
                    <cmr-field-name>belongToOrder</cmr-field-name>
                </cmr-field>
            </ejb-relationship-role>
    ...

The `OrderItemPK.java` class defines the contents of the complex primary key, as follows:

    public class OrderItemPK implements java.io.Serializable {

        public Integer itemID;
        public Integer orderID;

        public OrderItemPK() {
            this.itemId = null;
            this.orderId = null;
        }

        public OrderItemPK(Integer newItemId, Integer newOrderId) {
            this.itemId = newItemId;
            this.orderId = newOrderId;
        }

        public boolean equals(Object o) {
            if (o instanceof OrderItemPK) {
                OrderItemPK pk = (OrderItemPK) o;
                if (pk.itemId.intValue() == itemId.intValue() &&
                    pk.orderId.intValue() == orderId.intValue()) {
                    return true;
                }
            }
            return false;
        }

        public int hashCode() {
            return itemId.hashCode() * orderId.hashCode();
        }
    }

If you consider the automatically created database tables sufficient, you do not need to modify the `orion-ejb-jar.xml` file. However, if you need to map to existing database tables, then you modify the `orion-ejb-jar.xml` file to point to these tables (see "Configuring Explicit Mapping of Relationship Fields to the Database" for more information).

After the automatic generation of the `orion-ejb-jar.xml` file, copy it into your development directory. The database column names are defined in the `persistence-name` attributes in each of the persistent and relationship field name mappings. Ensure that the `persistence-name` attributes for both the placeholder persistent field and foreign key are the same.

The following is the `orion-ejb-jar.xml` file for the Order/OrderItem example:

    <entity-deployment name="OrderItemBean"  table="ORDER_ITEM">
        <primkey-mapping>
            <cmp-field-mapping name="itemId" persistence-name="Item_ID" />
            <cmp-field-mapping name="orderId" persistence-name="Order_ID" />
        </primkey-mapping>
        <cmp-field-mapping name="price" persistence-name="Price" />
        <cmp-field-mapping name="belongToOrder">
            <entity-ref home="OrderBean">
                <cmp-field-mapping name="belongToOrder" persistence-name="Order_ID" />
            </entity-ref>
        </cmp-field-mapping>
    </entity-deployment>

    <entity-deployment name="OrderBean"  table="ORDER">
        <primkey-mapping>
            <cmp-field-mapping name="orderId" persistence-name="Order_ID" />
        </primkey-mapping>
        <cmp-field-mapping name="orderDesc" persistence-name="Order_Description" />
        <cmp-field-mapping name="items">
            <collection-mapping table="ORDER_ITEM">
                <primkey-mapping>
                    <cmp-field-mapping name="OrderBean_orderId">
                        <entity-ref home="OrderBean">
                            <cmp-field-mapping name="OrderBean_orderId">
                        </entity-ref>
                    </cmp-field-mapping>
                </primkey-mapping>
                <value-mapping type="OrderItemLocal">
                    <cmp-field-mapping name="OrderItemBean_itemId">
                        <entity-ref home="OrderItemBean">
                            <cmp-field-mapping name="OrderItemBean_itemId">
                                    <cmp-field-mapping name="OrderItemBean_itemId">
                                </fields>
                            </cmp-field-mapping>
                        </entity-ref>
                    </cmp-field-mapping>
                </value-mapping>
                </collection-mapping>
            </cmp-field-mapping>
    </entity-deployment>

The following takes place in the `<entity-deployment>` (see Table A-1, "Attributes of the <entity-deployment> Element") section for the `OrderItemBean` of the `orion-ejb-jar.xml` file for the Order/OrderItem example:

- The table is defined in the `table` attribute, which is `ORDER_ITEM` in this example.

- The column name for the `itemId` is defined in the `persistence-name` attribute as `Item_ID`.

- The column name for the placeholder persistent field, `orderId`, is defined in the `persistence-name` attribute as `Order_ID`.

- The foreign key, `belongToOrder`, is mapped to the database column, `Order_ID`, which is the same column as the placeholder persistent field, `orderId`.

Both the foreign key (`belongToOrder`), and the placeholder persistent field (`orderId`) must point to the same database column.

Finally, you must update the bean implementation to work with both the placeholder persistent field and the foreign key, as follows:

1. In the `ejbCreate` method, do the following:

   - Create the placeholder persistent field that takes the place of the foreign key field.

   - Set a value in the placeholder persistent field in the `ejbCreate` method. This value is written out to the foreign key field in the database table.

2. In the `ejbPostCreate` method, set the foreign key to the value in the duplicate persistent field.

   Note: Since the foreign key is a part of a primary key, you can only set it once.

In the Order/OrderItem example, the `orderId` persistent field is set in the `ejbCreate` method, whereas the `belongToOrder` relationship field is set in the `ejbPostCreate` method, as follows:

    public OrderItemPK ejbCreate(OrderItem orderItem) throws CreateException {
        setItemId(orderItem.getItemId());
        setOrderId(orderItem.getOrderId());
        setPrice(orderItem.getPrice());
        return new OrderItemPK(orderItem.getItemId(),orderItem.getOrderId());
    }

    public void ejbPostCreate(OrderItem orderItem) throws CreateException {
        // right after the bean has been created
        try {
            Context ctx = new InitialContext();
            OrderLocalHome orderHome =
                          (OrderLocalHome)ctx.lookup("java:comp/env/OrderBean");
            OrderLocal order = orderHome.findByPrimaryKey(orderItem.getOrderId());
            setBelongToOrder(order);
        }
        catch(Exception e) {
            e.printStackTrace();
            throw new EJBException(e);
        }
    }

The following is the code for the `OrderItem` object that is passed into the `ejbCreate` and `ejbPostCreate` methods:

    public class OrderItem implements java.io.Serializable {

        private Integer itemId;
        private Integer orderId;
        private Double price;

        public OrderItem(Integer itemId, Integer orderId, Double price) {
            this.itemId = itemId;
            this.orderId = orderId;
            this.price = price;
        }

        public Integer getItemId() {
            return itemId;
        }

        public void setItemId(Integer itemId) {
            this.itemId = itemId;
        }

        public Integer getOrderId() {
            return orderId;
        }

        public void setOrderId(Integer orderId) {
            this.orderId = orderId;
        }

        public Double getPrice() {
            return price;
        }

        public void setPrice(Double price) {
            this.price = price;
        }

        public boolean equals(Object other) {
            if (other instanceof OrderItem) {
                OrderItem orderItem = (OrderItem)other;
                if (itemId.equals(orderItem.getItemId()) &&
                    orderId.equals(orderItem.getOrderId()) &&
                    price.equals(orderItem.getPrice()) ) {
                    return true;
                }
            }
            return false;
        }
    }

## Configuring Automatic Primary Key Generation

If you specify a `java.lang.Object` as the primary key class type in `<prim-key-class>` element of your deployment descriptor, but do not specify the primary key name in `<primkey-field>` element, then the primary key is automatically generated by the container, as follows:

    <enterprise-beans>
        <entity>
            <display-name>Employee</display-name>
            <ejb-name>EmployeeBean</ejb-name>
            <local-home>employee.EmployeeLocalHome</local-home>
            <local>employee.EmployeeLocal</local>
            <ejb-class>employee.EmployeeBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Object</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-version>2.x</cmp-version>
            <abstract-schema-name>Employee</abstract-schema-name>
            <cmp-field><field-name>empNumber</field-name></cmp-field>
            <cmp-field><field-name>empName</field-name></cmp-field>
            <cmp-field><field-name>salary</field-name></cmp-field>
        </entity>
    ...
    </enterprise-beans>

Once defined, the container creates a column called `autoid` in the entity bean table for the primary key of type `LONG`. The container uses random numbers for the primary key values. This is generated in the `orion-ejb-jar.xml` for the bean, as follows:

    <primkey-mapping>
        <cmp-field-mapping name="auto_id" persistence-name="autoid"/>
    </primkey-mapping>