Comercialización:
ventas@acsinet-solutions.com
Servicios Educacionales:
educacional@acsinet-solutions.com
Servicios de Consultoría:
consultoría@acsinet-solutions.com
Comercialización:
soporte@acsinet-solutions.com

Teléfonos:
58 93 36 63
Oficina:
58 93 36 63
Fax:
58 93 36 63
Celulares:
044 55 54 56 77 45







Mapeo Sencillo con Configuración Básica de Spring Framework

Esta serie de documentos tiene por objetivo presentar ejemplos de tipos de mapeos que se pueden realizar con Hibernate 3 y Java Persistence API ( JPA ). La intención es cubrir paulatinamente con ejemplos desde los mapeos más sencillos, hasta los más complicados.

En este segundo documento se integra el manejo de Spring Framework para la organización de servicios. De esta manera, se puede tener mucho mejor control del código asociado a servicios en una aplicación java.

El mapeo que se realiza aquí es el mismo que se mostró con anterioridad, la diferencia radica en la definición de una clase para la lógica de negocio asociada al manejo de facturas, y a la definición de los servicios de la aplicación en un archivo de servicios de Spring Framework.

La siguiente imagen ilustra la estructura de la aplicación que se presenta en este tutorial:

Si se contrasta este diagrama con el presentado en el tutorial anterior, se pueden visualizar los siguientes aspectos:

Las clases de código que se manejarán en este tutorial se pueden obtener a partir de este archivo comprimido. Para hacer uso de Spring Framework, los archivos JAR correspondientes al mismo deben encontrarse definidos para el proyecto java que se está manejando ( liga ).



Spring Framework

Spring Framework es una librería open source que ofrece facilidades avanzadas para la organización de servicios en una aplicación java. Fue planteado originalmente como un mecanismo alterno a la complejidad existente en aproximaciones como EJB 2.x. Actualmente, con la simplificación de la especificación EJB 3, Spring Framework sigue siendo una alternativa útil en escenarios que no justifican el manejo de un contenedor EJB, aunque también puede ser utilizada como tecnología complementaria a EJB sobre aquellos aspectos que no cubre la especificación estándar.

Spring Framework ofrece facilidades de integración con otras especificaciones y librerías como JDBC, Hibernate, EJB, JDO, etc. En este caso se utilizará para organizar los servicios de la aplicación. Estos servicios pueden visualizarse en la siguiente imagen:

Los servicios mostrados son los siguientes:

Todos estos servicios son configurados en el archivo services.xml que se analiza a continuación.



Configuración de Spring Framework: services.xml



Los tres servicios que integran este archivo de configuración se organizan de la siguiente manera:



<?xml version='1.0'?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/
spring-beans.dtd">
<beans>
    <bean id="dataSource" ...>...</bean>
    <bean id="sessionFactory" ...>...</bean>
    <bean id="objectManager" ...>...</bean>
</beans>

Cada bloque <bean> dentro del elemento raíz <beans> del archivo describe un servicio en particular. Toda la configuración de dicho servicio se realiza dentro de cada uno de estos bloques. Cada uno de estos servicios tiene un nombre único por medio del cual se puede hacer referencia al mismo. Los nombres de los servicios en el archivo presentado son "dataSource", "sessionFactory" y "objectManager".


Servicio de DataSource JDBC

Por ejemplo, para la configuración de la conectividad hacia la base de datos, se puede definir el bloque del servicio "dataSource" como sigue:



  <bean id="dataSource"
  class="org.apache.commons.dbcp.BasicDataSource"
   destroy-method="close">
     <property name="driverClassName">
       <value>com.informix.jdbc.IfxDriver</value>
     </property>
     <property name="url">      <property name="username">      <property name="password">      </bean>

Este bloque define un servicio implementado por una instancia de tipo javax.sql.DataSource proporcionada por el paquete Jakarta Commons DBCP. Los valores de las propiedades driverClassName, url, username y password son específicos a cada tipo de conexión hacia la base de datos, y equivalen a los valores que en el tutorial anterior se habían configurado en el archivo hibernate.cfg.xml.

La clase que se utiliza para crear este servicio se lista en el atributo class de la etiqueta ; en este caso la clase es org.apache.commons.dbcp.BasicDataSource. Esta clase cuenta con métodos setter correspondientes a las propiedades que se listan en este código; en otras palabras, cuenta con métodos setDriverClassName(), setUrl(), setUsername() y setPassword(). Estos métodos son ejecutados por Spring Framework con los valores configurados en este archivo al inicializar el servicio.

Evidentemente, este mismo mecanismo puede ser aprovechado para configurar servicios definidos por el programador.

Para hacer uso de la clase org.apache.commons.dbcp.BasicDataSource, los archivos JAR de los proyectos Apache Commons DBCP y Apache Commons Pool deben ser agregados al CLASSPATH de ejecución del programa; de lo contrario, un stack trace como el siguiente será desplegado al ejecutar el programa:



[ERROR]15/10/2007 10:53:51,195 (Test): Error:
org.springframework.beans.factory.BeanDefinitionStoreException: Error 'Bean class
[org.apache.commons.dbcp.BasicDataSource] not found' in resource'class path resource
[single_table_simple_key_basic_spring/services.xml]' at:
Bean 'dataSource'; nested exception is java.lang.ClassNotFoundException:
org.apache.commons.dbcp.BasicDataSource
java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSource
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:109)
at org.springframework.beans.factory.support.BeanDefinitionReaderUtils.
createBeanDefinition(BeanDefinitionReaderUtils.java:65)

Es importante reconocer que este tipo de excepciones se deben a la ausencia de clases en el CLASSPATH de ejecución del programa. Nótese sobre todo que esta excepción encapsula un "java.lang.ClassNotFoundException" para org.apache.commons.dbcp.BasicDataSource. Para mayor información con respecto a la interpretación de excepciones, consultar los tutoriales de la serie dedicada a la interpretación de excepciones y mensajes de error en java en este mismo sitio web.



Servicio de SessionFactory Hibernate

El servicio definido para Hibernate puede manejarse de acuerdo a la siguiente estructura:



   <bean id="sessionFactory" class=
   "org.springframework.orm.hibernate3.annotation.
  AnnotationSessionFactoryBean">
     <property name="annotatedClasses">
       <list>
         <value>
          single_table_simple_key_basic_spring.FacturaDTO
         </value>
       </list>
     </property>
     <property name="hibernateProperties">
       <props>
         <prop key="hibernate.dialect">
         org.hibernate.dialect.InformixDialect
       </prop>
       </props>
     </property>
   </bean>

La configuración de SessionFactory sobre Spring Framework que soporta el manejo de anotaciones es proporcionada por la clase AnnotationSessionFactoryBean, que es proporcionada como parte de las extensiones de Spring Framework para el manejo de Hibernate 3 ( spring-hibernate3.jar ). Si este archivo JAR no se agrega al CLASSPATH del proyecto en tiempo de ejecución, la siguiente excepción será lanzada por el programa:



[ERROR]15/10/2007 11:24:29,948 (Test): Error:
org.springframework.beans.factory.BeanDefinitionStoreException: Error 'Bean class [org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean]
not found' in resource'class path resource [single_table_simple_key_basic_spring/services.xml]' at:
Bean 'sessionFactory'; nested exception is java.lang.ClassNotFoundException:
org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean
java.lang.ClassNotFoundException:
org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:109)
at org.springframework.beans.factory.support.BeanDefinitionReaderUtils.
createBeanDefinition(BeanDefinitionReaderUtils.java:65)

La definición del servicio de Hibernate sobre Spring Framework es similar a la que se realizó con el archivo de configuración hibernate.cfg.xml en el tutorial anterior. La estructura de esta configuración podría visualizarse como sigue ( comparar esta imagen con su equivalente en el tutorial anterior ):

El bloque correspondiente a la propiedad annotatedClasses únicamente sirve para hacer referencia a clases configuradas vía anotaciones; siguiendo esta aproximación, se definen tantos elementos <value> como clases mapeadas con anotaciones existan en el proyecto actual. Para hacer referencia a clases mapeadas vía XML o a queries definidos en XML, se requiere de otra sección no mostrada aquí de nombre mappingResources. Se hablará de esta otra propiedad en un tutorial posterior de esta serie dedicado al manejo de named queries en Hibernate 3.

La propiedad "hibernateProperties" define - por su parte - las propiedades específicas de Hibernate, como el manejo de dialectos por medio de la propiedad "hibernate.dialect". En el archivo hibernate.cfg.xml, esta sección se aprovechaba también para definir facilidades como la conectividad hacia base de datos y el servicio de transacciones; sin embargo, aquí estas facilidades se definen como servicios a nivel Spring Framework que se enlazan con el componente actual, por lo que esta sección de propiedades de Hibernate resulta ser más sencilla.

En la configuración del archivo services.xml, el servicio de Hibernate "sessionFactory" depende en realidad del servicio de conexiones hacia la base de datos "dataSource". La siguiente imagen ilustra esta dependencia entre ambos servicios:

Spring Framework permite enlazar dos servicios que se relacionan entre sí a través de propiedades ( de la misma manera que se definen parámetros de configuración ). Este enlazado se puede definir de forma explícita o implícita.

El enlazado de forma explícita hace uso de elementos <property> que hacen referencia a un servicio a partir de otro. El listado de los dos servicios anteriores haciendo uso de esta aproximación sería el siguiente:



   <?xml version='1.0'?>
   <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
   "http://www.springframework.org/dtd/spring-beans.dtd">

   <beans>
     <bean id="dataSource"
     class="org.apache.commons.dbcp.BasicDataSource"
     destroy-method="close">
       <property name="driverClassName">
         <value>...</value>
       </property>
       <property name="url">
        <value>...</value>
      </property>
       <property name="username">
        <value>...</value>
      </property>
       <property name="password">
        <value>...</value>
      </property>
     </bean>
     <bean id="sessionFactory" class=
     "org.springframework.orm.hibernate3.annotation.
     AnnotationSessionFactoryBean">
       <property name="dataSource">
         <ref local="dataSource"/>
       </property>
       <property name="annotatedClasses">
         <list>
           <value>
            single_table_simple_key_basic_spring.
            FacturaDTO </value>
         </list>
       </property>
       <property name="hibernateProperties">
         <props>
           <prop key="hibernate.dialect">
             org.hibernate.dialect.InformixDialect
           </prop>
         </props>
       </property>
     </bean>
   </beans>

Nótese la definición de una propiedad "dataSource" en el servicio de Hibernate. Esta propiedad contiene una etiqueta <ref local="dataSource"/> que lo que hace en realidad es hacer referencia al servicio "dataSource" definido en este mismo archivo. De esta manera lo que ocurre es que se realiza el enlazado entre servicios que se ilustró en la imagen anterior.

A nivel código, este enlazado está soportado a través de un método setter sobre la clase AnnotationSessionFactoryBean. Este método se llama "setDataSource" para que corresponda al nombre de la propiedad "dataSource", y debe recibir un tipo de dato compatible con el servicio hacia el cual se está realizando el enlazado, en este caso javax.sql.DataSource:



  public void setDataSource( DataSource dataSource )
    { ... }

Este mecanismo tan sencillo permite también realizar asociaciones entre servicios definidos por el desarrollador; y de hecho será utilizado más adelante para relacionar las clases de lógica de negocio con el servicio de Hibernate.

Spring Framework define una característica denominada 'autowire', que permite enlazar entre sí servicios a través de algún patrón de nombres o de tipo de dato. En el ejemplo anterior, puede verse que tanto el nombre de la propiedad ( setDataSource() ), como el nombre del servicio con el que se quiere enlazar dicha propiedad se llaman "dataSource". Entonces, podemos instruir a Spring Framework para que intente realizar un enlazado automático ( autowire ) de propiedades basadas en el nombre de las propiedades de los objetos que implementan cada servicio, y de los respectivos servicios que tienen el mismo nombre que esta propiedad. El código base para indicar esto a nivel archivo de servicios es el siguiente:



   <?xml version='1.0'?>
   <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
   "http://www.springframework.org/dtd/spring-beans.dtd">
   <beans default-autowire="byName">
     <bean id="dataSource" ...>...</bean>
     <bean id="sessionFactory" ...>...</bean>
     <bean id="objectManager" ...>...</bean>
   </beans>

Nótese que el atributo default-autowire permite indicar que los servicios se tratarán de enlazar entre sí haciendo coincidir el nombre de una propiedad en la clase, con el nombre de un servicio. Así, si un componente tiene un método setDataSource(), éste tratará de ser enlazado con un componente llamado "dataSource", si dicho componente existe.

Una vez hecho esto, la capacidad de autoenlazado puede ser desactivada a nivel componente individual, por medio del atributo autowire, por ejemplo:

  

Los ejemplos presentados en éste y otros tutoriales hacen uso de esta funcionalidad de enlazado automático; así se minimiza la cantidad de código declarativo en los archivos de configuración.



Servicio de Lógica de Negocio

Por último se define en el archivo de configuración services.xml una referencia a la clase que contendrá la lógica de negocio para esta aplicación. La declaración de esta clase es muy sencilla:



   <bean id="objectManager"
     class="single_table_simple_key_basic_spring.ObjectManagerImpl"/>

El servicio tiene un identificador "objectManager", y es implementado por la clase ObjectManagerImpl en el paquete actual. Esta clase hace referencia al servicio de Hibernate, por lo que se enlaza con dicho servicio a través del método setSessionFactory() definido como parte de la misma. El código relacionado con este método se mostrará más adelante cuando se presente la estructura de dicha clase.

El listado completo asociado al archivo services.xml puede consultarse en los archivos de código de este tutorial.



Interfase de Lógica de Negocio: ObjectManager.java

La interfase de lógica de negocio define los métodos que ofrecerá el componente de lógica de negocio en una aplicación empresarial. La definición de interfases para servicios es importante por diversos motivos; uno de los más importantes es que habilita la integración transparente de código de infraestructura ( código no escrito por el desarrollador: comunicación remota, transacciones, seguridad, etc. ) en infraestructuras como EJB 3, o Spring Framework con AOP ( siguientes tutoriales ). Un cliente de dicho servicio no dependerá de la implementación particular, sino de su interfase, como puede verse en la imagen anterior. De esta manera, se pueden agregar servicios o facilidades a dicha interacción, y también se puede cambiar la implementación del servicio de forma independiente al cliente. Esta separación entre interfase e implementación será aprovechada directamente por Spring Framework en las facilidades que se discutirán en el siguiente tutorial de esta serie.

El código de la interfase ObjectManager es el siguiente:

  package single_table_simple_key_basic_spring;

  /**
   * Interfase que define un servicio para el
   * manejo de facturas.
   */
  public interface ObjectManager
  {
     public int insertObject( FacturaDTO factura );

     public FacturaDTO getObject( int id );
     public FacturaDTO queryObject( int id );
     public int findMaxKey();
     public void updateObject( FacturaDTO factura );
     public void deleteObject( int id );

    }

Como puede verse, se define en este caso un método por cada tipo de funcionalidad que se tenía en la implementación planteada en el tutorial anterior.



Implementación de Lógica de Negocio: ObjectManagerImpl.java

La implementación de la interfase del servicio ObjectManager es definida por la clase ObjectManagerImpl. Se puede recordar que esta clase fue referenciada desde el archivo de configuración services.xml.

La estructura básica para esta clase es la siguiente:

   package single_table_simple_key_basic_spring;

  import org.hibernate.*;
  import org.hibernate.criterion.Expression;

  import java.util.List;

  /**
   * Implementación de un servicio basado en Hibernate.
   */
  public final class ObjectManagerImpl
  implements ObjectManager
  {

     /**
     * Hibernate session factory
     */
     private SessionFactory _sessionFactory;

     /////////////////////////////////////////////////////
     // Configuration methods

     /**
     * Session Factory setter
     */
     public void setSessionFactory
     ( SessionFactory sessionFactory )
       { _sessionFactory = sessionFactory; }

     ...
  }

Nótese que se implementa la interfase ObjectManager definida en el inciso anterior. También es importante mencionar que esta clase incluye una variable de ambiente de tipo org.hibernate.SessionFactory, y un método setter que establece el valor de la misma. Este es el método setter que es invocado automáticamente por Spring Framework durante la configuración de la clase.

En el caso de que la asociación entre clases no se realice, una excepción de tipo java.lang.NullPointerException será lanzada sobre una línea que haga uso de la variable de instancia _sessionFactory; por ejemplo:



  [ERROR]15/10/2007 15:49:19,296 (Test): Error:
  java.lang.NullPointerException
   at single_table_simple_key_basic_spring.ObjectManagerImpl.
   insertObject(ObjectManagerImpl.java:33)
   at single_table_simple_key_basic_spring.Test.execute(Test.java:38)
   at single_table_simple_key_basic_spring.Test.main(Test.java:18)

Esto implicaría que la referencia no fue inicializada, debido a que el método setSessionFactory() no fue invocado. Las razones para esto pueden ser los siguientes:

Posteriormente se define la implementación de los métodos en la clase de lógica de negocio. La implementación de estos métodos es equivalente a las que se definieron en el tutorial anterior, por ejemplo:



  public int insertObject( FacturaDTO factura )
   {
     Session session = _sessionFactory.openSession();

     try
       { insertObject( session, factura ); }
     finally
       { session.close(); }

     return factura.getId();
   }

   private void insertObject
    ( Session session, FacturaDTO factura )
   {
     boolean commit = false;
     Transaction transaction =
     session.beginTransaction();

     try
     {
       session.save( factura );

       commit = true;
     }
     finally
     {
       if( commit )
         transaction.commit();
       else
         transaction.rollback();
     }
   }

El listado completo de esta clase se puede consultar en el código completo del tutorial.



Clase de Prueba: Test.java

La clase de prueba lee la configuración de archivo de servicios y la carga como un objeto de tipo org.springframework.context.ApplicationContext. A partir de este objeto se puede obtener una referencia al servicio que implementa la interfase ObjectManager. El siguiente método de la clase Test carga la configuración de Spring Framework y obtiene el servicio con el nombre "objectManager".



   /**
   * Configurar los servicios de Spring Framework,
   * y obtener una referencia
   * al servicio de nombre "objectManager".
   */
   private ObjectManager getObjectManager()
   {
     ApplicationContext context =
     new ClassPathXmlApplicationContext
       ( "/single_table_simple_key_basic_spring/services.xml" );

       return ( ObjectManager )context.
         getBean( "objectManager" );
   }

La referencia regresada por este método se utiliza en el método execute() de la clase para invocar a cada uno de los métodos del servicio:



   /**
   * Método de ejecución de altas, bajas,
   * cambios y consultas.
   */
   private void execute() throws Exception
   {
      ObjectManager manager = getObjectManager();

      FacturaDTO factura1 = new FacturaDTO();

      factura1.setNombre( "factura1" );

      int id = manager.insertObject( factura1 );

      ...
   }


Conclusiones

Spring Framework permite organizar servicios basados en Hibernate de forma que se separe la lógica de negocio de otras capas en una aplicación java. Entre más compleja es una aplicación, más importante resulta esta separación. Cada uno de los aspectos de una aplicación se puede encapsular como un servicio, los cuales pueden ser agregados a una aplicación conforme van siendo requeridos, y pueden ser configurados para poder invocarse entre sí cuando se necesite.



Regresar






Copyright © ACSINET S.A. de C.V. 2007 http://www.acsinet-solutions.com

Derechos Reservados de este Material:
Atribución-No Comercial-Licenciamiento Recíproco 2.5 México

http://creativecommons.org/licenses/by-nc-sa/2.5/mx/

Eres libre de: a) copiar, distribuir y comunicar públicamente la obra, b) hacer obras derivadas. Bajo las condiciones siguientes: a) Atribución. Debes reconocer la autoría de la obra en los términos especificados por el propio autor o licenciante. b) No comercial. No puedes utilizar esta obra para fines comerciales. c) Licenciamiento Recíproco. Si alteras, transformas o creas una obra a partir de esta obra, solo podrás distribuir la obra resultante bajo una licencia igual a ésta.
Al reutilizar o distribuir la obra, tiene que dejar bien claro los términos de la licencia de esta obra. Alguna de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor. Nada en esta licencia menoscaba o restringe los derechos morales del autor.

Java and all Java-based trademarks and logos are trademarks of Sun Microsystems in the United States and/or other countries.

Other products and services are trademarks of their respective owners.




Copyright © ACSINET S.A. de C.V. 2000 - 2007 Derechos Reservados.


Java™ is a trademark of Sun Microsystems in the United States and/or other countries All other products and services are trademarks of their respective owners.