Cuando usamos la arquitectura Enterprise JavaBeans, los datos se leen y escriben en la base de datos sin tener que escribir ning�n c�digo SQL. Pero �qu� pasa si no quereos almacenar los datos en una base de datos, o si queremos escribir nuestros propios comandos SQL, o manejar transaciones?
Podemos sobreescribir el contenedor controlador interno de persistencia e implementar un Bean controlador de persistencia usando nuestro propio almacenamiento de datos y nuestro c�digo de manejo de transaciones.
La persistencia del Bean controlador se convierte en �til cuando queremos m�s control del que proporciona el contenedor controlador. Por ejemplo podr�amos sobreescribir la mayor�a de los contenedores para que mapeen un Bean en una fila de la tabla, implementar nuestros propios m�todos finder, o personalizar el cach�.
Este cap�tulo presenta dos versiones de la clase RegistrationBean del cap�tulo anterior. Una versi�n lee y escribe los datos del usuario en un fichero usando streams de entrada y salida serializados. La otra versi�n proporciona nuestros propios comandos SQL para leer y escribir en la base de datos. Tambi�n explica c�mo podemos escribir nuestro propio c�digo de manejo de transaciones.
�Bean-Controlador de Persistencia y la Plataforma JDBC
Puede que haya algunas veces que querramos sobreescribir la persistencia del contenedor controlador e implementar m�todos de Beans de entidad o de sesi�n para usar nuestros propios comandos SQL. Este tipo de persistencia controlada por el Bean puede ser �til si necesitamos aumentar el redimiento o mapear datos de m�ltiples Beans en una s�la fila de la tabla de la base de datos.
Esta secci�n nos muestra c�mo convertir la clase RegistrationBean.java para acceder a la base de datos con la clase PreparedStatement del JDBC.
�Conectar con la Base de Datos
Esta versi�n de la clase RegistrationBean.java establece la conexi�n con la base de datos ejemplarizando una clase est�tica Driver y proporcionando el m�todo getConnection.
El m�todo getConnection necesita la clase est�tica DriverManager para un motor de la base datos registrada que corresponda con la URL. En este caso, la URL es weblogic.jdbc.jts.Driver.
//Create static instance of database driver static { new weblogic.jdbc.jts.Driver(); } //Get registered driver from static instance public Connection getConnection() throws SQLException{ return DriverManager.getConnection( "jdbc:weblogic:jts:ejbPool"); }
�M�todo Create
El m�todo ejbCreate asigna valores a las variables miembro, obtiene una conexi�n con la base de datos, y crea un ejemplar de la clase java.sql.PreparedStatement para ejecutar la sentencia SQL que escribe los datos en la tabla registration de la base de datos.
Un objeto PreparedStatement se crea desde una sentenica SQL que se env�a a la base de datos y se precompila antes de enviar cualquier dato. Podemos llamar a las sentencias setXXX apropiadas sobre el objeto PreparedStatement para enviar datos. Manteniendo los objetos PreparedStatement y Connection como variables de ejemplar privadas reducimos la sobrecarga porque las sentencias SQL no tienen que compilarse cada vez que se env�an.
Lo �ltimo que hace el m�todo ejbCreate es crear una clase de clave primaria con el ID del usuario, y devolverlo al contenedor.
public RegistrationPK ejbCreate(String theuser, String password, String emailaddress, String creditcard) throws CreateException, RemoteException { this.theuser=theuser; this.password=password; this.emailaddress=emailaddress; this.creditcard=creditcard; this.balance=0; try { con=getConnection(); ps=con.prepareStatement("insert into registration ( theuser, password, emailaddress, creditcard, balance) values ( ?, ?, ?, ?, ?)"); ps.setString(1, theuser); ps.setString(2, password); ps.setString(3, emailaddress); ps.setString(4, creditcard); ps.setDouble(5, balance); if (ps.executeUpdate() != 1) { throw new CreateException ( "JDBC did not create a row"); } RegistrationPK primaryKey = new RegistrationPK(); primaryKey.theuser = theuser; return primaryKey; } catch (CreateException ce) { throw ce; } catch (SQLException sqe) { throw new CreateException (sqe.getMessage()); } finally { try { ps.close(); } catch (Exception ignore) {} try { con.close(); } catch (Exception ignore) {} } }
�M�todo Load
Este m�todo obtiene la clave primaria desde el contexto de entidad y lo pasa al m�todo refresh que carga los datos.
public void ejbLoad() throws RemoteException { try { refresh((RegistrationPK) ctx.getPrimaryKey()); } catch (FinderException fe) { throw new RemoteException (fe.getMessage()); } }
�M�todo Refresh
El m�todo refresh es el c�digo suministrado por el programador para cargar los datos desde la base de datos. Chequea la clave primaria, obtiene la conexi�n con la base de datos, y crea un objeto PreparedStatement para consultar en la base de datos la clave primaria especificada.
Los datos se leen desde la base de datos en un ResultSet y se asignan a las variables miembro globales para que RegistrationBean tenga la informaci�n m�s actualizada del usuario.
private void refresh(RegistrationPK pk) throws FinderException, RemoteException { if (pk == null) { throw new RemoteException ("primary key cannot be null"); } Connection con = null; PreparedStatement ps = null; try { con=getConnection(); ps=con.prepareStatement("select password, emailaddress, creditcard, balance from registration where theuser = ?"); ps.setString(1, pk.theuser); ps.executeQuery(); ResultSet rs = ps.getResultSet(); if (rs.next()) { theuser = pk.theuser; password = rs.getString(1); emailaddress = rs.getString(2); creditcard = rs.getString(3); balance = rs.getDouble(4); } else { throw new FinderException ( "Refresh: Registration (" + pk.theuser + ") not found"); } } catch (SQLException sqe) { throw new RemoteException (sqe.getMessage()); } finally { try { ps.close(); } catch (Exception ignore) {} try { con.close(); } catch (Exception ignore) {} } }
�M�todo Store
Este m�todo obtiene una conexi�n con la base de datos y crea un PreparedStatement para actualizarla.
public void ejbStore() throws RemoteException { Connection con = null; PreparedStatement ps = null; try { con = getConnection(); ps = con.prepareStatement("update registration set password = ?, emailaddress = ?, creditcard = ?, balance = ? where theuser = ?"); ps.setString(1, password); ps.setString(2, emailaddress); ps.setString(3, creditcard); ps.setDouble(4, balance); ps.setString(5, theuser); int i = ps.executeUpdate(); if (i == 0) { throw new RemoteException ( "ejbStore: Registration ( " + theuser + ") not updated"); } } catch (RemoteException re) { throw re; } catch (SQLException sqe) { throw new RemoteException (sqe.getMessage()); } finally { try { ps.close(); } catch (Exception ignore) {} try { con.close(); } catch (Exception ignore) {} } }
�M�todo Find
El m�todo ejbFindByPrimaryKey corresponde con la firma del m�todo FindByPrimaryKey del interface RegistrationHome. Este llama al m�todo refresh para obtener o refrescar los datos de usuario para el usuario espec�ficado en la clave primaria.
La versi�n de persistencia del contenedor controlador de RegistrationBean no implementa este m�todo porque el contenedor maneja la obtenci�n y refresco de los datos de usuario.
public RegistrationPK ejbFindByPrimaryKey( RegistrationPK pk) throws FinderException, RemoteException { if ((pk == null) || (pk.theuser == null)) { throw new FinderException ("primary key cannot be null"); } refresh(pk); return pk; }
�Control de Transaciones
�No ser�a maravilloso si cada operaci�n que intentara nuestra aplicaci�n tuviera �xito? Desafortunadamente, en el mundo multi-thread de las aplicaciones distribuidas y recursos compartidos, esto no es siempre posible.
�Por qu�? Primero de todo, los recursos compartidos deben mantener una vista consistente de los datos de todos los usuarios. Esto significa que leer y escribir tiene que ser controlado para que los usuarios no se sobreescriban los datos unos a los otros, o los errores de transaci�n no corrompan la integridad de los datos. Tambi�n, si trabajamos en una red con retardos intermitenes o ca�das de conexiones, el potencial para que las operaciones fallen en una aplicaci�n basada en web se incrementa con el n�mero de usuarios.
Los fallos de operaciones son inevitables, lo mejor es recuperar luego la seguridad, y aqu� es donde entra el control de transaciones. Las bases de datos modernas y los controladores de transaciones nos permiten deshacer y repetir el estado de una secuencia de operaciones fallidas para asegurar que los datos son consistentes para el acceso desde m�ltiples threads.
Esta secci�n a�ade c�digo al SellerBean del ejemplo de la casa de subastas para que pueda manejar la inserci�n de it�ms en la subasta m�s all� del controlador de transaciones por defecto proporcionado por su contenedor.
��Por qu� Controlar las Transaciones?
Cuando accedemos a una base de datos usando el API JDBC, todas las aplicaciones se ejecutan con una entrega autom�tica expl�cita por defecto. Esto significa que cualquier aplicaci�n que est� viendo los datos ver� los datos actualizados despu�s de cada llamada a JDBC.
Para aplicaciones sencillas, esto podr�a ser aceptable, pero consideremos la aplicaci�n de la casa de subastas y las secuencias que ocurren cuando SellerBean inserta un �tem de subasta. Primero se carga la cuenta del usuario para listar el �tem, y se a�ade el �tem a la lista de �tems de la subasta. Estas operaciones ivolucran a RegistrationBean para cobrar la cuenta y AuctionItemBean para a�adir el �tem a la lista de subasta.
En el modo de entrega autom�tico, si falla la inserci�n del �tem de subasta, s�lo se se puede deshacer el listado, y tenemos que ajustar manualmente la cuenta del usuario para descontarle la lista de cargos. Mientras tanto, otro thread podr�a estar intentando deducir de la misma cuenta de usuario, sin encontrar cr�dito, y abortando cuando quiz�s unos milisegundos despu�s se podr�a haber completado.
Hay dos formas para segurarnos de que el d�bito se ha devuelto a su valor cuando falla la inserci�n de un �tem en la subasta:
- A�adir c�digo de sincronizaci�n de sesi�n al Bean de sesi�n del contenedor controlador para obtener el control sobre las entregas de la transaci�n y volver atr�s.
- Configurar JDBC para los servicios de modo de entrega transaci�n y a�adir c�digo para arrancar, parar, entregar y deshacer la transaci�n. Esto es una transaci�n controlada por Bean y puede ser usada por Beans de entidad y de sesi�n.
�Sincronizaci�n de Sesis�n
Un Bean de sesi�n controlado por contenedor puede opcionalmente incluir sincronizaci�n de sesi�n para controlar la entrega autom�tica por defecto propocionada por el contenedor. El c�digo de sincronizaci�n permite al contenedor notificar al Bean cuando se alcanzan los puntos importantes en la transaci�n. Adem�s de recibir la notificaci�n, el Bean puede tomar cualquier acci�n necesaria antes de que la transaci�n proceda con el siguiente punto.
Nota: Un Bean de Sesi�n que usa transaciones controladas por Bean no necesita sincronizaci�n de sesi�n porque tiene la entrega totalmente controlada.
�Ejemplo de Control por Contenedor
SellerBean es un Bean de sesi�n que usa RegistrationBean para comprobar la ID y la password del usuario cuando alguien postea un �tem para la subasta y apunta en la cuenta del vendedor un listado, y AuctionItemBean a�ade los nuevos �tems a la base de datos.
La transaci�n empieza en el m�todo insertItem con el apunte del d�bito y termina cuando se entrega la transaci�n completa o se deshace. La transaci�n completa incluye deshacer el apunte de 50 centavos si el �tem de subasta es null (la inserci�n falla), o si se captura una excepci�n. Si el �tem de subasta no es null y la inserci�n se realiza con �xito, se entrega la transaci�n completa, incluyendo el cobro de los 50 centavos.
�C�digo
Para usar sincronizaci�n de sesi�n, un Bean de sesi�n implementa el interface SessionSynchronzation y sus tres m�todos, afterBegin, beforeCompletion, y afterCompletion. Este ejemplo adapta el c�digo de SellerBean.java para usar sincronizaci�n de sesi�n.
public class SellerBean implements SessionBean, SessionSynchronization { private transient SessionContext ctx; private transient Properties p = new Properties(); private transient boolean success = true; public void afterBegin() {} public void beforeCompletion() { if (!success ) { ctx.setRollbackOnly(); } } public void afterCompletion(boolean state) {}
afterBegin: El contenedor llama a este m�todo antes del d�bito para notificar al Bean de sesi�n de que una nueva transaci�n va a comenzar. Podemos implementar este m�todo que haga cualquier trabajo pr�vio en la base de datos que pudiera ser necesario para la transaci�n. En este caso no son necesarios trabajos pr�vios, por eso este m�todo no est� implementado.
beforeCompletion: El contenedor llama a este m�todo cuando est� listo para escribir el �tem de subasta y el d�bito en la base de datos, pero antes de hacerlo realmente (entregarlo). Podemos implementar este m�todo para escribir cualquier actualizaci�n cach� de la base de datos o deshacer la transaci�n. En este ejemplo, el m�todo llama al m�todo setRollbackOnly sobre el contexto de la sesi�n en el caso de que la variable success sea false durante la transaci�n.
afterCompletion: El contenedor llama a este m�todo cuando la transaci�n se entrega. Un valor booleano de true significa que el dato ha sido enviado y false significa que la transaci�n se ha deshecho. El m�todo usa el valor boolean para determinar si necesita resetear el estado del Bean en el caso de que se haya deshecho. En este ejemplo, no es necesario resetear el estado en el caso de un fallo.
Aqu� est� el m�todo insertItem con comentarios que muestran d�nde est�n los puntos donde se llama a los m�todos de SessionSynchronization.
public int insertItem(String seller, String password, String description, int auctiondays, double startprice, String summary) throws RemoteException { try{ Context jndiCtx = new InitialContext(p); RegistrationHome rhome = (RegistrationHome) sCtx.lookup("registration"); RegistrationPK rpk=new RegistrationPK(); rpk.theuser=seller; Registration newseller=rhome.findByPrimaryKey(rpk); if((newseller == null) || (!newseller.verifyPassword(password))) { return(Auction.INVALjdcbook_USER); } //Call to afterBegin newseller.adjustAccount(-0.50); AuctionItemHome home = (AuctionItemHome) jndiCtx.lookup("auctionitems"); AuctionItem ai= home.create(seller, description, auctiondays, startprice, summary); if(ai == null) { success=false; return Auction.INVALjdcbook_ITEM; } else { return(ai.getId()); } }catch(Exception e){ System.out.println("insert problem="+e); success=false; return Auction.INVALjdcbook_ITEM; } //Call to beforeCompletion //Call to afterCompletion }
�Modo de Entrega de la Transaci�n
Si configuramos los servicos JDBC para modo de entrega de transaci�n, podemos hacer que el Bean controle la transaci�n. Para configurar los servicios de JDBC para la entrega, llamamos a con.setAutoCommit(false) sobre nuestra conexi�n JDBC. No todos los drivers JDBC soportan el modo de entrega, pero para hacer que el Bean controle y maneje las transaciones, necesitamos un driver que lo haga.
El modo de entrega de la transaci�n nos permite a�adir c�digo que crea una red de seguridad alrededor de una secuencia de operaciones dependientes. El API de Transaction de Java, proporciona las ayudas que necesitamos para crear esa red de seguridad. Pero si estamos usando la arquitectura JavaBeans de Enterprise, podemos hacerlo con mucho menos c�digo. S�lo tenemos que configurar el servidor de JavaBeans de Entrprise, y especificar en nuestro c�digo donde empieza la transaci�n, donde para, donde se deshace y se entrega.
�Configuraci�n del servidor
Configurar el servidor de JavaBeans Enterprise implica especificar las siguientes selecciones en un fichero de configuraci�n para cada Bean:
- Un nivel de aislamiento para especificar c�mo de exclusivo es el acceso de una transasci�n a los datos compartidos.
- Un atributo de transaci�n para especificar c�mo controlar las transaciones mediante el Bean o el contenedor que contin�a en otro Bean.
- Un tipo de transaci�n para especificar si la transaci�n es manejada por el contenedor o por el Bean.
Por ejemplo, podr�amos especificar estas selecciones para el servidor BEA Weblogic en un fichero DeploymentDescriptor.txt para cada Bean.
Aqu� est� la parte de DeploymentDescriptor.txt para SellerBean que especifica el nivel de aislamiento y el atributo de transaci�n.
(controlDescriptors (DEFAULT isolationLevel TRANSACTION_SERIALIZABLE transactionAttribute REQUIRED runAsMode CLIENT_IDENTITY runAsIdentity guest ); end DEFAULT ); end controlDescriptors
Aqu� est� el equivalente en lenguaje XML.
<container-transaction> <method> <ejb-name>SellerBean<ejb-name> <method-name>*<method-name> <method> <transaction-type>Container<transaction-type> <trans-attribute>Required<trans-attribute> <container-transaction>
En este ejemplo, SellerBean est� controlado por el Bean.
<container-transaction> <method> <ejb-name>SellerBean<ejb-name> <method-name>*<method-name> <method> <transaction-type>Bean<transaction-type> <trans-attribute>Required<trans-attribute> <container-transaction>
Descripci�n de Atributo de Transaci�n: Un Bean Enterprise usa un transaction attribute para especificar si una transaci�n de Bean es manejada por el propio Bean o por el contenedor, y c�mo manejar las transaciones que empezaron en otro Bean.
El servidor de JavaBeans de Enterprise s�lo puede controlar una transaci�n a la vez. Este modelo sigue el ejemplo configurado por el Object Transaction Service (OTS) de la OMG, y significa que la especificaci�n actual de los JavaBeans Enterpirse no proporcionan una forma para transaciones anidadas. Una transaci�n anidada es un nueva transaci�n que arranca dentro de otra transaci�n existente. Mientras que las transaciones anidadas no est�n permitidas, continuar una transaci�n existente en otro Bean es correcto.
Cuando se entra en un Bean, el servidor crea un contexto de transaci�n para controlar la transaci�n. Cuando la transaci�n es manejada por le Bean, accedemos para comenzar, entregar o deshacer la transaci�n cuando sea necesario.
Aqu� est�n los atributos de transaci�n con una breve descripci�n de cada uno de ellos. Los nombres de los atributos cambiaron entre las especificaciones 1.0 y 1.1 de los JavaBeans Enterprise.
Especificaci�n 1.1 | Especificaci�n 1.0 | Explicaci�n |
---|---|---|
REQUIRED | TX_REQUIRED | Transaci�n controlada por el contenedor. El servidor arranca y maneja una nueva transaci�n a petici�n del usuario o contin�a usando la transaci�n que se arranc� en el c�digo que llam� a este Bean. |
REQUIRESNEW | TX_REQUIRED_NEW | Transaci�n controlada por contenedor. El servidor arranca y maneja una nueva transaci�n. Si una transaci�n existente arranca esta transaci�n, la suspende hasta que la transaci�n se complete. |
Especificado como tipo de transaci�n de Bean en el Descriptor de desarrollo | TX_BEAN_MANAGED | Transaci�n controlada por el Bean. Tenemos acceso al contexto de la transaci�n para empezar, entregar o deshacer la transaci�n cuando sea necesario. |
SUPPORTS | TX_SUPPORTS | Si el c�digo que llama a este Bean tiene un transaci�n en ejecuci�n, incluye este Bean en esa transaci�n. |
NEVER | TX_NOT_SUPPORTED | Si el c�digo que llama a un m�todo en este Bean tiene una transaci�n ejecunt�ndose, suspende esa transaci�n hasta que la llamada al m�todo de este Bean se complete. No se crea contexto de transaci�n para este Bean. |
MANDATORY | TX_MANDATORY | El atributo de transaci�n para este Bean se configura cuando otro bean llama a uno de sus m�todos. En este caso, este bean obtiene el atributo de transaci�n del Bean llamante. Si el Bean llamante no tiene atributo de transaci�n, el m�todo llamado en este Bean lanza una excepcio�n TransactionRequired. |
Descripci�n del Nivel de Aislamiento: Un Bean de Enterprise usa un nivel de aislamiento para negociar su propia interacci�n con los datos compartidos y la interacci�n de otros threads con los mismos datos compartidos. Como el nombre indica, existen varios niveles de aislamiento con TRANSACTION_SERIALIZABLE siendo el nivel m�s alto de integridad de los datos.
Nota:Debemos asegurarnos de que nuestra base de datos puede soportar el nivel elegido. En la especificaci�n 1.1 de los JavaBeans de Enterprise, s�lo los Beans de sesi�n con persistencia controlada por el Bean pueden seleccionar el nivel de aislamiento.
Si la base de datos no puede controlar el nivel de aislamiento, el servidor de JavaBeans Enterprise dar� un fallo cuando intente acceder al m�todo setTransactionIsolation de JDBC.
TRANSACTION_SERIALIZABLE: Este nivel proporciona la m�xima integridad de los datos. El Bean decide la cantidad de accesos exclusivos. Ninguna otra transaci�n puede leer o escribir estos datos hasta que la transaci�n serializable se complete.
En este contexto, serializable significa proceso como una operaci�n serial, y no deber�a confundirse con la serializaci�n de objetos para preservar y restaurar sus estados. Ejecutar transaciones como una s�la operaci�n serial es la selecci�n m�s lenta. Si el rendimiento es un problema, debemos usar otro nivel de aislamiento que cumpla con los requerimientos de nuestra aplicaci�n, pero mejore el rendimiento.
TRANSACTION_REPEATABLE_READ: En este nivel, los datos leidos por una transaci�n pueden ser leidos, pero no modificados, por otra transaci�n. Se garantiza que el dato tenga el mismo valor que cuando fue le�do por primera vez, a menos que la primera transaci�n lo cambie y escriba de nuevo el valor cambiado.
TRANSACTION_READ_COMMITTED: En este nivel, los datos le�dos por una transaci�n no pueden ser le�dos por otra transaci�n hasta que la primera transaci�n los haya entregado o deshecho
TRANSACTION_READ_UNCOMMITTED:En este nivel, los datos involucrados en una transaci�n pueden ser le�dos por otros threads antes de que la primera transaci�n se haya completado o se haya deshecho. Las otras transaciones no pueden saber si los datos fueron finalmente entregados o deshechos.
�Ejemplo de Bean Controlador
SellerBean es un Bean de sesi�n que usa RegistrationBean para chequear la ID y la password del usuario cuando alguien postea un �tem para la subasta, apunta el d�bito en la cuenta del usuario, y AuctionItemBean a�ade un nuevo �tem a la base de datos de la subasta.
La transaci�n empieza en el m�todo insertItem con el d�bito de la cuenta y termina cuando la transaci�n completa se entrega o se deshace. La transaci�n completa incluye deshacer el apunte de 50 centavos si el �tem de subasta es null (la inserci�n falla), o si se captura una excepci�n. Si el �tem de subasta no es null y la inserci�n se realiza con �xito, se entrega la transaci�n completa, incluyendo el cobro de los 50 centavos.
Para este ejemplo, el nivel de aislamiento es TRANSACTION_SERIALIZABLE, y el atributo de transaci�n es TX_BEAN_MANAGED. Los otros Beans en la transaci�n, RegistrationBean y AuctionItemBean, tienen un nivel de aislamiento de TRANSACTION_SERIALIZABLE y un atributo de transaci�n de REQUIRED.
Los cambios en esta versi�n de SellerBean sobre la versi�n del contenedor controlador se marcan con comentarios:
public int insertItem(String seller, String password, String description, int auctiondays, double startprice, String summary) throws RemoteException { //Declare transaction context variable using the //javax.transaction.UserTransaction class UserTransaction uts= null; try{ Context ectx = new InitialContext(p); //Get the transaction context uts=(UserTransaction)ctx.getUserTransaction(); RegistrationHome rhome = ( RegistrationHome)ectx.lookup("registration"); RegistrationPK rpk=new RegistrationPK(); rpk.theuser=seller; Registration newseller= rhome.findByPrimaryKey(rpk); if((newseller == null)|| (!newseller.verifyPassword(password))) { return(Auction.INVALjdcbook_USER); } //Start the transaction uts.begin(); //Deduct 50 cents from seller's account newseller.adjustAccount(-0.50); AuctionItemHome home = ( AuctionItemHome) ectx.lookup("auctionitems"); AuctionItem ai= home.create(seller, description, auctiondays, startprice, summary); if(ai == null) { //Roll transaction back uts.rollback(); return Auction.INVALjdcbook_ITEM; } else { //Commit transaction uts.commit(); return(ai.getId()); } }catch(Exception e){ System.out.println("insert problem="+e); //Roll transaction back if insert fails uts.rollback(); return Auction.INVALjdcbook_ITEM; } }
�M�todos de B�squeda de Bean Controlador
La b�squeda en el contenedor controlador descrita en el cap�tulo 2 est� basada en el m�canismo del m�todo finder donde el descriptor de desarrollo, en lugar del Bean, especifica el comportamiento del m�todo finder. Mientras el mecanismo del m�todo finder funciona bien para consultas sencillas, no puede manejar operaciones complejas que impliquen m�s de un tipo de Bean o tablas de bases de datos. Tambi�n, la especificaci�n 1.1 de los JavaBeans de Enterprise actualmente no proporciona para poner las reglas del m�todo finder en el descriptor de desarrollo.
Por eso, para consultas y b�squedas m�s complejas, tenemos que escribir b�squedas y consultas controladas por el Bean. Esta secci�n explica c�mo escribir una versi�n de la facilidad de b�squeda de la casa de subastas controlada por el Bean. La b�squeda controlada por el Bean inplica cambios en el m�todo AuctionServlet.searchItems y un nuevo Bean de sesi�n, SearchBean.
�AuctionServlet.searchItems
La b�squeda empieza cuando el usuario final env�a una cadena de b�squeda a la facilidad de b�squeda de la p�gina principal de la casa de subastas, y pulsa el boton Submit. Esto llama a AuctionServlet, que recupera la cadena de b�squeda desde la cabecera HTTP y la pasa al m�todo searchItem.
Nota: La l�gica de b�squeda para este ejemplo es bastante simple. El prop�sito es mostrar c�mo mover la l�gica de b�squeda a otro Bean Enterprise separado para que podamos mover b�squedas m�s complejas nosotros solos.

La operaci�n searchItem se hace en dos partes: 1) usar la cadena de b�squeda para recuperar las claves primarias, y 2) usar las claves primarias para recuperar los �tems de la subasta
Parte 1: Lo primero que hace el m�todo searchItems es pasar la cadena de b�squeda enviada por el usuario final al Bean de sesi�n SearchBean.
SearchBean (descrito en la siguiente secci�n) implementa una b�squeda controlada por Bean que recupera una lista de claves primarias para todos los �tems de la subasta cuyo campo Summary contenga caracteres que correspondan con los de la cadena de b�squeda. Esta lista es devuelta al m�todo searchItems en una variable Enumeration.
Enumeration enum=(Enumeration) search.getMatchingItemsList(searchString);

Parte 2: El m�todo searchItems usa la lista Enumeration devuelta en la parte 1 y usa AuctionItemBean para recuperar cada Bean por turno llamando a findByPrimaryKey sobre cada clave primaria de la lista. Esta es una b�squeda controlada por contenedor basada en el mecanismo del m�todo finder descrito en el cap�tulo 2.
//Iterate through search results while ((enum != null) && enum.hasMoreElements())) { while(enum.hasMoreElements(in)) { //Locate auction items AuctionItem ai=ahome.findByPrimaryKey(( AuctionItemPK)enum.nextElement()); displayLineItem(ai, out); } }
�SearchBean
La clase SearchBean.java define una b�squeda controlada por el Bean para claves primarias de �tems de subasta con los campos summary que contienen caracteres que corresponden con la cadena de b�squeda. Este Bean establece una conexi�n con la base de datos, y proporciona los m�todos getMatchingItemsList y EJBCreate.
�Conexi�n con la Base de Datos
Como este Bean controla su propio acceso a la base de datos, tiene que establecer su propia conexi�n con ella. No puede delegar esto al contenedor.
La conexi�n con la base de datos se establece ejemplarizando una clase Driver est�tica y proporcionando el m�todo getConnection. Este m�todo requiere una clase est�tica DriverManager para registrar un driver con la base de datos que corresponda con la URL. En este caso la URL es weblogic.jdbc.jts.Driver.
//Establish database connection static { new weblogic.jdbc.jts.Driver(); } public Connection getConnection() throws SQLException { return DriverManager.getConnection( "jdbc:weblogic:jts:ejbPool"); }
�Obtener la Lista de �tems Encontrados
El m�todo getMatchingItemsList busca AuctionItemsBean y crea un objeto PreparedStatement para hacer una consulta a la base de datos por los campos summary que contengan la cadena de b�squeda. Los datos se leen desde la base de datos dentro de un ResultSet, almacenado en un Vector, y devuelto a AuctionServlet.
public Enumeration getMatchingItemsList( String searchString) throws RemoteException { ResultSet rs = null; PreparedStatement ps = null; Vector v = new Vector(); Connection con = null; try{ //Get database connection con=getConnection(); //Create a prepared statement for database query ps=con.prepareStatement("select id from auctionitems where summary like ?"); ps.setString(1, "%"+searchString+"%"); //Execute database query ps.executeQuery(); //Get results set rs = ps.getResultSet(); //Get information from results set AuctionItemPK pk; while (rs.next()) { pk = new AuctionItemPK(); pk.id = (int)rs.getInt(1); //Store retrieved data in vector v.addElement(pk); } rs.close(); return v.elements(); }catch (Exception e) { System.out.println("getMatchingItemsList: "+e); return null; }finally { try { if(rs != null) { rs.close(); } if(ps != null) { ps.close(); } if(con != null) { con.close(); } } catch (Exception ignore) {} } }
�M�todo Create
El m�todo ejbCreate crea un objeto javax.naming.InitialContext. Esta es una clase JNDI (Java Nanimg and Directory) que permite a SearchBean acceder a la base de datos sin relacionarse con el contenedor:
public void ejbCreate() throws CreateException, RemoteException { Properties p = new Properties(); p.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.TengahInitialContextFactory"); try{ ctx = new InitialContext(p); }catch(Exception e) { System.out.println("create exception: "+e); } }