The order Application
The
order
application is a simple inventory and ordering application for maintaining a catalog of parts and placing an itemized order of those parts. It has entities that represent parts, vendors, orders, and line items. These entities are accessed using a stateful session bean that holds the business logic of the application. A simple command-line client adds data to the entities, 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?
order
is a simplified version of an ordering system that has all these elements.The
order
application consists of two modules:order-ejb
, an enterprise bean JAR file containing the entities, the support classes, and a stateful session bean that accesses the data in the entities; andorder-app-client
, the application client that populates the entities with data and manipulates the data, displaying the results in a terminal.Entity Relationships in the order Application
The
order
application demonstrates several types of entity relationships: one-to-many, many-to-one, one-to-one, unidirectional, and self-referential relationships.Self-Referential Relationships
A self-referential relationship is a relationship between relationship fields in the same entity.
Part
has a fieldbomPart
that has a one-to-many relationship with the fieldparts
, which is also inPart
. 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
Part
is a compound primary key, a combination of thepartNumber
andrevision
fields. It is mapped to thePARTNUMBER
andREVISION
columns in theEJB_ORDER_PART
table.... @ManyToOne @JoinColumns({ @JoinColumn(name="BOMPARTNUMBER", referencedColumnName="PARTNUMBER"), @JoinColumn(name="BOMREVISION", referencedColumnName="REVISION") }) public Part getBomPart() { return bomPart; } ... @OneToMany(mappedBy="bomPart") public Collection<Part> getParts() { return parts; } ...One-to-One Relationships
Part
has a field,vendorPart
, that has a one-to-one relationship withVendorPart
'spart
field. That is, each part has exactly one vendor part, and vice versa.Here is the relationship mapping in
Part
:Here is the relationship mapping in
VendorPart
:@OneToOne @JoinColumns({ @JoinColumn(name="PARTNUMBER", referencedColumnName="PARTNUMBER"), @JoinColumn(name="PARTREVISION", referencedColumnName="REVISION") }) public Part getPart() { return part; }Note that, because
Part
uses a compound primary key, the@JoinColumns
annotation is used to map the columns in theEJB_ORDER_VENDOR_PART
table to the columns inEJB_ORDER_PART
.EJB_ORDER_VENDOR_PART
'sPARTREVISION
column refers toEJB_ORDER_PART
'sREVISION
column.One-to-Many Relationship Mapped to Overlapping Primary and Foreign Keys
Order
has a field,lineItems
, that has a one-to-many relationship withLineItem
's fieldorder
. That is, each order has one or more line item.
LineItem
uses a compound primary key that is made up of theorderId
anditemId
fields. This compound primary key maps to theORDERID
andITEMID
columns in theEJB_ORDER_LINEITEM
database table.ORDERID
is a foreign key to theORDERID
column in theEJB_ORDER_ORDER
table. This means that theORDERID
column is mapped twice: once as a primary key field,orderId
; and again as a relationship field,order
.Here's the relationship mapping in
Order
:@OneToMany(cascade=ALL, mappedBy="order") public Collection<LineItem> getLineItems() { return lineItems; }Here is the relationship mapping in
LineItem
:Unidirectional Relationships
LineItem
has a field,vendorPart
, that has a unidirectional many-to-one relationship withVendorPart
. That is, there is no field in the target entity in this relationship.Primary Keys in the order Application
The
order
application uses several types of primary keys: single-valued primary keys, compound primary keys, and generated primary keys.Generated Primary Keys
VendorPart
uses a generated primary key value. That is, the application does not assign primary key values for the entities, but instead relies on the persistence provider to generate the primary key values. The@GeneratedValue
annotation is used to specify that an entity will use a generated primary key.In
VendorPart
, the following code specifies the settings for generating primary key values:@TableGenerator( name="vendorPartGen", table="EJB_ORDER_SEQUENCE_GENERATOR", pkColumnName="GEN_KEY", valueColumnName="GEN_VALUE", pkColumnValue="VENDOR_PART_ID", allocationSize=10) @Id @GeneratedValue(strategy=GenerationType.TABLE, generator="vendorPartGen") public Long getVendorPartNumber() { return vendorPartNumber; }The
@TableGenerator
annotation is used in conjunction with@GeneratedValue
'sstrategy=TABLE
element. That is, the strategy used to generate the primary keys is use a table in the database.@TableGenerator
is used to configure the settings for the generator table. The name element sets the name of the generator, which isvendorPartGen
inVendorPart
.The EJB_ORDER_SEQUENCE_GENERATOR table, which has two columns
GEN_KEY
andGEN_VALUE
, will store the generated primary key values. This table could be used to generate other entity's primary keys, so thepkColumnValue
element is set toVENDOR_PART_ID
to distinguish this entity's generated primary keys from other entity's generated primary keys. TheallocationSize
element specifies the amount to increment when allocating primary key values In this case, eachVendorPart
's primary key will increment by 10.The primary key field
vendorPartNumber
is of typeLong
, as the generated primary key's field must be an integral type.Compound Primary Keys
A compound primary key is made up of multiple fields and follows the requirements described in Primary Key Classes. To use a compound primary key, you must create a wrapper class.
In
order
, two entities use compound primary keys:Part
andLineItem
.
Part
uses thePartKey
wrapper class.Part
's primary key is a combination of the part number and the revision number.PartKey
encapsulates this primary key.
LineItem
uses theLineItemKey
class.LineItem
's primary key is a combination of the order number and the item number.LineItemKey
encapsulates this primary key. This is theLineItemKey
compound primary key wrapper class:package order.entity; public final class LineItemKey implements java.io.Serializable { private Integer orderId; private int itemId; public int hashCode() { return ((this.getOrderId()==null ?0:this.getOrderId().hashCode()) ^ ((int) this.getItemId())); } public boolean equals(Object otherOb) { if (this == otherOb) { return true; } if (!(otherOb instanceof LineItemKey)) { return false; } LineItemKey other = (LineItemKey) otherOb; return ((this.getOrderId()==null ?other.orderId==null:this.getOrderId().equals (other.orderId)) && (this.getItemId == other.itemId)); } public String toString() { return "" + orderId + "-" + itemId; } }The
@IdClass
annotation is used to specify the primary key class in the entity class. InLineItem
,@IdClass
is used as follows:The two fields in
LineItem
are tagged with the@Id
annotation to mark those fields as part of the compound primary key:@Id public int getItemId() { return itemId; } ... @Id @Column(name="ORDERID", nullable=false, insertable=false, updatable=false) public Integer getOrderId() { return orderId; }For
orderId
, we also use the@Column
annotation to specify the column name in the table, and that this column should not be inserted or updated, as it is an overlapping foreign key pointing at theEJB_ORDER_ORDER
table'sORDERID
column (see One-to-Many Relationship Mapped to Overlapping Primary and Foreign Keys). That is,orderId
will be set by theOrder
entity.In
LineItem
's constructor, the line item number (LineItem.itemId
) is set using theOrder.getNextId
method.public LineItem(Order order, int quantity, VendorPart vendorPart) { this.order = order; this.itemId = order.getNextId(); this.orderId = order.getOrderId(); this.quantity = quantity; this.vendorPart = vendorPart; }
Order.getNextId
counts the number of current line items, adds one, and returns that number.
Part
doesn't require the@Column
annotation on the two fields that comprisePart
's compound primary key. This is becausePart
's compound primary key is not an overlapping primary key/foreign key.@IdClass(order.entity.PartKey.class) @Entity ... public class Part { ... @Id public String getPartNumber() { return partNumber; } ... @Id public int getRevision() { return revision; } ... }Entity Mapped to More Than One Database Table
Part
's fields map to more than one database table:EJB_ORDER_PART
andEJB_ORDER_PART_DETAIL
. TheEJB_ORDER_PART_DETAIL
table holds the specification and schematics for the part. The@SecondaryTable
is used to specify the secondary table.... @Entity @Table(name="EJB_ORDER_PART") @SecondaryTable(name="EJB_ORDER_PART_DETAIL", pkJoinColumns={ @PrimaryKeyJoinColumn(name="PARTNUMBER", referencedColumnName="PARTNUMBER"), @PrimaryKeyJoinColumn(name="REVISION", referencedColumnName="REVISION") }) public class Part { ... }
EJB_ORDER_PART_DETAIL
shares the same primary key values asEJB_ORDER_PART
. ThepkJoinColumns
element of@SecondaryTable
is used to specify thatEJB_ORDER_PART_DETAIL
's primary key columns are foreign keys toEJB_ORDER_PART
. The@PrimaryKeyJoinColumn
sets the primary key column names and specifies which column in the primary table the column refers to. In this case, the primary key column names for bothEJB_ORDER_PART_DETAIL
andEJB_ORDER_PART
are the same:PARTNUMBER
andREVISION
, respectively.Cascade Operations in the order Application
Entities that have relationships to other entities often have dependencies on the existence of the other entity 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
order
, there are two cascade delete dependencies in the entity relationships. If theOrder
to which aLineItem
is related is deleted, then theLineItem
should also be deleted. If theVendor
to which aVendorPart
is related is deleted, then theVendorPart
should also be deleted.You specify the cascade operations for entity relationships by setting the
cascade
element in the inverse (non-owning) side of the relationship. The cascade element is set toALL
in the case ofOrder.lineItems
. This means that all persistence operations (deletes, updates, and so on) are cascaded from orders to line items.Here is the relationship mapping in
Order
:@OneToMany(cascade=ALL, mappedBy="order") public Collection<LineItem> getLineItems() { return lineItems; }Here is the relationship mapping in
LineItem
:BLOB and CLOB Database Types in the order Application
The
PARTDETAIL
table in the database has a column,DRAWING
, of typeBLOB
.BLOB
stands for binary large objects, which are used for storing binary data such as an image. TheDRAWING
column is mapped to the fieldPart
.drawing
of typejava.io.Serializable
. The@Lob
annotation is used to denote that the field is large object.
PARTDETAIL
also has a column,SPECIFICATION
, of typeCLOB
.CLOB
stands for character large objects, which are used to store string data too large to be stored in aVARCHAR
column.SPECIFICATION
is mapped to the fieldPart.specification
of typejava.lang.String
. The@Lob
annotation is also used here to denote that the field is a large object.@Column(table="EJB_ORDER_PART_DETAIL") @Lob public String getSpecification() { return specification; }Both of these fields use the
@Column
annotation and set thetable
element to the secondary table.Temporal Types in the order Application
The
Order.lastUpdate
persistent property, which is of typejava.util.Date
, is mapped to theEJB_ORDER_ORDER.LASTUPDATE
database field, which is of the SQL typeTIMESTAMP
. To ensure the proper mapping between these types, you must use the@Temporal
annotation with the proper temporal type specified in@Temporal
's element.@Temporal
's elements are of typejavax.persistence.TemporalType
. The possible values are:Here is the relevant section of
Order
:Managing the order Application's Entities
The
RequestBean
stateful session bean contains the business logic and manages the entities oforder
.
RequestBean
uses the@PersistenceContext
annotation to retrieve an entity manager instance which is used to manageorder
's entities inRequestBean
's business methods.This
EntityManager
instance is a container-managed entity manager, so the container takes care of all the transactions involved in the managingorder
's entities.Creating Entities
The
RequestBean.createPart
business method creates a newPart
entity. TheEntityManager.persist
method is used to persist the newly created entity to the database.Part part = new Part(partNumber, revision, description, revisionDate, specification, drawing); em.persist(part);Finding Entities
The
RequestBean.getOrderPrice
business method returns the price of a given order, based on theorderId
. TheEntityManager.find
method is used to retrieve the entity from the database.The first argument of
EntityManager.find
is the entity class, and the second is the primary key.Setting Entity Relationships
The
RequestBean.createVendorPart
business method creates aVendorPart
associated with a particularVendor
. TheEntityManager.persist
method is used to persist the newly createdVendorPart
entity to the database, and theVendorPart.setVendor
andVendor.setVendorPart
methods are used to associate theVendorPart
with theVendor
.PartKey pkey = new PartKey(); pkey.partNumber = partNumber; pkey.revision = revision; Part part = em.find(Part.class, pkey); VendorPart vendorPart = new VendorPart(description, price, part); em.persist(vendorPart); Vendor vendor = em.find(Vendor.class, vendorId); vendor.addVendorPart(vendorPart); vendorPart.setVendor(vendor);Using Queries
The
RequestBean.adjustOrderDiscount
business method updates the discount applied to all orders. It uses thefindAllOrders
named query, defined inOrder
:The
EntityManager.createNamedQuery
method is used to run the query. Because the query returns aList
of all the orders, theQuery.getResultList
method is used.The
RequestBean.getTotalPricePerVendor
business method returns the total price of all the parts for a particular vendor. It uses a named parameter,id
, defined in the named queryfindTotalVendorPartPricePerVendor
defined inVendorPart
.@NamedQuery( name="findTotalVendorPartPricePerVendor", query="SELECT SUM(vp.price) " + "FROM VendorPart vp " + "WHERE vp.vendor.vendorId = :id" )When running the query, the
Query.setParameter
method is used to set the named parameterid
to the value ofvendorId
, the parameter toRequestBean.getTotalPricePerVendor
.return (Double) em.createNamedQuery( "findTotalVendorPartPricePerVendor") .setParameter("id", vendorId) .getSingleResult();The
Query.getSingleResult
method is used for this query because the query returns a single value.Removing Entities
The
RequestBean.removeOrder
business method deletes a given order from the database. It uses theEntityManager.remove
method to delete the entity from the database.Building and Running the order Application
This section describes how to build, package, deploy, and run the
order
application. To do this, you will create the database tables in the Java DB server, then build, deploy, and run the example.Creating the Database Tables in NetBeans 5.5
To create the database tables in Java DB, the database server included with Application Server, you need to create the database connection and execute the SQL commands in
<
INSTALL
>/examples/common/sql/javadb/tutorial.sql
.Creating the Database Connection
To create the database connection do the following:
- Click the Runtime tab.
- Right-click the Databases node and select New Connection to open the New Connection dialog.
- Under Name, select Java DB (Network).
- Set Database URL to the following:
jdbc:derby://localhost:1527/sun-appserv-samples
- Set User Name to
APP
.- Set Password to
APP
.- Select the Remember Password during this Session box.
- Click OK.
Creating the Tables
To create the tutorial tables, do the following:
- Select File
Open File.
- Navigate to
<
INSTALL
>/examples/common/sql/javadb/
and opentutorial.sql
.- In the editor pane, select the connection URL to Java DB:
jdbc:derby://localhost:1527/sun-appserv-samples
- Click the Run SQL button at the top of the editor pane.
You will see the output from the SQL commands in the Output tab.
Deleting the Tables
To delete the tutorial tables, do the following:
- Select File
Open File.
- Navigate to
<
INSTALL
>/examples/common/sql/javadb/
and opendelete.sql
.- In the editor pane, select the connection URL to Java DB:
jdbc:derby://localhost:1527/sun-appserv-samples
- Click the Run SQL button at the top of the editor pane.
You will see the output from the SQL commands in the Output tab.
Creating the Database Tables Using Ant
The database tables are automatically created by the
create-tables
task, which is called before you deploy the application with theant deploy
task. To manually create the tables, do the following:
Note: The first time the
create-tables
task is run, you will see error messages when the task attempts to remove tables that don't exist. Ignore these error messages. Subsequent calls tocreate-tables
will run with no errors and will reset the database tables.
Building, Packaging, Deploying, and Running order In NetBeans 5.5
Follow these instructions to build, package, deploy, and run the
order
example to your Application Server instance using NetBeans 5.5.
- In NetBeans 5.5, select File
Open Project.
- In the Open Project dialog, navigate to
<
INSTALL
>/javaeetutorial5/examples/ejb/
.- Select the
order
folder.- Select the Open as Main Project and Open Required Projects checkboxes.
- Click Open Project Folder.
- In the Projects tab, right-click the
order
project and select Run Project.You will see the following output from the application client in the Output tab:
... 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 4312 Counting all line items Found 3 line items Found 1 out of 2 vendors with 'I' in the name: Gadget, Inc. run-order-app-client: run-ant: run: BUILD SUCCESSFUL (total time: 22 seconds)Building, Packaging, Deploying, and Running order Using Ant
To build the application components of
order
, enter the following command:This runs the
default
task, which compiles the source files and packages the application into an EAR file located at<
INSTALL
>/examples/ejb/order/dist/order.ear
.To deploy the EAR, make sure the Application Server is started, then enter the following command:
After
order.ear
is deployed, a client JAR,orderClient.jar
, is retrieved. This contains the application client.To run the application client, enter the following command:
You will see the following output:
... run: [echo] Running appclient for Order. appclient-command-common: [exec] Cost of Bill of Material for PN SDFG-ERTY-BN Rev: 7: $241.86 [exec] Cost of Order 1111: $664.68 [exec] Cost of Order 4312: $2,011.44 [exec] Adding 5% discount [exec] Cost of Order 1111: $627.75 [exec] Cost of Order 4312: $1,910.87 [exec] Removing 7% discount [exec] Cost of Order 1111: $679.45 [exec] Cost of Order 4312: $2,011.44 [exec] Average price of all parts: $117.55 [exec] Total price of parts for Vendor 100: $501.06 [exec] Ordered list of vendors for order 1111 [exec] 200 Gadget, Inc. Mrs. Smith [exec] 100 WidgetCorp Mr. Jones [exec] Counting all line items [exec] Found 6 line items [exec] Removing Order 4312 [exec] Counting all line items [exec] Found 3 line items [exec] Found 1 out of 2 vendors with 'I' in the name: [exec] Gadget, Inc. BUILD SUCCESSFUL
Note: Before re-running the application client, you must reset the database by running the
create-tables
task.
The all Task
As a convenience, the
all
task will build, package, deploy, and run the application. To do this, enter the following command:Undeploying order
To undeploy
order.ear
, enter the following command: