viernes, 19 de julio de 2013

Spring Data en español



El acceso a bases de datos es una de las tareas más comunes en el desarrollo de software, al principio esta tarea se realizaba simplemente haciendo uso de JDBC (Java Database Connectivity) y poco a poco fue evolucionando utilizando patrones de diseño como el DAO (Data Access Object) y a través de frameworks y API's tales como Hibernate y JPA, estos ORM (Object Relational Mapping) nos han ayudado a reducir muchas de las tareas que se hacían antes con JDBC.

Spring Data es un módulo de Spring que vamos a utilizar sobre JPA para hacer las tareas de acceso a base de datos aún más sencillas, las ventajas se notarán a simple vista ya que nosotros simplemente crearemos métodos en una interfaz y Spring Data se encargará de hacer las implementaciones por nosotros de tal modo que si nombramos un método como "findByName" Spring Data creará la implementación necesaria para buscar en la base de datos a través del nombre sin que nosotros creemos ni una sola conexión ni procesemos ningún resultado.

Spring Data es como el aire acondicionado, una vez que lo utilicen no podrán vivir sin el.

Lo primero que vamos a hacer es crear un proyecto dentro de nuestro entorno de desarrollo.



Seleccionaremos la opción "Maven->Java Application" y hacemos click en "Next".


Seleccionamos un nombre del proyecto en mi caso será "EjemploSpringData", un group id para mi caso será "mx.com.geeksjava" y hacemos click en "Finish".


Con esto crearemos un proyecto vacío con la estructura de un proyecto Maven.


Una vez creado el proyecto lo que vamos a hacer es definir cuales son las dependencias(bibliotecas) de nuestro proyecto, para hacer esto vamos a modificar nuestro archivo de configuración "pom.xml", dentro de este archivo vamos a agregar las siguientes dependencias dentro de la etiqueta <dependencies></dependencies>.

 <dependencies>  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>4.7</version>  
       <scope>test</scope>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-orm</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-test</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>commons-dbcp</groupId>  
       <artifactId>commons-dbcp</artifactId>  
       <version>1.4</version>  
     </dependency>  
     <dependency>  
       <groupId>mysql</groupId>  
       <artifactId>mysql-connector-java</artifactId>  
       <version>5.1.17</version>  
     </dependency>  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-core</artifactId>  
       <version>3.6.10.Final</version>  
     </dependency>  
     <dependency>  
       <groupId>javassist</groupId>  
       <artifactId>javassist</artifactId>  
       <version>3.8.0.GA</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework.data</groupId>  
       <artifactId>spring-data-jpa</artifactId>  
       <version>1.3.2.RELEASE</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-aspects</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>joda-time</groupId>  
       <artifactId>joda-time</artifactId>  
       <version>2.0</version>  
     </dependency>  
     <dependency>  
       <groupId>joda-time</groupId>  
       <artifactId>joda-time-hibernate</artifactId>  
       <version>1.3</version>  
     </dependency>  
     <dependency>  
       <groupId>com.google.guava</groupId>  
       <artifactId>guava</artifactId>  
       <version>r05</version>  
     </dependency>  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-entitymanager</artifactId>  
       <version>3.6.10.Final</version>  
     </dependency>  
   </dependencies>  


Ahora damos click secundario en nuestro proyecto y seleccionamos la opción "Build with Dependencies" con esto se van a descargar las dependencias necesarias para nuestro proyecto, si es la primera vez que utilizan Maven esto puede tardar algo de tiempo.





Ya que tenemos todas las dependencias necesarias para poder realizar nuestro proyecto vamos a crear  nuestra base de datos en mysql.


 create database springdata;  
 use springdata;  
 CREATE TABLE Persona(  
      id_persona INTEGER PRIMARY KEY AUTO_INCREMENT,  
      nombre VARCHAR(30) NOT NULL,  
      ap_pat VARCHAR(30),  
      ap_mat VARCHAR(30),  
      edad INTEGER,  
      sexo CHAR(1)  
 );  


Ya creada la base de datos lo que vamos a crear un paquete dentro de "Source Packages" haremos click secundario en dicho paquete y seleccionaremos "New ->Other", en la pantalla que nos aparecerá seleccionaremos "Persistence->Entity Classes from Database" y hacemos click en "Next".



En la opción "Database Connection" seleccionaremos "New Database Connection", en la opción "Driver" vamos a elegir donde dice "MySQL (Connector/J driver) " y hacemos click en "Next".


Colocaremos la información sobre la base de datos a la cual nos vamos a conectar, hacemos click en el botón "Test Connection" y si nos arroja un "Connection Succeeded" hacemos click en "Finish".


Seleccionamos la tabla personas en la lista oprimimos el botón "Add" y hacemos click en siguiente.


Dejamos las opciones por defecto en la siguiente pantalla hacemos click en "Next", en la opción "Collection Type" seleccionaremos "java.util.List" y hacemos click en "Finish".


Como pueden ver se creó una clase Java que representa a la tabla en la base de datos que seleccionamos, es importante mencionar que hasta ahorita no hemos hecho nada de Spring Data, esta es configuración y la creación de una entity de JPA.Ahora borremos las cosas que no vamos a necesitar como las @NamedQueries de tal modo que quede de la siguiente forma:

 package mx.com.geeksjava.ejemplospringdata.model;  
 import java.io.Serializable;  
 import javax.persistence.Basic;  
 import javax.persistence.Column;  
 import javax.persistence.Entity;  
 import javax.persistence.GeneratedValue;  
 import javax.persistence.GenerationType;  
 import javax.persistence.Id;  
 import javax.persistence.Table;  
 import javax.xml.bind.annotation.XmlRootElement;  
 @Entity  
 @Table(name = "persona")  
 @XmlRootElement  
 public class Persona implements Serializable {  
   private static final long serialVersionUID = 1L;  
   @Id  
   @GeneratedValue(strategy = GenerationType.IDENTITY)  
   @Basic(optional = false)  
   @Column(name = "id_persona")  
   private Integer idPersona;  
   @Basic(optional = false)  
   @Column(name = "nombre")  
   private String nombre;  
   @Column(name = "ap_pat")  
   private String apPat;  
   @Column(name = "ap_mat")  
   private String apMat;  
   @Column(name = "edad")  
   private Integer edad;  
   @Column(name = "sexo")  
   private Character sexo;  
   public Persona() {  
   }  
   public Persona(Integer idPersona) {  
     this.idPersona = idPersona;  
   }  
   public Persona(Integer idPersona, String nombre) {  
     this.idPersona = idPersona;  
     this.nombre = nombre;  
   }  
   public Integer getIdPersona() {  
     return idPersona;  
   }  
   public void setIdPersona(Integer idPersona) {  
     this.idPersona = idPersona;  
   }  
   public String getNombre() {  
     return nombre;  
   }  
   public void setNombre(String nombre) {  
     this.nombre = nombre;  
   }  
   public String getApPat() {  
     return apPat;  
   }  
   public void setApPat(String apPat) {  
     this.apPat = apPat;  
   }  
   public String getApMat() {  
     return apMat;  
   }  
   public void setApMat(String apMat) {  
     this.apMat = apMat;  
   }  
   public Integer getEdad() {  
     return edad;  
   }  
   public void setEdad(Integer edad) {  
     this.edad = edad;  
   }  
   public Character getSexo() {  
     return sexo;  
   }  
   public void setSexo(Character sexo) {  
     this.sexo = sexo;  
   }  
   @Override  
   public int hashCode() {  
     int hash = 0;  
     hash += (idPersona != null ? idPersona.hashCode() : 0);  
     return hash;  
   }  
   @Override  
   public boolean equals(Object object) {  
     // TODO: Warning - this method won't work in the case the id fields are not set  
     if (!(object instanceof Persona)) {  
       return false;  
     }  
     Persona other = (Persona) object;  
     if ((this.idPersona == null && other.idPersona != null) || (this.idPersona != null && !this.idPersona.equals(other.idPersona))) {  
       return false;  
     }  
     return true;  
   }  
   @Override  
   public String toString() {  
     return "mx.com.geeksjava.ejemplospringdata.model.Persona[ idPersona=" + idPersona + " ]";  
   }  
 }  




Ahora vamos a crear un archivo properties de donde se cargará la información de la base de datos para esto, nos vamos a la carpeta "Other Sources" hacemos click secundario "New->Properties File", colocamos el nombre "conexion" y hacemos click en "Finish".



conexion.properties

 database.url=jdbc:mysql://localhost:3306/springdata
 database.driver=com.mysql.jdbc.Driver  
 database.username=root  
 database.password=hola123  

Dentro de "Source Packages" vamos a crear un paquete llamado "mx.com.geeksjava.ejemplospringdata.repository" y dentro de la carpeta "Other Sources" vamos a crear un archivo xml al que nombraremos "jpaApplicationContext.xml".



 <?xml version="1.0" encoding="UTF-8"?>  
 <beans xmlns="http://www.springframework.org/schema/beans"  
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
      xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"  
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
           http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd  
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">  
      <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">  
           <property name="driverClassName" value="${database.driver}"></property>  
           <property name="url" value="${database.url}"></property>  
           <property name="username" value="${database.username}"></property>  
           <property name="password" value="${database.password}"></property>  
      </bean>  
      <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
           <property name="entityManagerFactory" ref="emf"></property>  
      </bean>  
      <context:property-placeholder location="classpath:conexion.properties" />  
      <tx:annotation-driven transaction-manager="transactionManager" />  
      <bean id="emf"  
           class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
           <property name="dataSource" ref="dataSource" />  
           <property name="jpaVendorAdapter">  
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />  
           </property>  
           <property name="packagesToScan"  
                value="mx.com.geeksjava.ejemplospringdata.model" />  
           <property name="jpaProperties">  
                <props>  
                     <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>  
                     <prop key="hibernate.max_fetch_depth">3</prop>  
                     <prop key="hibernate.jdbc.fetch_size">50</prop>  
                     <prop key="hibernate.jdbc.batch_size">10</prop>  
                     <prop key="hibernate.show_sql">true</prop>  
                </props>  
           </property>  
      </bean>  
      <jpa:repositories base-package="mx.com.geeksjava.ejemplospringdata.repository"  
           entity-manager-factory-ref="emf" transaction-manager-ref="transactionManager"></jpa:repositories>  
      <context:annotation-config></context:annotation-config>  
      <context:component-scan base-package="mx.com.geeksjava.ejemplospringdata"></context:component-scan>  
 </beans>  


Con esto vamos a configurar Spring Data, lo que resta ya simplemente es utilizarlo y probar su funcionamiento, para esto vamos a crear una interfaz llamada "PersonaRepository" dentro del paquete "mx.com.geeksjava.ejemplospringdata.repository".


Ahora lo que vamos a modificarla de tal modo que quede de la siguiente forma.

 package mx.com.geeksjava.ejemplospringdata.repository;  
 import mx.com.geeksjava.ejemplospringdata.model.Persona;  
 import org.springframework.data.repository.CrudRepository;  
 import org.springframework.stereotype.Repository;  
 @Repository("personaRepository")  
 public interface PersonaRepository extends CrudRepository<Persona,Integer>{  
   public Persona findByNombre(String nombre);  
 }  


Como se puede ver la interfaz PersonaRepository hereda de CrudRepository y le pasamos 2 genéricos, el primero representa la entidad con la cual se va a trabajar y el segundo el tipo de dato de la llave primaria. Dentro de la interfaz definimos el método "findByNombre(String nombre)" con esto aunque nosotros no lo hagamos Spring Data realizará la implementación para buscar en la base de datos el nombre que le mandemos y nos devolverá un objeto de tipo persona con el resultado.

Para probarlo utilizaremos el Framework JUnit, para esto nos iremos a la carpeta "Test Packages"  y crearemos el paquete "mx.com.geeksjava.ejemplospringdata" dentro de ese paquete hacemos la siguiente clase.




 package mx.com.geeksjava.ejemplospringdata;  
 import mx.com.geeksjava.ejemplospringdata.model.Persona;  
 import mx.com.geeksjava.ejemplospringdata.repository.PersonaRepository;  
 import org.junit.Test;  
 import org.junit.runner.RunWith;  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.test.context.ContextConfiguration;  
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
 import static org.junit.Assert.*;  
 @ContextConfiguration("/jpaApplicationContext.xml")  
 @RunWith(SpringJUnit4ClassRunner.class)  
 public class PersonaRepositoryTest {  
   @Autowired  
   private PersonaRepository personaRepository;  
   @Test  
   public void pruebaBusqueda() {  
     personaRepository.deleteAll();  
     Persona p = new Persona();  
     p.setApMat("Sánchez");  
     p.setApPat("López");  
     p.setNombre("Juan");  
     p.setSexo('m');  
     p.setEdad(12);  
     personaRepository.save(p);      
     Persona q = personaRepository.findByNombre("Juan");  
     assertEquals(p.getNombre(), q.getNombre());  
   }  
   @Test  
   public void pruebaBorrar() {  
     Persona p = new Persona();  
     p.setNombre("Mario Orlando Hardy ");  
     p.setApPat("Hamlet Brenno");  
     p.setApMat("Benedetti Farrugia");  
     p.setSexo('m');  
     p.setEdad(93);  
     Persona q = new Persona();  
     q.setNombre("Jaime");  
     q.setApPat("Sabines");  
     q.setApMat("Gutiérrez");  
     q.setSexo('m');  
     q.setEdad(72);  
     personaRepository.save(p);      
     personaRepository.save(q);  
     personaRepository.deleteAll();  
     Iterable<Persona> personas = personaRepository.findAll();  
     assertEquals(false, personas.iterator().hasNext());  
   }  
 }  


Este es un test de JUnit el cual tiene dos pruebas la primera valida que se guarde un elemento y la soegunda que se borre todo el contenido de la tabla, como se puede ver personaRepository es un objeto de la interfaz que creamos "PersonaRepository" y nosotros nunca implementamos los métodos "save", "deleteAll" ni "findByNombre". La implementación de estos métodos fue hecha por Spring Data.



En este ejemplo solo creamos una tabla con su entidad y con su repository pero en una aplicación normal tendremos muchas tablas con sus entidades y sus repositories, como pueden ver el código que ahorramos fue muchísimo y fue muy limpio ya que no hicimos conexiones a la base de datos, no utilizamos nada de sql o jpql, no realizamos las implementaciones para crear, leer, actualizar ni borrar nada en la base de datos, todo lo hizo Spring Data por nosotros.

Si comparamos el desarrollo de software con el Fútbol esto sería como un "Programa Bonito".


Espero les haya quedado claro la forma en la que podemos utilizar Spring Data y las ventajas que nos da en el desarrollo de software.

El código completo EjemploSpringData.rar.



--------->>>>>>>Autor: Alejandro Agapito Bautista
--------->>>>>>>>>>>>Oracle Certified Java Programer
--------->>>>>>>>>>>>Oracle Certified Web Components Developer
--------->>>>>>>>>>>>IBM Certified Academic Assosiate DB2.
--------->>>>>>>Twitter: @raidentrance
--------->>>>>>>Contacto:raidentrance@gmail.com

1 comentario: