The Container-Managed Relationship is used when a composition relationship exists between CMP entity beans. This example is a shopping cart that contains the books that have been selected for purchase. The ShoppingCart requires persistent storage to allow its life cycle to span system shutdowns and crashes. The ShoppingCart will be implemented as a CMP entity EJB. The books in the shopping cart establish a one-to-many relationship between the ShoppingCart and the BookEntityCMPBean . The local interfaces of the BookEntity will be used by the ShoppingCart for improved efficiency and higher performance. Creating the ShoppingCart Remote InterfaceThe remote interface shown in Listing 21.18 allows books to be added to the cart and all books to be retrieved from the cart as an array. This example uses the BookEntityVO as a convenient way to pass all attributes in a single value object parameter. The addBook() method is overloaded to pass an instance of a BookEntity as the parameter. Listing 21.18 Remote Interface for the ShoppingCart/** * Remote interface for the ShoppingCart */ package com.objectmind.ShoppingCart; import com.objectmind.BookStore.*; import java.rmi.RemoteException; import javax.ejb.EJBObject; import javax.ejb.CreateException; import javax.ejb.FinderException; import javax.naming.NamingException; public interface ShoppingCart extends EJBObject { public void addBook(BookEntityVO book) throws NamingException, CreateException, RemoteException, FinderException; public void addBook(BookEntity book) throws NamingException, CreateException, RemoteException; public BookEntityVO[] getAllBooks() throws RemoteException; } Creating the ShoppingCart Home InterfaceThe ShoppingCartHome , shown in Listing 21.19, creates a reference to a ShoppingCart for the given primary key. The finder methods find a ShoppingCart by matching a primary key and also find all instances of ShoppingCart . Listing 21.19 Sample Remote Home Interface for ShoppingCart/** * ShoppingCartHome is the home interface for the ShoppingCart. */ package com.objectmind.ShoppingCart; import java.rmi.RemoteException; import java.util.Collection; import javax.ejb.EJBHome; import javax.ejb.CreateException; import javax.ejb.FinderException; public interface ShoppingCartHome extends EJBHome { /** * Create method for ShoppingCart * @return BookEntity EJBObject */ public ShoppingCart create() throws CreateException, RemoteException; /** * findByPrimaryKey is required for all entity beans * @param primaryKey Integer * @return ShoppingCart object matching primary key */ public ShoppingCart findByPrimaryKey( Integer primaryKey ) throws FinderException, RemoteException; /** * Finder method to create a list of all shopping carts * @return collection of BookEntity objects in a Vector */ public Collection findAll() throws FinderException, RemoteException; } Creating the ShoppingCart CMP Entity BeanThe attributes of the ShoppingCart are its primary key and a collection of books. The primary key is the cartId of type java.lang.String while books are represented through a java.util.Collection . Listing 21.20 shows the CMP entity bean implementation of the shopping cart. Listing 21.20 The ShoppingCartCMPBean Is Implemented As an Abstract Class That Uses Container-Managed Persistence/** * ShoppingCartCMPBean example of a CMP Entity EJB */ package com.objectmind.ShoppingCart; import com.objectmind.BookStore.*; import java.util.Collection; import javax.ejb.EntityBean; import javax.ejb.EntityContext; import javax.ejb.EJBException; import javax.ejb.CreateException; import javax.ejb.FinderException; import javax.ejb.RemoveException; import java.rmi.RemoteException; import javax.naming.NamingException; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; import java.sql.SQLException; import java.sql.DriverManager; import java.sql.Connection; import java.sql.PreparedStatement; /** * @ejbHome <{ShoppingCartHome}> * @ejbRemote <{ShoppingCart}> * @ejbPrimaryKey <{ShoppingCartPK}> * @ejbTransactionAttribute Required * @persistent*/ // must implement the EntityBean interface public abstract class ShoppingCartCMPBean implements EntityBean { // the WebLogic Server will provide a context private EntityContext ctx; public void setEntityContext(EntityContext context) throws EJBException { // save the EntityContext ctx = context; } public void unsetEntityContext() throws EJBException { // clear the EntityContext ctx = null; } /** * abstract setters and getters for container-managed fields */ abstract public Integer getCartID(); abstract public void setCartID(Integer id); abstract public Integer getCustomerID(); abstract public void setCustomerID( Integer id ); abstract public Collection getBooks(); abstract public void setBooks(Collection books); /** * stubs for container-managed behavior */ public void ejbActivate() throws javax.ejb.EJBException{ } public void ejbPassivate() throws javax.ejb.EJBException { } public void ejbRemove() throws RemoveException { } public void ejbStore() throws javax.ejb.EJBException { } public void ejbLoad() throws javax.ejb.EJBException { } /** * initialize the bean from the value object */ public Integer ejbCreate() { return null; } public void ejbPostCreate() { } public void addBook(BookEntityVO bookData) throws NamingException, CreateException, FinderException { try { // use JNDI to lookup the Home interface for Book Context jndiContext = new InitialContext(); BookEntityHome bookHome = (BookEntityHome)jndiContext.lookup("cmp.BookEntity"); // make sure the book is in stock before adding // it to the shopping cart BookEntity book = bookHome.findBookByTitle( bookData.getTitle() ); // create a new Book entity book = bookHome.create(bookData); // add the Book reference to our book list Collection books = getBooks(); books.add(book); } catch( FinderException x ) { ctx.setRollbackOnly(); throw x; } catch( RemoteException x ) { } } public void addBook(BookEntity book) throws NamingException, CreateException { // add the Book reference to our book list Collection books = getBooks(); books.add(book); } public BookEntityVO[] getAllBooks() { Collection books = getBooks(); return (BookEntityVO[])books.toArray(); } } The ShoppingCart Primary KeyThe weblogic-cmp-rdbms-jar.xml deployment descriptor for the ShoppingCart specifies automatic key generation for the primary key. The primary key class for the ShoppingCart is therefore, java.lang.Integer . Refer to the ejb-jar.xml shown in Listing 21.22 to see the specification for the <prim-key-class> for the ShoppingCart . Also, refer to Listing 21.24 to see the specification for <automatic-key-generation> in the weblogic-cmp-rdbms-jar.xml descriptor. These topics are explained in the "Deployment on WebLogic Server" section later in this chapter. Testing the Book StoreThe client of an entity EJB is typically either a session EJB, a JSP or a servlet. The Customer class, shown in Listing 21.21, is provided here as an example to test the API for the BookEntity and ShoppingCart entity EJBs. The customer creates an instance of the home interface for the BookEntity . This BookEntityHome object is used to invoke the finder methods for books. A reference to a ShoppingCart is obtained by looking up the ShoppingCartHome and using it to create a ShoppingCart . The addBook() method of ShoppingCart is invoked when a book is purchased. Listing 21.21 The Customer Class Tests the API for the BookEntity and ShoppingCart Entity EJBs/** * BookStore customer will purchase books by * adding them to a shopping cart. */ package com.objectmind.BookStore; import java.util.Hashtable; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.ejb.CreateException; import javax.ejb.DuplicateKeyException; import java.rmi.RemoteException; import com.objectmind.ShoppingCart.*; public class Customer { // object attributes private Context context; private ShoppingCart cart; private BookEntityHome bookFinder; private BookEntityVO bookData; public Customer( String title, String author, float price, String id ) throws NamingException, CreateException, RemoteException { // create the JNDI context context = createJNDIContext(); // create the book entity home to // use its finder methods for books bookFinder = createBookFinder(); bookData = new BookEntityVO(); bookData.setTitle( title ); bookData.setAuthor( author ); bookData.setBookID( id ); bookData.setPrice( price ); // create a sample book try { bookFinder.create( bookData ); } catch( RemoteException x ) { // the DuplicateKeyException is nested as an RemoteException System.err.println( "Book already exists" ); } catch( Exception x ) { System.err.println( x.getMessage() ); } // create a shopping cart cart = createShoppingCart(); } private Context createJNDIContext() throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL,"t3://localhost:7001"); Context context = new InitialContext( env ); return context; } public BookEntityHome createBookFinder() throws NamingException, CreateException { // use JNDI to lookup the Home interface for Book BookEntityHome bookHome = (BookEntityHome)context.lookup("cmp.BookEntity"); return bookHome; } public ShoppingCart createShoppingCart() throws NamingException, CreateException, RemoteException { // use JNDI to lookup the Home interface for Book ShoppingCartHome cartHome = (ShoppingCartHome)context.lookup("cmp.ShoppingCart"); ShoppingCart cart = cartHome.create(); return cart; } public void purchase() throws Exception { cart.addBook( bookData ); // if no exceptions were thrown... System.out.println( "Thank you for purchasing " + bookData.getTitle() ); } /** * Main method to unit test the customer API */ public static void main(String[] args) { // check command line args if( args.length != 4 ) { System.err.println( "usage: Customer <title> <author> <price> <bookID>"); System.exit( -1 ); } try { String title = args[0]; String author = args[1]; float price = Float.valueOf( args[2] ).floatValue(); String bookID = args[3]; Customer customer = new Customer( title, author, price, bookID ); customer.purchase(); } catch( Exception x ) { System.err.println( "Failed to purchase \"" + args[0] + "\"" ); } } } Creating the Deployment DescriptorThe ShoppingCart uses CMP for the cartID field and CMR for the one-to-many relationship with books. All methods of ShoppingCart are specified to use the Required transaction type for CMT. These specifications are defined in the ejb-jar.xml deployment descriptor shown in Listing 21.22. Listing 21.22 The Deployment Descriptor Provides the Specification for the Persistence and Transactions Provided by the Container [View full width] <!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'> <!-- Generated XML! --> <ejb-jar> <enterprise-beans> <entity> <ejb-name>ShoppingCart</ejb-name> <home>com.objectmind.ShoppingCart.ShoppingCartHome</home> <remote>com.objectmind.ShoppingCart.ShoppingCart</remote> <ejb-class>com.objectmind.ShoppingCart.ShoppingCartCMPBean</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>ShoppingCartSchema</abstract-schema-name> <cmp-field> <field-name>cartID</field-name> </cmp-field> <cmp-field> <field-name>customerID</field-name> </cmp-field> <primkey-field>cartID</primkey-field> <query> <query-method> <method-name>findAll</method-name> <method-params> </method-params> </query-method> <ejb-ql><![CDATA[SELECT OBJECT(s) FROM ShoppingCartSchema AS s]]></ejb-ql> </query> </entity> <entity> <ejb-name>BookStore</ejb-name> <home>com.objectmind.BookStore.BookEntityHome</home> <remote>com.objectmind.BookStore.BookEntity</remote> <local-home>com.objectmind.BookStore.BookEntityLocalHome</local-home> <local>com.objectmind.BookStore.BookEntityLocal</local> <ejb-class>com.objectmind.BookStore.BookEntityCMPBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>com.objectmind.BookStore.BookEntityPK</prim-key-class> <reentrant>False</reentrant> <cmp-version>2.x</cmp-version> <abstract-schema-name>BookstoreSchema</abstract-schema-name> <cmp-field> <field-name>bookID</field-name> </cmp-field> <cmp-field> <field-name>title</field-name> </cmp-field> <cmp-field> <field-name>author</field-name> </cmp-field> <cmp-field> <field-name>price</field-name> </cmp-field> <primkey-field>bookID</primkey-field> <query> <query-method> <method-name>findBookByTitle</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </query-method> <ejb-ql> <![CDATA[SELECT OBJECT(a) FROM BookstoreSchema AS a WHERE a.title = ?1]]> </ejb-ql> </query> <query> <query-method> <method-name>findBooksByAuthor</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </query-method> <ejb-ql><![CDATA[SELECT OBJECT(a) FROM BookstoreSchema AS a WHERE a.author = ?1]]>< /ejb-ql> </query> </entity> </enterprise-beans> <relationships> <ejb-relation> <ejb-relation-name>ShoppingCart-Book</ejb-relation-name> <ejb-relationship-role> <ejb-relationship-role-name>ShoppingCart-Has-Books</ejb-relationship-role-name> <multiplicity>many</multiplicity> <relationship-role-source> <ejb-name>BookStore</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>cart</cmr-field-name> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <ejb-relationship-role-name>Book-Has-ShoppingCart</ejb-relationship-role-name> <multiplicity>one</multiplicity> <relationship-role-source> <ejb-name>ShoppingCart</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>books</cmr-field-name> <cmr-field-type>java.util.Collection</cmr-field-type> </cmr-field> </ejb-relationship-role> </ejb-relation> </relationships> <assembly-descriptor> <container-transaction> <method> <ejb-name>ShoppingCart</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>BookStore</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> |