Advanced CMP Topics: The OrderApp Example
The
OrderAppapplication is an advanced CMP example. It contains entity beans that have self-referential relationships, one-to-one relationships, unidirectional relationships, unknown primary keys, primitive primary key types, and composite primary keys.Structure of OrderApp
OrderAppis a simple inventory and ordering application for maintaining a catalog of parts and placing an itemized order of those parts. It has entity beans that represent parts, vendors, orders, and line items. These entity beans are accessed using a stateful session bean that holds the business logic of the application. A simple command-line client adds data to the entity beans, manipulates the data, and displays data from the catalog.The information contained in an order can be divided into different elements. What is the order number? What parts are included in the order? What parts make up that part? Who makes the part? What are the specifications for the part? Are there any schematics for the part?
OrderAppis a simplified version of an ordering system that has all these elements.This example assumes that you have successfully built, assembled, and deployed the
RosterAppexample application and that you are familiar with assembling entity beans indeploytool.
OrderAppconsists of three modules:DataRegistryJAR, an enterprise bean JAR file containing the entity beans, the support classes, and the database schema file;RequestJAR, an enterprise bean JAR containing a stateful session bean that accesses the data in the entity beans; andOrderAppClient, the application client that populates the entity beans with data and manipulates the data, displaying the results in a terminal.Figure 27-10 shows
OrderApp's database tables.
![]()
Figure 27-10 Database Tables in
OrderAppBean Relationships in OrderApp
The
RosterAppexample application shows how to set up one-to-many and many-to-many relationships between entity beans.OrderAppdemonstrates two additional types of entity bean relationships (see Figure 27-11): one-to-one and self-referential relationships.
![]()
Figure 27-11 Relationships between Entity Beans in
OrderAppSelf-Referential Relationships
A self-referential relationship is a relationship between container-managed relationship fields (CMR) in the same entity bean.
PartBeanhas a CMR fieldbomPartthat has a one-to-many relationship with the CMR fieldparts, which is also inPartBean. That is, a part can be made up of many parts, and each of those parts has exactly one bill-of-material part.The primary key for
PartBeanis a compound primary key, a combination of thepartNumberandrevisionfields. It is mapped to thePART_NUMBERandREVISIONcolumns in thePARTtable.One-to-One Relationships
PartBeanhas a CMR field,vendorPart, that has a one-to-one relationship withVendorPartBean's CMR fieldpart. That is, each part has exactly one vendor part, and vice versa.One-to-Many Relationship Mapped to Overlapping Primary and Foreign Keys
OrderBeanhas a CMR field,lineItems, that has a one-to-many relationship withLineItemBean's CMR fieldorder. That is, each order has one or more line item.
LineItemBeanuses a compound primary key that is made up of theorderIdanditemIdfields. This compound primary key maps to theORDER_IDandITEM_IDcolumns in theLINEITEMdatabase table.ORDER_IDis a foreign key to theORDER_IDcolumn in theORDERStable. This means that theORDER_IDcolumn is mapped twice: once as a primary key field,orderId; and again as a relationship field,order.Unidirectional Relationships
LineItemBeanhas a CMR field,vendorPart, that has a unidirectional many-to-one relationship withVendorPartBean. That is, there is no CMR field in the target entity bean in this relationship.Primary Keys in OrderApp's Entity Beans
The
OrderAppexample uses more complicated primary keys than doesRosterApp.Unknown Primary Keys
In
OrderApp,VendorPartBeanuses an unknown primary key. That is, the enterprise bean does not specify a primary key field, and usesjava.lang.Objectas the primary key class.The
LocalVendorPartHomeinterface'sfindByPrimaryKeymethod is defined as follows:When you package
OrderAppindeploytool, you will set the primary key class forVendorPartBeanto Unknown Primary Key.deploytoolwill create the proper entry in the enterprise bean deployment descriptor.See Generating Primary Key Values for more information on unkown primary keys.
Primitive Type Primary Keys
VendorBeanuses a primary key that is a Java programming language primitive type, anint. To use a primitive type as the primary key, you must create a wrapper class.VendorKeyis the wrapper class forVendorBean.The wrapper primary key class has the same requirements as described in The Primary Key Class. This is the
VendorKeywrapper class:package dataregistry; public final class VendorKey implements java.io.Serializable { public int vendorId; public boolean equals(Object otherOb) { if (this == otherOb) { return true; } if (!(otherOb instanceof VendorKey)) { return false; } VendorKey other = (VendorKey) otherOb; return (vendorId == other.vendorId); } public int hashCode() { return vendorId; } public String toString() { return "" + vendorId; } }Compound Primary Keys
A compound primary key is made up of multiple fields and follows the requirements described in The Primary Key Class. To use a compound primary key, you must create a wrapper class.
In
OrderApp, two entity beans use compound primary keys:PartBeanandLineItemBean.
PartBeanuses thePartKeywrapper class.PartBean's primary key is a combination of the part number and the revision number.PartKeyencapsulates this primary key.
LineItemBeanuses theLineItemKeyclass.LineItemBean's primary key is a combination of the order number and the item number.LineItemKeyencapsulates this primary key. This is theLineItemKeycompound primary key wrapper class:package dataregistry; public final class LineItemKey implements java.io.Serializable { public Integer orderId; public int itemId; public boolean equals(Object otherOb) { if (this == otherOb) { return true; } if (!(otherOb instanceof LineItemKey)) { return false; } LineItemKey other = (LineItemKey) otherOb; return ((orderId==null?other.orderId==null:orderId.equals (other.orderId)) && (itemId == other.itemId)); } public int hashCode() { return ((orderId==null?0:orderId.hashCode()) ^ ((int) itemId)); } public String toString() { return "" + orderId + "-" + itemId; } }Entity Bean Mapped to More Than One Database Table
PartBean's fields map to more than one database table:PARTandPART_DETAIL. ThePART_DETAILtable holds the specification and schematics for the part. When you set up the container-managed fields and relationships indeploytool, you will addPART_DETAILas a secondary table forPartBean.Finder and Selector Methods
VendorBeanhas two finder methods:findByPartialNameandfindByOrder. ThefindByPartialNamemethod searches through the vendor list for matches to a partial name.findByOrderfinds all vendors for a particular order.
LineItemBeanhas one finder method,findAll, which finds all line items.
OrderBeanhas one selector method,ejbSelectAll, which returns all orders.
VendorPartBeanhas two selector methods.ejbSelectAvgPricereturns the average price of all parts from a vendor.ejbSelectTotalPricePerVendorreturns the price of all the parts from a particular vendor.Selector methods cannot be accessed outside a bean instance because the selector methods are not defined in the bean interface. If you are using a selector method to return data to a caller, the selector method must be called from a home or business method. In
OrderApp, theLocalVendorPartHome.getAvgPricemethod returns the result of theejbSelectAvgPricemethod inVendorPartBean.The return type of a selector query is usually defined by the return type of the
ejbSelectmethods. You must specify the return type asRemoteif the method returns a remote interface or ajava.util.Collectionof remote interfaces. If the return type is a local interface or ajava.util.Collectionof local interfaces, set the return type toLocal. If the return type is neither a local nor a remote interface, nor a collection of local or remote interfaces, do not set the return type (indeploytool, set the return type toNone). TheOrderBean.ejbSelectAllmethod returns a collection of local interfaces.VendorPartBean.ejbSelectAvgPriceandVendorPartBean.ejbSelectTotalPricePerVendorreturn aDouble, so the return type is set toNone.Using Home Methods
Home methods are defined in the home interface of a bean and correspond to methods named
ejbHome<METHOD>in the bean class. For example, a methodgetValue, defined in theLocalExampleHomeinterface, corresponds to theejbHomeGetValuemethod implemented inExampleBean. TheejbHome<METHOD>methods are implemented by the bean developer.
OrderAppuses three home methods:LocalOrderHome.adjustDiscount,LocalVendorPartHome.getAvgPrice, andLocalVendorPartHome.getTotalPricePerVendor. Home methods operate on all instances of a bean rather than on any particular bean instance. That is, home methods cannot access the container-managed fields and relationships of a bean instance on which the method is called.For example,
LocalOrderHome.adjustDiscountis used to increase or decrease the discount on all orders.Cascade Deletes in OrderApp
Entity beans that use container-managed relationships often have dependencies on the existence of the other bean in the relationship. For example, a line item is part of an order, and if the order is deleted, then the line item should also be deleted. This is called a cascade delete relationship.
In
OrderApp, there are two cascade delete dependencies in the bean relationships. If theOrderBeanto which aLineItemBeanis related is deleted, then theLineItemBeanshould also be deleted. If theVendorBeanto which aVendorPartBeanis related is deleted, then theVendorPartBeanshould also be deleted.BLOB and CLOB Database Types in OrderApp
The
PART_DETAILtable in the database has a column,DRAWING, of typeBLOB.BLOBstands for binary large objects, which are used for storing binary data such as an image. TheDRAWINGcolumn is mapped to the container-managed fieldPartBean.drawingof typejava.io.Serializable.
PART_DETAILalso has a column,SPECIFICATION, of typeCLOB.CLOBstands for character large objects, which are used to store string data too large to be stored in aVARCHARcolumn.SPECIFICATIONis mapped to the container-managed fieldPartBean.specificationof typejava.lang.String.
Note: You cannot use a
BLOBorCLOBcolumn in theWHEREclause of a finder or selector EJB QL query.
Building and Running the OrderApp Example
This section assumes that you are familiar with how to package entity beans in
deploytoolas described in Building and Running the RosterApp Example and have created the JDBC resource.
Note: Application Server 8.2 includes a copy of the open source Derby database server. Application Server 8.0/8.1 includes the PointBase database server. If you are using Application Server 8.0/8.1, either follow the instructions in the J2EE Tutorial at
http://java.sun.com/j2ee/1.4/docs/tutorial-update6/doc/index.htmlthat works with Application Server 8.0/8.1 or upgrade to Application Server 8.2 (seehttp://java.sun.com/j2ee/1.4/download.html#appservto download).
Create the Database Tables
To create the database tables, do the following:
Capture the Database Schema
To capture the database schema, do the following:
Build the Application
To build the application components of
OrderApp, do the following:Package the Application
You will now package the enterprise beans, support classes, database schema, and client class in
deploytool. This section assumes that you are familiar with how to package these application modules indeploytool.Create the Application Modules
- Create a new application in
deploytoolnamedOrderAppin
<INSTALL>/j2eetutorial14/examples/ejb/cmporder/- Create an enterprise bean JAR named
RequestJARthat contains the files in
<INSTALL>/j2eetutorial14/examples/ejb/cmporder/build/
request/- Set up a stateful session bean,
RequestBean, inRequestJARwith a remote home interface ofrequest.RequestHomeand a remote interface ofrequest.Request.- Create an enterprise bean JAR named
DataRegistryJARthat contains the files in
<INSTALL>/j2eetutorial14/examples/ejb/cmporder/build/
dataregistryAnd the database schema file:
<INSTALL>/j2eetutorial14/examples/ejb/cmporder/build/
cmporder.dbschema- Set up the entity beans (
LineItemBean,OrderBean,PartBean,VendorBean, andVendorPartBean) according to Table 27-2 through Table 27-6.Configure the Entity Bean Relationships
Now we'll configure the relationships of the entity beans and map the fields and relationships to the database tables.
- Set up the bean relationships according to Table 27-7:
- Set the JNDI Name of the CMP Resource to
jdbc/ejbTutorialDB.- Create the database mappings using the
cmporder.dbschemafile in the Sun-specific Settings dialog box, CMP Database view.- Manually map
OrderBeanto theORDERSdatabase table in the Sun-specific Settings dialog box, CMP Database view:- Map
PartBeanto thePARTandPART_DETAILdatabase tables:
- Select
PartBeanin the Enterprise Bean field under Persistent Field Mappings.- Click Advanced Settings under Mappings for Bean
PartBean.- Click Add.
- In the Secondary Table field select
PART_DETAIL.- Select
PART_NUMBERin the Primary Table Column.- Select
PART_NUMBERin the Secondary Table Column.- Click Add Pair.
- Select
REVISIONin the Primary Table Column.- Select
REVISIONin the Secondary Table Column.- Click OK.
- Click OK.
- Set the Fetch group of the
drawingandspecificationfields toNone.- Click Automap All to automatically map the fields and relationships to the database tables. Repeat this step for all the entity beans until all the relationships and fields are mapped.
- Click Close.
Add the Finder and Selector Queries
Add the finder and selector queries to the entity beans as listed in Table 27-8 and Table 27-9:
Note: The queries are included in the
cmporderQueries.txtfile, located in<INSTALL>/j2eetutorial14/examples/ejb/cmporder/to make it easier to enter the queries.
Set the Transaction Attributes
The transactions for all our enterprise beans (
RequestBean,LineItemBean,OrderBean,PartBean,VendorBean, andVendorPartBean) must be managed by the container.Set RequestBean's Enterprise Bean References
RequestBeanaccesses the local entity beans contained inDataRegistryJAR. You must set the references to the entity beans inRequestBean.
- Select
RequestBeaninRequestJAR.- Click the EJB Ref's tab.
- Enter the references according to Table 27-10. All the references are to local entity beans.
Package the Application Client
Now we'll add the application client to the EAR.
Deploy the Enterprise Application
OrderAppis now ready to be deployed:Run the Client Application
The client application accesses the
RequestBeansession bean, which in turn manipulates data inOrderApp's entity beans.
Note: This example will perform poorly compared with a well-designed CMP application.
OrderAppis designed primarily for instructional purposes, and does not follow the best practices recommendations as outlined in the book Designing Enterprise Applications with the J2EEPlatform, Second Edition, Inderjeet Singh et al., (Addison-Wesley, 2002).
To run the client, follow these steps:
- In a terminal, go to
<INSTALL>/j2eetutorial14/examples/ejb/cmporder/- Enter the following command:
appclient -client OrderAppClient.jar- You will see the following output in the terminal:
Cost of Bill of Material for PN SDFG-ERTY-BN Rev: 7: $241.86
Cost of Order 1111: $664.68
Cost of Order 4312: $2,011.44
Adding 5% discount
Cost of Order 1111: $627.75
Cost of Order 4312: $1,910.87
Removing 7% discount
Cost of Order 1111: $679.45
Cost of Order 4312: $2,011.44
Average price of all parts: $117.55
Total price of parts for Vendor 100: $501.06
Ordered list of vendors for order 1111
200 Gadget, Inc. Mrs. Smith
100 WidgetCorp Mr. Jones
Counting all line items
Found 6 line items
Removing Order
Found 3 line items
Found 1 out of 2 vendors with 'I' in the name:
Gadget, Inc.
Note: Re-create the database tables using the
create-db_commontask before re-running the client.