Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración

.�Contexto

Los beans de entidad no se han pensado para representar todos los objetos persistentes del modelo. Los beans de entidad son mejores para objetos de negocio persistentes gen�ricos.

.�Problema

En una aplicaci�n de la plataforma J2EE, los clientes -- como aplicaciones, p�ginas JSP, servlets, componentes JavaBeans -- acceden a los beans de entidad mediante sus interfaces remotos. As�, toda llamada de cliente potencialmente se enruta a trav�s de los stubs (trozos) y skeletons (esqueletos), incluso si el cliente y el bean enterprise est�n en la misma M�quina Virtual Java, en el mismo sistema operativo o en la misma m�quina f�sica. Cuando los beans enterprise son objetos espec�ficos, los clientes tienden a invocar los m�todosdel bean de forma m�s individual, resultando en una gran sobrecarga en la red.

Los beans de entidad representan objetos persistentes de negocio distribuidos. Siempre que desarrollemos o migremos una aplicaci�n a la plataforma J2EE, la especificad de los objetos es muy importante cuando decidimos implementarlos como beans de entidad. Los beans de entidad deber�an representar objetos de negocio gen�ricos, como aquellos que proporcionan un comportamiento complejo m�s all� de simplemente obtener y seleccionar valores de campos. Estos objetos gen�ricos normalmente tienen objetos dependientes. Un objeto dependiente es un objeto que no tiene un significado real cuando no est� asociado con su padre gen�rico.

Un problema recurrente es el mapeo directo del modelo a un modelo de JavaBeans Enterpise (especificamente beans de entidad). Esto crea una relaci�n entre los objetos beans de entidad en su consideraci�n de gen�ricos contra objetos espec�ficos (o dependientes). Determinar cuando utilizar objetos gen�ricos o especificados es muy dificil y se puede hacer mejor utilizando un modelo de relaciones mediante Unified Modeling Language (UML).

Hay varias �reas que se ven impactadas por la aproximaci�n del dise�o del bean de entidad espec�fico:

  • Relaciones de Entidades - Mapear directamente un objeto de modelo a un modelo EJB no tiene en cuenta el impacto de las relaciones entre objetos. Las relaciones inter-objetos se transforman directamente en relaciones inter-beans. Como resultado, un bean de entidad podr�a contener o almacenar una referencia remota a otro bean de entidad. Sin embargo, mantener referencias remotas a objetos distribuidos implica t�cnicas y sem�nticas diferentes a la de mantener referencias a objetos locales. Junto con el incremento de complejidad del c�digo, se reduce la flexibilidad porque el bean de entidad debe cambiar si cambia alguna de sus relaciones.
    Adem�s, no hay garant�as de la validez de las referencias de un bean de entidad a otro bean de entidad en el tiempo. Dichas referencias se establecen din�micamente utilizando el objeto home de la entidad y la clave primaria para ese ejemplar de bean. Esto implica una alta sobrecarga de mantenimiento para el chequeo de validez de la referencia por cada referencia de bean-de-entidad-a-bean-de-entidad.
  • Manejabilidad - Implementar objetos espec�ficos como beans de entidad resulta en un mayor n�mero de beans de entidad en el sistema, el desarrollador debe proporcionar clases para el interface home, el interface remote, la implementaci�n del bean y la clave primaria.
    Adem�s, el contenedor podr�a generar clases para soportar la implementaci�n del bean de entidad. Cuando se crea el bean, esta clases se convierten en objetos reales en el contenedor. En breve, el contenedor crea varios objetos para soportar cada ejemplar de bean de entidad. Un mayor n�mero de beans de entidad significa m�s clases y c�digo que mantener por el equipo de desarrollo. Tambi�n resulta en un mayor n�mero de objetos en el contenedor. Esto puede impactar negativamente en el rendimiento de la aplicaci�n.
  • Rendimiento de Red -- los beans de entidad espec�ficos, potencialmente tienen m�s relaciones inter-beans. Los beans de entidad son objetos distribuidos. Cuando un bean de entidad invoca un m�todo de otro bean de entidad, el contenedor trata esa llamada como una llamada remota, incluso si ambos beans est�n en el mismo contenedor. Si el n�mero de relaciones bean-de-entidad-a-bean-de-entidad se incrementa, se reduce la escalabilidad del sistema debido a la fuerte sobrecarga de la red.
  • Dependencia del Esquema de Base de Datos - Cuando los beans de entidad son espec�ficos cada ejemplar normalmente representa una s�la fila de la base de datos. Esto no es una aplicaci�n apropiada del dise�o de beans de entidad, ya que los beans de entidad son m�s apropiados para componentes gen�ricos. La implementaci�n de beans de entidad espec�ficos normalmente es una representaci�n directa del esquema de la base de datos subyacente en el dise�o del bean de entidad. Cuando los clientes utilizan estos beans de entidad espec�ficos, est�n operando esencialmente a nivel de fila en la base de datos, ya que cada bean de entidad efectivamente es una �nica fila. Como el bean de entidad modela directamente una sola fila de la base de datos, los clientes dependen del esquema de la base de datos. Cuando el esquema cambia, tambi�n deben cambiar las definiciones del bean de entidad. Adem�s, como los clientes est�n operando con la misma especificidad, deben observar y reaccionar ante estos cambios. Esta dependencia del esquema causa una p�rdida de flexibilidad e incrementa la sobrecarga de mentenimiento requerida cuando cambia el esquema.
  • Especificidad del Objeto (Gen�rico frente a Espec�fico) -- La especificidad del objeto impacta en la transferencia de datos entre el bean enterprise y el cliente. En muchas aplicaciones, los clientes necesitan un trozo de datos mayor que una o dos filas de la tabla. En dichos casos, implementar cada unos de estos objetos espec�ficos como un bean de entidad significa que el cliente tendr�a que manejar las relaciones entre todos esos objetos espec�ficos. Dependiendo de los requerimientos de datos, el cliente podr�a tener que realizar muchas b�squedas de varios beans de entidad para obtener la informaci�n necesaria.

.�Causas

  • Los beans de entidad son mejores para implementar objetos gen�ricos debido a la alta sobrecarga asociada con todo bean de entidad. Todo bean de entidad se implementa utilizando muchos objetos, como el objeto home, el objetoremote, la implementaci�n del bean, y la clave primaria, y todos son controlados por los servicios del contenedor.
  • Las aplicaciones que mapean directamente un esquema de una base de datos relacional a beans de entidad (donde cada fila de la tabla se representa como un ejemplar de un bean de entidad) tienden a tener un mayor n�mero de beans de entidad espec�ficos. Es deseable mantener el n�mero de beans de entidad gen�ricos y reducir el n�mero de beans de entidad de la aplicaci�n, y as� reducir el n�mero de beans deentidad de la aplicaci�n.
  • Mapear directamente el modelo de objetos al modelo EJB trae beans de entidad espec�ficos. Estos beans normalmente se mapean al esquema de la base de datos. Este mapeo de entidades-a-fila-de-base-de-datos causa problemas relacionados con el rendimiento, la manejabilidad, la seguridad y el manejo de transaciones. Las relaciones entre las tablas se implementan como relaciones entre beans de entidad, lo que significa que los beans de entidad contienen referencias a otros beans de entidad para implementar las relaciones espec�ficas. Es muy costoso manejar las relaciones entre estos beans porque deben establecerse din�micamente, utilizando los objetos home de los beans de entidad y las claves primarias de los beans enterprise.
  • Los clientes no necesitan conocer la implementaci�n del esquema de la base de datos para utilizar y soportar los beans de entidad. Con beans de entidad espec�ficos, el mapeo se hace normalmente para que cada ejemplar de bean de entidad corresponda con una s�la fila de la base de datos. Este mapeo espec�fico crea una dependencia entre el cliente y el esquema de la base de datos subyacente, ya que el cliente trata con beans espec�ficos y �stos esencialmente son una representaci�n directa del esquema subyacente. Esto resulta en un fuerte acoplamineto entre el esquema de la base de datos y los beans de entidad. Un cambio en el esquema causa el cambio correspondiente en el bean de entidad, y adem�s requiere el cambio correspondiente en los clientes.
  • Hay un incremento de la charlataner�a de las aplicaciones debido a la intercomunicaci�n entre los beans espec�ficos. Esta excesiva comunicaci�n entre los beans espec�ficos frecuentemente provoca un cuello de botella en el rendimiento. Toda llamada a m�todo del bean de entidad se hace mediante la capa de red, incluso si el llamador est� en el mismo espacio de direcciones que el bean llamado (es decir, si tanto el cliente como el bean de entidad est�n en el mismo contenedor). Aunque algunos contenedores han optimizado este escenario, el desarrollador no puede esperar esta optimizaci�n en todos los contenedores.
  • Se puede observar una charlataneria adicional entre el cliente y los beans de entidad porque el cliente podr�a tener que comunicar con muchos beans de entidad espec�ficos para cumplir los requerimientos. Es deseable reducir la comunicaci�n entre los beans de entidad para reducir la charlataneria entre el cliente y la capa del bean de entidad.

.�Soluci�n

Utilizar Composite Entity para modelar, representar y manejar un conjunto de objetos persistentes relacionados en vez de representarlos como beans de entidad espec�ficos individuales. Un bean Composite Entity representa un grupo de objetos.

Para entender esta soluci�n, primero definamos que significan los objetos persistentes y discutiremos sus relaciones.

Un objeto persistente es un objeto que se almacena en alg�n tipo de almacenamiento. Normalmente m�ltiples clientes comparten los objetos persistentes. Los objetos persistentes se pueden clasificar en dos tipos: objetos gen�ricos y objetos dependientes.

Un objeto gen�rico es autosuficiente. Tiene su propio ciclo de vida y maneja sus relaciones con otros objetos. Todo objeto gen�rico podr�a referenciar o contener uno o m�s objetos. El objeto gen�rico normalmente maneja el ciclo devida de estos objetos. De ah�, que esos objetos sean conocidos como objetos dependientes. Un objeto dependiente puede ser un simple objeto auto-contenido o podr�a a su vez contener otros objetos dependientes.

El ciclo de vida de un objeto dependiente est� fuertemente acoplado al ciclo de vida del objeto gen�rico. Un cliente s�lo podr�a acceder al objeto dependiente atrav�s del objeto gen�rico. Es decir, los objetos dependientes no se exponen directamente a los clientes porque su objeto padre (gen�rico) los maneja. Los objetos dependientes no pueden existir por s� mismos. En su lugar, siempre necesitan tener su objeto gen�rido (o padre) para justificar su existencia.

Normalmente, podemos ver la relaciones entre un objeto gen�rico y sus objetos dependientes como un �rbol. El objeto gen�rico es la ra�z del �rbol (el nodo ra�z). Cada objeto dependiente puede ser un objeto dependientes solitario (un nodo hoja) que es hijo del objeto gen�rico. O, el objeto dependiente puede tener relaciones padre-hijo con otros objetos dependientes, en cuyo caso se considera un nodo rama.

Un bean Composite Entity puede representar un objeto gen�rico y todos sus objetos dependientes relacionados. Esta combinaci�n de objetos persistentes relacionados dentro de un s�lo bean de entidad reduce dr�sticamente el n�mero de beans de entidad requeridos por la aplicaci�n. Esto genera un bean de entidad altamente gen�rico que puede obtener mejor los beneficios de los beans de entidad que un bean de entidad espec�fico.

Sin la aproximaci�n Composite Entity, hay una tendencia a ver todos los objetos gen�ricos y dependientes como beans de entidad separados, suponiendo un gran n�mero de beans de entidad.

.�Estructura

Aunque hay muchas estrategias para implementar el patr�n Composite Entity, la primera que vamos a explicar est� representada en el siguiente diagrama de clases. Aqu� el Composite Entity contiene el objeto gen�rico, y �ste contiene objetos dependientes:

El diagrama de secuencia de la siguiente imagen muestra las interacciones para este patr�n:

.�Participantes y Responsabilidades

.�CompositeEntity

CompositeEntity es un bean de entidad gen�rico. Podr�a ser un objeto gen�rico, o podr�a contener una referencia a un objeto gen�rico.

.�Coarse-Grained Object

Un objeto gen�rico es un objeto que tiene su propio ciclo de vida y que maneja sus propias relaciones con otros objetos. Un objeto gen�rico puede ser un objeto Java contenido en el CompositeEntity. O, el propio Composite Entity puede ser un objeto gen�rico que contiene objetos dependientes.

.�DependentObject1, DependentObject2, y DependentObject3

Un objeto dependiente es un objeto que depende de un objeto gen�rico el cual gobierna el ciclo de vida del objeto dependiente. Un objeto dependiente puede contener otros objetos dependientes; as� podr�a existir un �rbol de objetos dentro del Composite Entity.

.�Estrategias

Esta secci�n explica las diferentes estrategias para implementar el patr�n Composite Entity. Las estrategias consideran posibles alternativas y opciones para objetos persistentes (gen�ricos y dependientes), y la utilizaci�n deTransfer Objects.

.�El Composite Entity Contiene Objetos Gen�ricos

En esta estrategia, el Composite Entity almacena o contiene el objeto gen�rico. Este objeto gen�rico contin�a teniendo relaciones con sus objetos dependientes. La secci�n Estructuradescribe esta estrategia principal.

.�El Composite Entity Implementa el Objeto Gen�rico

En esta estrategia, el propio Composite Entity es el objeto gen�rico y tiene atributos y m�todos de objeto gen�rico. Los objetos dependientes son atributos del Composite Entity. Como el Composite Entity es el objeto gen�rico, el bean de entidad expresa y maneja todas las relaciones entre el objeto gen�rico y los objetos dependientes. La siguiente figura muestra el diagrama de clases de esta estrategia:

En esta otra figura podemos ver el diagrama de secuencia para esta estrategia:

.�Lazy Loading

Un Composite Entity puede estar compuesto de muchos niveles de objetos dependientes en su �rbol de objetos. Cargar todos los objetos dependientes cuando el contendor EJB llama al ejbLoad() del bean puede utilizar mucho tiempo y recursos. Una forma de optimizar esto es utilizar esta estrategia para cargar los objetos dependientes. Cuando se llama al m�todo ejbLoad(), primero s�lo carga aquellos objetos dependientes que son m�s cruciales para los clientes deComposite Entity. Luego, cuando el cliente accede a un objeto dependiente que no se haya cargado de la base de datos, el Composite Entity puede realizar una carga bajo demanda. As�, si no se utilizan algunos objetos dependientes, �stos no ser�n cargados en la inicializaci�n. Sin embargo, cuando el cliente necesita dichos objetos dependientes, los carga en cualquier momento. Una vez que se han cargado los objetos dependientes, las siguientes llamadas al m�todo ejbLoad() deben incluir esos objetos dependientes para recargar o sincronizar los cambios con el almacenamiento persistente.

.�Store Optimization (Dirty Marker)

Un problema com�n con la persistencia controlada por el bean ocurre cuando almacenamos todo el conjunto de objetos durante una operaci�n ejbStore(). Como el contendor EJB no tiene forma de conocer que datos han cambiado en el bean de entidad y en sus objetos dependientes, pone el bal�n en el lado del desarrollador para que determine qu� y como almacenar. Algunos contenedores EJB proporcionan una caracter�stica para indentificar qu� objetos del Composite Entity se necesita almacenar debido a una actualizaci�n anterior. Esto se podr�a hacer si los desarrolladores implementaran un m�todo especial en los objetos dependientes, como isDirty(), al que llame el contenedor para comprobar si el objeto se ha actualizado desde la �ltima operaci�n ejbStore().

Una soluci�n gen�rica podr�a ser utilizar un interface, DirtyMarker, como se ve en el diagrama de la siguiente figura. La idea es hacer que los objetos dependientes implementen el interface DirtyMarker para dejarle al llamador (normalmente el m�todo ejbStore()) saber si el estado del objeto dependiente ha cambiado. De esta forma, el llamador puede elegir si obtener los datos para su almacenamiento posterior.

La siguiente figura contiene un diagrama de secuencia que muestra un ejemplo de interacci�n para esta estrategia.

El cliente realiza un actualizaci�n del Composite Entity, que resulta en un cambio en DependentObject3. Se accede a DependentObject3 mediante su padre DependentObject2. El Composite Entity es el padre de DependentObject2. Cuando se realiza la actualizaci�n, se llama al metodo setDirty() en DependentObject3. Posteriormente, cuando el contenedor llama al m�todo ejbStore() sobre este ejemplar de Composite Entity, el m�todo ejbStore() puede chequear qu� objetos dependientes se han cambiado y grabar selectivamente en la base de datos aquellos que han cambiado. La marca dirty se resetea despu�s de que la grabaci�n haya tenido �xito.

El interface DirtyMarker tambi�n puede incluir m�todos que reconozcan otros estados de persistencia del objeto dependiente. Por ejemplo, si se incluye un nuevo objeto dependiente dentro del Composite Entity, el m�todo ejbStore() deber�a poder reconocer qu� operaci�n utilizar en este caso, el objeto dependiente no se ha modificado, sino que es nuevo. Extendiendo el interface DirtyMarker para incluir un m�todo llamado isNewz(), el m�todo ejbStore() puede invocar una operaci�n de inserci�n en lugar de una operaci�n de actualizaci�n. De forma similar, incluyendo un m�todo llamado isDeleted(), el m�todo ejbStore() puede invocar una operaci�n de borrado cuando sea necesario.

En casos donde se llama a ejbStore() sin actualizaciones intermedias, del Composite Entity, ninguno de los objetos dependientes ha sido actualizado.

Esta estrategia evita la gran sobrecarga de tener que persistir todos los objetos dependientes en la base de datos siempre que el contenedor llame al m�todo ejbStore().

Nota:
La especificaci�n EJB 2.0 corrige las estrategias Lazy Loading y Store Optimization. Es posible utilizar estas estrategias en implementaciones anteriores a la EJB 2.0.
.�Composite Transfer Object

Con un Composite Entity, un cliente puede obtener toda la informaci�n requerida con s�lo una llamada a un m�todo. Como el Composite Entity o implementa o contiene el objeto gen�rico y la herencia (o �rbol) de objetos dependientes, puede crear el Transfer Object requerido y devolverlo al cliente aplicando el patr�nTransfer Object. En la siguiente imagen podemos ver el diagrama de secuencia para esta estrategia:

El Transfer Object puede ser un objeto simple o compuesto que tenga subobjetos, dependiendo de los datos solicitados por el cliente. El Transfer Object es serializable y se pasa por valor al cliente. ElTransfer Object funciona s�lo como un mero transmisor de objetos; no tiene responsabilidades con respecto a la seguridad, las transaciones, y la l�gica de negocio. El Transfer Object empaqueta toda la informaci�n en un objeto, obteniendo la informaci�n con una llamada remota en vez de con m�ltiples llamadas. Una vez que el cliente recibe el Transfer Object, todas las llamadas posteriores del cliente al Transfer Object son llamadas locales.

La discusi�n apunta a c�mo la entidad puede empaquetar todos sus datos en un Transfer Object compuesto y devolver este al cliente. Sin embargo,esta estrategia tambi�n permite al bean de entidad devolver s�lo los datos que el cliente ha solicitado. Si el cliente s�lo necesita los datos de un subconjunto de objetos dependientes, entonces el Transfer Object compuesto devuelto puede contener los datos derivados s�lo de aquellas partes requeridas y no de todos los objetos dependientes. Esto podr�a ser una aplicaci�n de la estrategia Multiple Transfer Objects del patr�n Transfer Object.

.�Consecuencias

  • Elimina las Relaciones Inter-Entidades
    Utilizando el patr�n Composite Entity, los objetos dependientes se unen en un s�lo bean de entidad, eliminando todas las relaciones entre beans de entidad. Este patr�n proporciona un lugar central para manejar las relaciones y la herencia de los objetos.
  • Mejora la Manejabilidad Reduciendo los Beans de Entidad
    Como se ha explicado, la implementaci�n de objetos persistentes como beans de entidad espec�ficos resulta en un mayor n�mero de clases que necesitamos desarrollar y mantener. Utilizar Composite Entity reduce el n�mero de clases EJB y el c�digo, y hace m�s f�cil el mantenimiento. Mejora la manejabilidad de la aplicaci�n teniendo un menor n�mero de componentes gen�ricos en lugar de tener muchos m�s componentes espec�ficos.
  • Mejora el Rendimiento de Red
    La uni�n de objetos dependientes mejora el rendimiento general. Esta uni�n elima toda la comunicaci�n espec�fica entre objetos dependientes a trav�s de la red. Si todos los objetos dependientes se dise�aran como beans de entidad espec�ficos, provocar�a una sobrecarga en la red debido a la comunicaci�n entre los beans de entidad.
  • Reduce la Dependencia del Esquema de la Base de Datos
    Cuando se utiliza el patr�n Composite Entity, resulta en implementaciones de beans de entidad gen�ricos. El esquema de la base de datos est� oculto para los clientes, ya que el mapeo entre el bean de entidad y el esquema es interno del bean de entidad. Los cambios en el esquema de la base de daos podr�an requerir cambios en los beans de CompositeEntity. Sin embargo, los clientes no se ven afectados porque los beans Composite Entity no exponen el esquema al mundo exterior.
  • Incrementa la Generalidad del Objeto
    Con un Composite Entity, el cliente normalmente busca un s�lo bean de entidad en lugar de una gran cantidad de beans de entidad espec�ficos. El cliente le pide los datos al Composite Entity. �ste puede crear un Transfer Object compuesto que contenga todos los datos del bean de entidad y devolverlo al cliente en una �nica llamada a m�todo.
  • Facilita la Creaci�n de Transfer Object Compuestos
    Utilizando esta estrategia, se reduce la comunicaci�n entre el cliente y el bean de entidad, ya que el bean Composite Entity puede devolver un Transfer Object compuesto proporcionando un mecanismo para enviar Transfer Objects serializados. Aunque un Transfer Object devuelva todos los datos en una llamada remota, la cantidad de datos devueltos con esta �nica llamada es mucho mayor que la cantidad de datos devuelta por llamadas remotas independientes para obtener propiedades de beans de entidad individuales. Este inconveniente se puede sobrellevar cuando el objetivo es evitar la repetici�n de llamadas remotas y b�squedas m�ltiples.
  • Sobrecarga de Grupos de Objetos Dependientes Multi-Nivel
    Si el conjunto de objetos dependientes manejado por el Composite Entity tiene muchos niveles, entonces se incrementa la carga y almacenamiento de objetos dependientes. Esto se puede reducir utilizando estrategias de optimizaci�n para la carga y almacenamiento, peo entonces podr�a aparecer otra sobrecarga asociada con el chequeo de objetos obsoletos para su almacenamiento y la carga de los objetos requeridos.

.�Codigo de Ejemplo

Consideremos una aplicaci�n de Servicio Profesional de automatizaci�n (PSA) donde se implementa un objeto de negocio: Resource utilizando el patr�n Composite Entity. Resource representa el recurso "empleado" que est� asignado a los proyectos. Cada objeto Resource puede tener diferentes objetos dependientes, de esta forma:

  • BlockOutTime - Este objeto dependiente representa el periodo de tiempo que el recurso no est� disponible por razones como formaci�n, vacaciones, d�as libres, etc. Como cada recurso puede tener varios periodos de absentismo, la relaci�n Resource-a-BlockOutTime es de uno-a-muchos.
  • SkillSet - Este objeto dependiente representa las "Habilidades" (Skills) que posee un recurso. Como cada recurso puede tener m�tiples habilidades, la relaci�n de Resource-a-SkillSet es de uno a muchos.

.�Implementaci�n del patr�n Composite Entity

El patr�n para el objeto de negocio Resource se implementa como un Composite Entity (ResourceEntity), como se ve en el siguiente c�digo. La relaci�n uno-a-muchos con sus objetos dependientes (objetos BlockOutTime y SkillSet) se implementa utilizando collections.


package corepatterns.apps.psa.ejb;

import corepatterns.apps.psa.core.*;
import corepatterns.apps.psa.dao.*;
import java.sql.*;
import javax.sql.*;
import java.util.*;
import javax.ejb.*;
import javax.naming.*;

public class ResourceEntity implements EntityBean {
  public String employeeId;
  public String lastName;
  public String firstName;
  public String departmentId;
  public String practiceGroup;
  public String title;
  public String grade;
  public String email;
  public String phone;
  public String cell;
  public String pager;
  public String managerId;
  
  // Collection of BlockOutTime Dependent objects
  public Collection blockoutTimes;

  // Collection of SkillSet Dependent objects
  public Collection skillSets;

  ...  

  private EntityContext context;
// Entity Bean methods implementation
public String ejbCreate(ResourceTO resource) throws 
  CreateException {
    try {
      this.employeeId = resource.employeeId;
      setResourceData(resource);
      getResourceDAO().create(resource);
    } catch(Exception ex) {
      throw new EJBException("Reason:" + ...);
    }
    return this.employeeId;
}
  
public String ejbFindByPrimaryKey(String primaryKey) 
  throws FinderException {
    boolean result;
    try {
      ResourceDAO resourceDAO = getResourceDAO();
      result = 
        resourceDAO.selectByPrimaryKey(primaryKey);
    } catch(Exception ex) {
      throw new EJBException("Reason:" + ...);
    }
    if(result) {
      return primaryKey;
    }
    else {
      throw new ObjectNotFoundException(...);
    }
  }
  
  public void ejbRemove() {
    try {
      // Remove dependent objects
      if(this.skillSets != null) {

        SkillSetDAO skillSetDAO = getSkillSetDAO();
        skillSetDAO.setResourceID(employeeId);
        skillSetDAO.deleteAll();
        skillSets = null;
      }
      if(this.blockoutTime != null) {
        BlockOutTimeDAO blockouttimeDAO = 
            getBlockOutTimeDAO();
        blockouttimeDAO.setResourceID(employeeId);
        blockouttimeDAO.deleteAll();
        blockOutTimes = null;
      }

      // Remove the resource from the persistent store
      ResourceDAO resourceDAO = new 
        ResourceDAO(employeeId);
      resourceDAO.delete();
    } catch(ResourceException ex) {
      throw new EJBException("Reason:"+...);
    } catch(BlockOutTimeException ex) {
      throw new EJBException("Reason:"+...);
    } catch(Exception exception) {
      ...
    }
  }
  public void setEntityContext(EntityContext context) 
  {
    this.context = context;
  }
  
  public void unsetEntityContext() {
    context = null;
  }
  
  public void ejbActivate() {
    employeeId = (String)context.getPrimaryKey();
  }
  
  public void ejbPassivate() {
    employeeId = null;
  }
  
  public void ejbLoad() {
    try {
      // load the resource info from
      ResourceDAO resourceDAO = getResourceDAO();
      setResourceData((ResourceTO) 
        resourceDAO.load(employeeId));
      
      // Load other dependent objects, if necessary
      ...
    } catch(Exception ex) {
      throw new EJBException("Reason:" + ...);
    }
  }
  
  public void ejbStore() {
    try {
      // Store resource information
      getResourceDAO().update(getResourceData());

      // Store dependent objects as needed
      ...
    } catch(SkillSetException ex) {
      throw new EJBException("Reason:" + ...);
    } catch(BlockOutTimeException ex) {
      throw new EJBException("Reason:" + ...);
    }
    ...
  }
  public void ejbPostCreate(ResourceTO resource) {
  }

  // Method to Get Resource Transfer Object
  public ResourceTO getResourceTO() {
    // create a new Resource Transfer Object
    ResourceTO resourceTO = new 
        ResourceTO(employeeId);

    // copy all values 
    resourceTO.lastName = lastName;
    resourceTO.firstName = firstName;
    resourceTO.departmentId = departmentId;
    ...
    return resourceTO;
  }

  public void setResourceData(ResourceTO resourceTO) {
    // copy values from Transfer Object into entity bean
    employeeId = resourceTO.employeeId;
    lastName = resourceTO.lastName;
    ...
  }

  // Method to get dependent Transfer Objects
  public Collection getSkillSetsData() {
    // If skillSets is not loaded, load it first.
    // See Lazy Load strategy implementation.

    return skillSets;  
  }
  ...

  // other get and set methods as needed
  ...

  // Entity bean business methods
  public void addBlockOutTimes(Collection moreBOTs) 
  throws BlockOutTimeException {
    // Note: moreBOTs is a collection of 
    // BlockOutTimeTO objects
    try {
      Iterator moreIter = moreBOTs.iterator();
      while(moreIter.hasNext()) {
        BlockOutTimeTO botTO = (BlockOutTimeTO)moreIter.next();
        if (! (blockOutTimeExists(botTO))) {
          // add BlockOutTimeTO to collection
          botTO.setNew();
          blockOutTime.add(botTO);
        } else {
          // BlockOutTimeTO already exists, cannot add
          throw new BlockOutTimeException(...);
        }
      }
    } catch(Exception exception) {
      throw new EJBException(...);
    }
  }

  public void addSkillSet(Collection moreSkills) 
  throws SkillSetException {
    // similar to addBlockOutTime() implementation
    ...
  }

  ...

  public void updateBlockOutTime(Collection updBOTs) 
  throws BlockOutTimeException {
    try {
      Iterator botIter = blockOutTimes.iterator();
      Iterator updIter = updBOTs.iterator();
      while (updIter.hasNext()) {
        BlockOutTimeTO botTO = (BlockOutTimeTO)
          updIter.next();
        while (botIter.hasNext()) {
          BlockOutTimeTO existingBOT = 
            (BlockOutTimeTO) botIter.next();
          // compare key values to locate BlockOutTime
          if (existingBOT.equals(botTO)) {
            // Found BlockOutTime in collection
            // replace old BlockOutTimeTO with new one
            botTO.setDirty(); //modified old dependent
            botTO.resetNew(); //not a new dependent
            existingBOT = botTO;
          }
        }
      }
    } catch (Exception exc) {
      throw new EJBException(...);
    }
  }

  public void updateSkillSet(Collection updSkills) 
  throws CommitmentException {
    // similar to updateBlockOutTime...
    ...
  }

  ...

}

.�Implementar la Estrategia Lazy Loading

Cuando el contenedor carga por primera vez el Composite Entity utilizando el m�todo ejbLoad(), asumimos que s�lo se han cargado los datos del recurso. Esto incluye los atributos listados en el bean ResourceEntity, excluyendo las colecciones de objetos dependientes. Los objetos dependientes entonces s�lo se pueden cargar si el cliente invoca un m�todo de negocio que necesita que se carguen esos objetos dependientes. Consiguientemente, el m�todo ejbLoad() necesita seguir la pista de los objetos dependientes cargados de esta manera e incluirlos para su recarga.

Abajo podemos ver los m�todos m�s importantes de la clase ResourceEntity:



...
public Collection getSkillSetsData() {
throws SkillSetException {
  checkSkillSetLoad();
  return skillSets;
}

private void checkSkillSetLoad() 
throws SkillSetException {
  try {
    // Lazy Load strategy...Load on demand
    if (skillSets == null)
      skillSets = 
        getSkillSetDAO(resourceId).loadAll();
  } catch(Exception exception) {
    // No skills, throw an exception 
    throw new SkillSetException(...);
  }
}

...

public void ejbLoad() {
  try {
    // load the resource info from
    ResourceDAO resourceDAO = new 
      ResourceDAO(employeeId);
    setResourceData((ResourceTO)resourceDAO.load());
      
    // If the lazy loaded objects are already
    // loaded, they need to be reloaded.
    // If there are not loaded, do not load them
    // here...lazy load will load them later.
    if (skillSets != null) {
      reloadSkillSets();
    }
    if (blockOutTimes != null) {
      reloadBlockOutTimes();
    }
    ...
    throw new EJBException("Reason:"+...);
  }
}
...

.�Implementar la Estrategia Store Optimization (Dirty Marker)

Para utilizar la estrategia Store Optimization, necesitamos que los objetos dependientes implementen el interface DirtyMarker, como se ve en el siguiente ejemplo:


public class SkillSetTO implements DirtyMarker, 
  java.io.Serializable {
  private String skillName;
  private String expertiseLevel;
  private String info;
  ...

  // dirty flag
  private boolean dirty = false;

  // new flag
  private boolean isnew = true;

  // deleted flag
  private boolean deleted = false;

  public SkillSetTO(...) {
    // initialization
    ...
    // is new TO
    setNew();
  }

  // get, set and other methods for SkillSet
  // all set methods and modifier methods
  // must call setDirty()
  public setSkillName(String newSkillName) {
    skillName = newSkillName;
    setDirty();
  }
  ...

  // DirtyMarker methods
  // used for modified Transfer Objects only
  public void setDirty() {
    dirty = true;
  }
  public void resetDirty() {
    dirty = false;
  }
  public boolean isDirty() {
    return dirty;
  }

  // used for new Transfer Objects only
  public void setNew() {
    isnew = true;
  }
  public void resetNew() {
    isnew = false;
  }
  public boolean isNew() {
    return isnew;
  }

  // used for deleted objects only
  public void setDeleted() {
    deleted = true;
  }
  public boolean isDeleted() {
    return deleted;
  }
  public void resetDeleted() {
    deleted = false;
  }

}

En el siguiente ejemplo podemos ver el m�todo ejbStore() optimizado para utilizarlo con esta estrategia:


...

  public void ejbStore() {
    try {
      // Load the mandatory data
      getResourceDAO().update(getResourceData());

      // Store optimization for dependent objects
      // check dirty and store
      // Check and store commitments
      if (skillSets != null) {
        // Get the DAO to use to store
        SkillSetDAO skillSetDAO = getSkillSetDAO();
        Iterator skillIter = skillSet.iterator();
        while(skillIter.hasNext()) {
          SkillSetTO skill = 
            (SkillSetTO) skillIter.next();
          if (skill.isNew()) {
            // This is a new dependent, insert it
            skillSetDAO.insert(skill);
            skill.resetNew();
            skill.resetDirty();
          }
          else if (skill.isDeleted()) {
            // delete Skill
            skillSetDAO.delete(skill);
            // Remove from dependents list
            skillSets.  remove(skill);
          } 
          else if (skill.isDirty()) {
            // Store Skill, it has been modified
            skillSetDAO.update(skill);
            // Saved, reset dirty.
            skill.resetDirty();
            skill.resetNew();
          }
        }
      }

      // Similarly, implement store optimization 
      // for other dependent objects such as 

      // BlockOutTime, ...
      ...
    } catch(SkillSetException ex) {
      throw new EJBException("Reason:"+...);
    } catch(BlockOutTimeException ex) {
      throw new EJBException("Reason:"+...);
    } catch(CommitmentException ex) {
      throw new EJBException("Reason:"+...);
    }
  }

  ...

.�Implementar la Estrategia Composite Transfer Object

Ahora consideremos el requerimiento donde el cliente necesita obtener todos los datos del ResourceEntity, y no s�lo una parte. Esto se puede hacer utilizando la estrategia Composite Transfer Object como se muestra en el siguiente ejemplo:


public class ResourceCompositeTO {
  private ResourceTO resourceData;
  private Collection skillSets;
  private Collection blockOutTimes;

  // Transfer Object constructors
  ...

  // get and set methods
  ...
}

El ResourceEntity proporciona un m�todo getResourceDetailsData() para devolver el objeto Transfer Object compuesto ResourceCompositeTO:


...
public ResourceCompositeTO getResourceDetailsData() {
  ResourceCompositeTO compositeTO =
    new ResourceCompositeTO (getResourceData(),
        getSkillsData(), getBlockOutTimesData());
  return compositeTO;
}
...

.�Patrones Relacionados

  • Transfer Object
    El patr�n Composite Entity usa el patr�n Transfer Object para crear el objeto que devuelve al cliente. El patr�n Transfer Object se utiliza para serializar el �rbol de objetos gen�ricos y de objetos dependientes, o parte de ese �rbol, seg�n se requiera.
  • Session Facade
    Si los objetos dependientes tienden a ser beans de entidad en vez de objetos Java normales, debemos intentar de utilizar el patr�n Session Facade para manejar las relaciones entre beans de entidad.
  • Transfer Object Assembler
    Este patr�n es similar al patr�n Transfer Object. Sin embargo, en este caso, las fuentes de datos para todos los Transfer Objects del compuesto son parte del propio Composite Entity, mientras que para el Transfer Object Assembler, los datos puede ser diferentes beans de entidad, beans de sesi�n, DAOs, objetos Java, etc.

.�Bean de Entidad como un Objeto Dependiende: Problemas y Recomendaciones

T�picamente, dise�amos los objetos dependientes como objetos Java que tienen una relaci�n directa con el objeto gen�rico padre. Sin embargo, podr�a hacer situaciones en las que un objeto dependiente podr�a aparecer como un propio bean de entidad:

  1. Si el objeto dependiente es dependiente de dos objetos padre diferentes (como es el caso de la asociaci�n de clases.
  2. Si el objeto dependiente ya existe como un bean de entidad en la misma aplicaci�n o se importa desde una aplicaci�n diferente.

En estos casos, el ciclo de vida del objeto dependiente podr�a parecer que no est� directamente relacionado y manejado por un s�lo objeto gen�rico padre. Entonces, �qu� podemos hacer cuando un objeto dependiente es un bean de entidad? �y, cu�ndo veamos un objeto dependiente que no es totalmente dependiente de su objeto padre? �o, cu�ndo no podamos identificar su �nico objeto padre?

Consideremos cada caso con un poco m�s de detalle:

  • Caso 1: El Objeto Dependiente depende de Dos Objetos Padre.

    Exploremos esto con el siguiente ejemplo. Un Commitment representa una asociaci�n entre un Resource y un Project. La siguiente figura muestra un ejemplo de diagrama de clases con las relaciones entre Project, Resource y Commitment.

    Commitment es un objeto dependiente. Projects y Resources son objetos gen�ricos. Cada Project tiene una relaci�n uno-a-muchos con objetos Commitment. Entonces. �Commitment es un objeto dependiente de Project o de Resource? La respuesta trata de analizar las interacciones para los casos de utilizaci�n que implican estos tres componentes. Si hacemos que el objeto Commitment sea dependiente del objeto Project, entonces cuando un Resource acceda a su lista de objetos Commitment, tiene que hacerlo a trav�s del objeto Project. Por otro lado, si Commitment es un objeto dependiente de un Resource, cuando Project acceda a su lista de objeto Commitment, tiene que hacerlo mediante el Resource. Estas dos elecciones introducen en el dise�o las relaciones bean-de-entidad-a-bean-de-entidad.

    Pero, �qu� pasa si Commitment es un bean de entidad en vez de un objeto dependiente? Entonces la relaci�n entre Project y su lista de objetos Commitment, y entre un Resource y su lista de objetos Commitment, ser� una relaci�n entidad-a-bean-de-entidad. Esto empeora el problema en que ahora tenemos dos relaciones bean-de-entidad-a-bean-de-entidad.

    Este tipo de relaciones no es recomendable debido a la sobrecarga asociada con su manejo y sustentaci�n.

  • Caso 2: El Objeto Dependiente ya existe como un Bean de Entidad

    En este caso, podr�a parecer que una forma de modelar esta relaci�n es almacenar la clave primaria del objeto dependitne en el objeto gen�rico. Cuando el objeto gen�rico necesite acceder al objeto dependiente, el resultado ser� una llamada bean-de-entidad-a-bean-de-entidad. Abajo podemos ver el diagrama de clases para este ejemplo:

    En la siguiente figura podemos ver el diagrama de secuencia para este escenario. El Composite Entity utiliza las referencias al objeto dependiente para buscar el bean de entidad requerido. En este caso el objeto dependiente es un proxy al bean de entidad dependiente:

    Aunque esto podr�a corrige el requerimiento de utilizar un bean de entidad dependiente partiendo de un bean de entidad padre, no es una soluci�n elegante. En su lugar, para evitar la complejidad del dise�o y manejo de las relaciones entre-entidades, consideremos la utilizaci�n de un bean de sesi�n para manejar las relaciones entre los beans de entidad. Seg�n nuestra experiencia, hemos encontrado que el patr�n Session Facade nos ayud� a solucionar este problema y nos proporcion� una mejor forma de manejar las relaciones bean-de-entidad-a-bean-de-entidad.

    Por eso, recomendamos evitar las relaciones bean-de-entidad-a-bean-de-entidad como la mejor pr�ctica, y para optimizar las relaciones dentro de un bean de sesi�n recomendamos la utilizaci�n del patr�n Session Facade.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR