Skip Headers

Oracle® Application Server Containers for J2EE Enterprise JavaBeans Developer's Guide
10g Release 2 (10.1.2)
Part No. B15505-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

Using a Foreign Key in a Composite Primary Key

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

However, if you have a foreign key within a composite primary key, you have the following problem:

This section uses the following example to describe the way around this problem:

An order for a company 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 will have to modify the deployment descriptors and bean implementation to add a placeholder CMP field that mimics the actual foreign key field. This field is set during the ejbCreate method. However, both the placeholder CMP field and the foreign key point to the same database column. The actual foreign key is updated during the ejbPostCreate method.

The following example demonstrates how to modify both deployment descriptors and the bean implementation.


Note:

You modify the ejb-jar.xml file with the placeholder CMP field and the foreign key. We recommend that you deploy the application with autocreate-tables element in the orion-application.xml file set to false to auto-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 6-11 A Foreign Key That Exists in a Primary Key

Each order contains one or more items. Thus, two beans are created, where 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, do the following in the ejb-jar.xml file:

  1. Define a CMP 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 this example, an orderId CMP field is defined in a <cmp-field> element. The orderId and itemId CMP fields are used to identify the composite primary key in the OrderItemPK.java.

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

    In this example, 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-form-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>
	</ejb-relation>
</relationships>

The OrderItemPK.java class defines what is in 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 itemId, Integer orderId)
  {
    this.itemId = itemId;
    this.orderId = orderId;
  }
}

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 the auto-created database tables are sufficient for you, 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.

After you allow the orion-ejb-jar.xml file to auto-generate, copy it into your development directory. The database column names are defined in the persistence-name attributes in each of the CMP and CMR field name mappings. Ensure that the persistence-name attributes for both the placeholder CMP field and foreign key are the same.

The following is the orion-ejb-jar.xml file for the order/order item example. In the <entity-deployment> section for the OrderItemBean,

Both the foreign key, belongToOrder, and the placeholder CMP field, orderId, must point to the same database column.

<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" 						persistence-name="Order_ID"/>
				</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">						<fields>
							<cmp-field-mapping name="OrderItemBean_itemId"  								persistence-name="Item_ID"/>							<cmp-field-mapping name="OrderItemBean_orderId"  								persistence-name="Order_ID"/>
						</fields>
					</cmp-field-mapping>
				</entity-ref>
			</cmp-field-mapping>
		</value-mapping>
		</collection-mapping>
	</cmp-field-mapping>
</entity-deployment>

Finally, you must update the bean implementation to work with both the placeholder CMP field and the foreign key.

  1. In the ejbCreate method, do the following:

    1. Create the placeholder CMP field that takes the place of the foreign key field.

    2. Set a value in the placeholder CMP 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 CMP field.


    Note:

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

In our example, the CMP field, orderId, is set in the ejbCreate method and the relationship field, belongToOrder, is set in the ejbPostCreate method.

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
{
	// when just after bean 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 OrderItem object that is passed into the ejbCreate and ejbPostCreate methods is as follows:

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;
    }
}