This BMP example illustrates what occurs when the methods of an entity EJB are invoked. The features that are common for all BMP entity beans are that they
Creating a Connection with a Data SourceWhen an entity EJB is created, it establishes a connection with a data source. The connection is used to query, store, and retrieve the persistent data that is being mapped by the entity EJB. As shown in Chapter 13, "Accessing Data Repositories Using JDBC," the connection may be established using the DriverManager directly. Chapter 16, "Managing Java Transactions Using JTA," introduced the DataSource and TxDataSource interfaces as features of the JDBC Optional Package. The DataSource or TxDataSource is located by performing a JNDI lookup on the configured name for the object. This isolation from the JDBC driver further increases portability and database independence. The applicaton obtains the database connection from the DataSource instance. In addition to these two methods of establishing a connection with a data source, Chapter 13 showed the configuration for the JDBC Data Source Factory. The DataSource Factory is an instance of a data source resource bound to the JNDI tree. The entity EJB performs a lookup on the JNDI tree to gain access to this resource. A resource manager is an object that controls access to an enterprise resource. The JDBC DataSource Factory is used to create a connection to a resource manager. The ejb-jar.xml deployment descriptor specifies the resource reference, as shown in Listing 21.12. Listing 21.12 The ejb-jar.xml Deployment Descriptor May Contain a Specification for a Resource Reference<resource-ref> <resource-ref-name>jdbc/ExampleDSRef</resource-ref-name> <res-type>javax.sql.Datasource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable<res-sharing-scope> <resource-ref> The weblogic-ejb-jar.xml deployment descriptor specifies the JNDI name bound to the resource connection factory. Listing 21.13 shows the reference descriptor snippet. Listing 21.13 The weblogic-ejb-jar.xml Deployment Descriptor Specifies the JNDI Name that Is Bound to the Resource Connection Factory<reference-descriptor> <resource-description> <res-ref-name>jdbc/ExampleDSRef</res-ref-name> <jndi-name>ExampleDS</jndi-name> <resource-description> </reference-descriptor> With the resource reference specified in the deployment descriptors, the EJB can look up the resource connection factory using JNDI. The code snippet used by the EJB to lookup the resource connection factory to obtain a DataSource is shown in Listing 21.14. Listing 21.14 The EJB Looks Up the Resource Connection Factory Using JNDIInitialContext ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/ExampleDSRef"); Connection con = ds.getConnection(); Creating the BookEntityBMPBeanThe behavior of an entity EJB is defined by the EntityBean interface. The implementation of the entity bean methods defines the object-relational mapping. The BookEntityBean first shown in Chapter 13 focused on the JDBC API. In Listing 21.15, the focus is turned toward the EntityBean interface as implemented using BMP. As mentioned in the section "Container-Managed Persistence Versus Bean-Managed Persistence," the BMP implementation gives the bean provider full control and flexibility over the connection with the database. Listing 21.15 An Entity EJB Using BMP Takes Full Control over the Interaction with the Database/** * BookEntityBMPBean example of a BMP Entity EJB */ package com.objectmind.BookStore; import java.util.Vector; import java.util.Collection; import javax.ejb.*; import javax.naming.InitialContext; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.DriverManager; import java.sql.Connection; import java.sql.PreparedStatement; /** * @ejbHome <{BookEntityHome}> * @ejbRemote <{BookEntity}> * @ejbPrimaryKey <{BookEntityPK}> * @ejbTransactionAttribute Required * @persistent*/ // must implement the EntityBean interface public class BookEntityBMPBean implements EntityBean { // the WebLogic Server will provide a context private EntityContext context; // object attributes public String bookID; // primary key private String title; private String author; private float price; public void setEntityContext(EntityContext context) throws javax.ejb.EJBException { // save the EntityContext this.context = context; } public void unsetEntityContext() throws javax.ejb.EJBException { // clear the EntityContext context = null; } /** ejbActivate is used to initialize any resources * required by the bean, files, sockets, etc... */ public void ejbActivate() throws javax.ejb.EJBException { } /** ejbPassivate is used to release any resources used by the bean */ public void ejbPassivate() throws javax.ejb.EJBException { } /** * ejbCreate() must be overloaded identical with the create() * methods of the home interface. The parameter list must match * the parameter list of the create() method in the home interface. */ public BookEntityPK ejbCreate(BookEntityVO value) throws javax.ejb.CreateException, javax.ejb.EJBException { bookID = value.getBookID(); title = value.getTitle(); author = value.getAuthor(); price = value.getPrice(); // insert a row into the database containing // this object's attributes try { Connection con = openConnection(); PreparedStatement ps = con.prepareStatement( "INSERT INTO books " + "( book_id, book_name, book_author, book_price ) " + "VALUES ( ?, ?, ? )" ); ps.setString( 1, bookID ); ps.setString( 2, title ); ps.setString( 3, author ); ps.setFloat ( 4, price ); if( ps.executeUpdate() != 1 ) { throw new CreateException( "Failed to create a row in the database" ); } ps.close(); // return the new primary key on success BookEntityPK pk = new BookEntityPK(); pk.bookID = bookID; return pk; } catch( Exception x ) { throw new CreateException( x.getMessage() ); } } /** * ejbPostCreate() must be overloaded identical with the * ejbCreate() methods. The ejbPostCreate() is automatically * called after ejbCreate() */ public void ejbPostCreate(BookEntityVO value) { // nothing more to do } /** * ejbRemove() deletes row with matching primary key */ public void ejbRemove() throws RemoveException { try { // always open a new connection Connection con = openConnection(); PreparedStatement ps = con.prepareStatement( "DELETE FROM books " + "WHERE book_id = ?" ); ps.setString( 1, bookID ); if( ps.executeUpdate() != 1 ) { throw new CreateException( "Failed to delete book with id " + bookID ); } ps.close(); } catch( Exception x ) { throw new RemoveException( x.getMessage() ); } } public void ejbStore() throws javax.ejb.EJBException { } public void ejbLoad() throws javax.ejb.EJBException { } // getters and setters for object attributes public String getBookID(){ return bookID; } public void setBookID(String param){ this.bookID = param; } public BookEntityPK ejbFindByPrimaryKey(BookEntityPK pk) throws javax.ejb.FinderException, javax.ejb.EJBException { // Write your code here // refresh this object's attributes by searching the database // for the primary key if( pk == null ) { throw new FinderException( "primary key cannot be null" ); } try { Connection con = openConnection(); PreparedStatement ps = con.prepareStatement( "SELECT author_id, book_name FROM books " + "WHERE book_id = ?" ); ps.setString( 1, pk.bookID ); ps.executeQuery(); ResultSet rs = ps.getResultSet(); if( rs.next() ) { bookID = pk.bookID; title = rs.getString( 2 ); } else { throw new FinderException( "Could not find " + pk.bookID ); } ps.close(); } catch( Exception x ) { throw new FinderException( x.getMessage() ); } return pk; } public Collection ejbFindBooksByAuthor(String author) throws javax.ejb.FinderException, javax.ejb.EJBException { PreparedStatement ps = null; try { Connection con = openConnection(); ps = con.prepareStatement( "SELECT book_id FROM books " + "WHERE author = ?" ); ps.setString( 1, author ); ResultSet rs = ps.executeQuery(); Vector books = new Vector(); while( rs.next() ) { BookEntityPK key = new BookEntityPK(); key.bookID = rs.getString( 1 ); } return books; } catch( Exception x ) { throw new FinderException( x.getMessage() ); } finally { closePS( ps ); } } public BookEntityVO getBookData(){ BookEntityVO value = new BookEntityVO(); value.setBookID( bookID ); value.setAuthor( author ); value.setTitle( title ); value.setPrice( price ); return value; } public void setBookData(BookEntityVO value) { bookID = value.getBookID(); author = value.getAuthor(); title = value.getTitle(); price = value.getPrice(); } public String getTitle(){ return title; } public String getAuthor(){ return author; } public double getPrice(){ return price; } public void setPrice( float value ) { price = value; //update database record PreparedStatement ps = null; try { Connection con = openConnection(); BookEntityPK key = (BookEntityPK)context.getPrimaryKey(); ps = con.prepareStatement( "UPDATE books " + "set book_price = ? " + "WHERE book_id = ?" ); ps.setFloat( 1, value ); ps.setString( 2, key.bookID ); int rows = ps.executeUpdate(); if( rows != 1 ) { throw new EJBException( "setPrice failed to update database" ); } } catch( Exception x ) { throw new EJBException( x.getMessage() ); } finally { closePS( ps ); } } // get Connection from DataSource public Connection openConnection() throws EJBException { try { InitialContext ic = new InitialContext(); DataSource ds = (DataSource)ic.lookup("MyDataSource"); return ds.getConnection(); } catch( Exception x ) { throw new EJBException( x.getMessage() ); } } private void closePS( PreparedStatement ps ) { try { if( ps != null ) ps.close(); } catch( SQLException x ) { } } } Creating the BMP Style Deployment DescriptorThe ejb-jar.xml deployment descriptor for the BookEntityBMPBean is shown in Listing 21.16. The JAR file contains a single deployment descriptor in the META-INF directory that defines the deployment characteristics for all the EJBS in the JAR. Listing 21.16 The Deployment Descriptor for a BMP Entity Bean Specifies the Names of the Interfaces for the EJB Class and Defines the Transaction Attributes<!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> <ejb-jar> <enterprise-beans> <entity> <ejb-name>BookEntityBMPBean</ejb-name> <home>BookEntityHome</home> <remote>BookEntity</remote> <local-home>BookEntityLocalHome</local-home> <local>BookEntityLocal</local> <ejb-class>BookEntityBMPBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>BookEntityPK</prim-key-class> <reentrant>False</reentrant> </entity> </enterprise-beans> <assembly-descriptor> <container-transaction> <method> <ejb-name>BookEntityBMPBean</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> |