sábado, 29 de agosto de 2015

Multi-proyecto con gradle


Vamos analizar la necesidad de crear un proyecto, compuesto de varias sub-proyectos.
  • Nos permite realizar una separación lógica de nuestro proyecto. Un clasico es el bom( la B.d), service(La capa de negocio), webapp (La capa de vista).
  • Esta separación nos va evitar que desde un modulo no se puedan llamar a clases del otro ( Si los configuramos como capas separadas). Ejemplo, llamar desde un servicio a la request de un contenedor de servlet que pertenece a la capa de vista.
Cada sub-proyecto va a ser independiente entre si, aunque lo normal es que tenga rasgos comunes.
Ojo: Esto es un ejemplo de como darle utilidad a los subproyectos.
Vamos a ver en este articulo un ejemplo, simple de montar un proyecto raiz y 2 modulos (bom, service).
El ejemplo esta en la siguiente dirección https://github.com/blancoparis-tfc/MultiProyectoGradle.

Organización del fichero.


El proyecto raiz ( o padre), contendrá cada uno de los sub-proyectos (El nombre de la carpeta será el nombre de cada uno de los subProyectos:
Organización del proyecto:
  • build.gradle: El buil del proyecto raiz.
  • settings.gradle: Donde indicaremos los modulos.
  • bom(Directorio): Es el subProyecto  bom.
  • service(Directorio): Es el sub-proyecto service.

Vamos a crear un tarea que nos diga el nombre de cada proyecto, para lo cual nos vamos a apoyar en el buid.gradle del proyecto raiz, vamos a poner una configuración común, de la tarea hola.

Configurar los sub-proyectos.


Ahora le indicamos los sub-proyectos, que en nuestro caso van a ser bom y service.
Fichero (settings.gradle)
include 'bom','service'

Configurar todos los proyectos.


Aquí vamos a ver como establecer la configuración a todos los proyectos.
Fichero (build.gradle)
allprojects {
  task hola << { task -> println "Soy $task.project.name"}
}
$task.project.name—> Accede al nombre del proyecto
La etiqueta allprojects nos pone la configuración en todos los proytectos, incluido el proyecto raiz
Ahora le indicamos los sub-proyectos, que en nuestro caso van a ser bom y service.
Fichero (settings.gradle)
include 'bom','service'
Ahora ejecutamos la tarea hola, ejecutando
gradle –q hola
El resultado es:
>Soy MultiProyectoGradle
>Soy bom
>Soy service

Configurar solo los submodulos


Aqui vamos a ver como establecer la configuración solo para los submodulos, para lo cual vamos a utilizar subprojects.
allprojects {
  task hola << { task -> println "Soy $task.project.name"}
}
subprojects{
  hola << {println "   - Dependo de multi-proyecto"}
}
Ahora ejecutamos la tarea hola, ejecutando
gradle –q hola
El resultado es:
>Soy MultiProyectoGradle
>Soy bom
>    - Dependo de multi-proyecto
>Soy service
>    - Dependo de multi-proyecto

Establecer un comportamiento especifico a un proyecto.


Ahora vamos a verr, como podemos indicar una configuración especifica a un sub-proyecto en cuestión, para lo cual vamos a usar project('<nombre del sub-proyecto).<tarea>
allprojects {
  task hola << { task -> println "Soy $task.project.name"}
}
subprojects{
  hola << {println "   - Dependo de multi-proyecto"}
}
project(':bom').hola << {
  println "        - Soy el proyecto  donde va a estar el modelo de dominio "
}
Ahora ejecutamos la tarea hola, ejecutando
gradle –q hola
>Soy MultiProyectoGradle
>Soy bom
>    - Dependo de multi-proyecto
>          - Soy el proyecto donde va a estar el modelo de dominio
>Soy service
>    - Dependo de multi-proyecto

Nota: La configuración del modulo, lo idea es que este en su propio scripts del proyecto, pero asi vemos como podemos establecer la configuración desde el modulo raiz.

Configurar los sub-proyectos


Ahora vamos a ver como configurar cada sub-modulo, con su propio scripts de gradle

/bom/build.gradle
hola.doLast {
  println "             - Soy el proyecto donde vamos a definir el modelo de dominio";
}
/service/build.gradle
hola.doLast {
  println "             - Soy el proyecto que implementa la capa de negocio";
}
Ahora ejecutamos la tarea hola, ejecutando
gradle –q hola
>Soy MultiProyectoGradle
>Soy bom
>    - Dependo de multi-proyecto
>          - Soy el proyecto donde vamos a definir el modelo de dominio
>Soy service
>    - Dependo de multi-proyecto
>          - Soy el proyecto que implementa la capa de negocio

Reglas de ejecución:


En este caso sigue la regla de ejecutar todas las tareas que se encuentren en la jerarquía, hacia abaojo, por lo cual si entramos en el directorio bom, solo ejecutara la de bom. 

 bom/gradle –q hola

>Soy bom
>    - Dependo de multi-proyecto
>          - Soy el proyecto donde vamos a definir el modelo de dominio

El orden de ejecución es el alfabetico por defecto segun los encuentra  en la jerarquia, por lo cual en el nivel inferior, (ejecuta el multi-proyecto (nivel 0), luego bom y service.

Establecer el orden de ejecución:


Como hemos visto en los ejemplos anteriores, el orden de ejecución es el raiz y el resto por orden alfabetico.

Aunque en este caso no lo necesitamos vamos a ver como establecer un orden de ejecución entre los diferentes sub-proyectos.

Para este ejemplo crearemos la tarea inverso y en le proyecto bom, le vamos a indicar que esta tarea le precede el service, para lo cual utilizaremos el atributo dependsOn

/bom/build.gradle
hola.doLast {
  println "             - Soy el proyecto donde vamos a definir el modelo de dominio";
}
task inverso(dependsOn: ":service:inverso") <<{
  println " El ultimo es bom";
}
/service/build.gradle
hola.doLast {
  println "             - Soy el proyecto que implementa la capa de negocio";
}
task inverso <<{
  println " El primero es service";
}
gradle –q inverso
> El primero es service
> El ultimo es bom

Conclusión

En este articulo hemos visto con unos ejemplos tontos diferentes comportamientos, a la hora de trabajar con un proyecto en gradle por módulos, sin meter ninguna complicación mas. Para realizarlo me he basado en la documentación de gradle oficial, donde vienen muchas mas cosas en el siguiente enlace (https://docs.gradle.org/current/userguide/multi_project_builds.html#multiprojectUseSubprojects)

Nota: En la documentación oficial de gradle hay muchas mas cosas, como mecanismos para filtrar etc...

sábado, 15 de agosto de 2015

Como configurar un Map en JPA



Vamos a ver como configurar un mapa en JPA. Yo lo he necesitado por que tengo un enumerado asociado a un campo String (Que tiene un valor), en vez de crear un campo por cada valor, creo un map al que le paso el tipo y me devuelve el valor.  Y si mañana necesito otro campo con la misma lógica, solo tengo que registrarlo en el enumerado y la B.D no se toca.

Ejemplo practico: Tengo 3 dimensiones para la visibilidad o entidades del sistema a las que le asocio una opción en el enumerado, a las cuales le aplico un comodín para saber a que usuarios aplico la seguridad. Por lo cual la lógica en el código es común y nos interesa,  que el modelo de datos lo tengo en un mapa, para que sea escalable. Tocando solo el tipo enumerado.

Nota: Yo estas cosas las hago cuando ya hay 2 o mas casos, o este muy claro que va a crecer en los requisitos, es mas una excepción que otra cosa.





Como configurar el mapeo:


En este caso vamos a crear un mapeo de un tipo enumerado a un campo:

public enum TipoDimensiones{

Dim1,Dim2,Dim3

}

Añadimos el mapeo a la entidad en cuestión

@Entity
public class Entity(){

@id
private Long id;

@ElementCollection
@MapKeyColumn(name = "tipoDimension")
@JoinTable(name="Entity_visivilidad", joinColumns=@JoinColumn(name="id"))
@Column(name="valor")
private Map<Tipodimensiones,String> visivilidad;

Ahora voy a explicar cada una de las anotaciones. para que entendamos que estamos haciendo y no copiemos como loros :P.


  • ElementCollection: Indica que vamos a rellenar el collection a partir de una tabla.

  • MapKeyColumn: Indica la clave del mapa, usaremos MapKeyColumn cuando sea un tipo básico, en caso de ser una entidad usaremos MapKey (Ojo con esto). 

  • JoinTable: Indicamos la tabla donde se va a persistir el mapa (atributo name) y como que campo de la entidad tenemos que relacionarlo (joinColumn) en nuestro caso es la clave primaria.

  • Column: Le indicamos cual sera la columna que alimentara el valor.




Otra forma de configurar el mapeo


Esto hace lo mismo que lo anterior, pero si nos fijamos en la configuración anterior es un poco redundante el indicarle el id, cuando hibernate tiene la capacidad de saber cual es su id.

Por defecto hibernate utiliza el número de orden para saber cual es el enumerado ( Que corresponde por la posición en el enum que tenemos, si queremos por claridad o otras historias indicarle que el tipo enumerado de la clave guarde el string usaremos la anotación MapKeyEnumerated.

@ElementCollection
@CollectionTable(name="Entity_visivilidad")
@MapKeyEnumerated(EnumType.STRING)
@MapKeyColumn( name = "tipoDimension" )
@Column(name="valor")
private Map<Tipodimensiones,String> visivilidad;

martes, 4 de agosto de 2015

Spring 4.2 + Jpa 2.1 + Hibernate 4.3


Ahora vamos a montar un proyecto de spring, configurando Jpa 2.1 implementado por hibernate 4.3.
Como B.D vamos a usar una embebida (HSQLBD), para que el proyecto sea totalmente autónomo.
Para lo cual vamos a seguir los siguientes pasos:
  • Establecer las dependencias necesarias para un proyecto Spring.
  • Montar la configuración de spring.
  • Montar la configuración de spring JPA.
  • Crear el entorno de test.
  • Montar un test, para que podamos probar nuestra configuración.
El ejemplo de este articulo lo podéis descargar del siguiente enlace https://github.com/blancoparis-tfc/SpringHibernate

Dependencias:

Primero vamos a ver que librerías que vamos a necesitar:

Spring

Las dependencias relacionadas con la versión de spring, en nuestro ejemplo vamos a utilizar la versión 4.2.0.RELEASE.
  • Spring-context: Es la parte de spring encargada de levantar un contenedor de beans.
  • Spring-orm: Es el modulo de spring que utilizaremos para trabajar con los orm, que en nuestro caso sera hibernate como implementación de JPA 2.1.
  compile 'org.springframework:spring-context:4.2.0.RELEASE'
  compile 'org.springframework:spring-orm:4.2.0.RELEASE'

JPA2.1 + Hibernate 4.3


Las dependencias, relacionada con jpa2.1 + hibernate 4.3.10.FINAL.


  • Hibernate-core: Es la implementación de hibernate normal.
  • Hibernate-entitymanager: Es la integración de hibernate con JPA.
    compile 'org.hibernate:hibernate-core:4.3.10.Final'
    compile 'org.hibernate:hibernate-entitymanager:4.3.10.Final'

B.D


Las dependencias, relacionadas como la B.D, que en nuestro caso sera la libreria de HSQLBD mas una implementación de el datasource.


  • Hsqlbd: Es la bd embebida que vamos a utilizar.
  • commons-dbcp: Es una implementación para el datasource, en un proyecto de producición podemos utilizar mejor este c3p0.
    compile 'commons-dbcp:commons-dbcp:1.4'
    compile 'org.hsqldb:hsqldb:2.3.3'

Entorno de test:


La librería que vamos a utilizar, para realizar los test, que en nuestro caso será junit + la librería de test de spring.


  • Junit: Es la librería que vamos a utilizar para realizar los test unitarios.
  • Spring-test: Es la librería para poder lanzar spring, desde los entornos de test.
    testCompile group: 'junit', name: 'junit', version: '4.+'
    testCompile 'org.springframework:spring-test:4.2.0.RELEASE'



Crear el proyecto


Ahora que ya sabemos cuales son las dependencias que necesitamos, vamos a crear el fichero de gradle para poder montar nuestro proyecto.


  • Le tenemos que indicar, que es un proyecto de java y se va integrar con eclipse, para lo cual usaremos los pluging de (java,eclipse)
  • La indicamos con que maquina de java, vamos a trabajar utilizando la variable sourceCompatibility
  • Le indicamos que vamos a usar los repositorios de maven:
  • Ponemos las dependencias que necesitemos.
apply plugin: 'java'
apply plugin: 'eclipse'

sourceCompatibility = 1.8
version = '1.0'

repositories {
    mavenCentral()
}

dependencies {
  compile 'org.springframework:spring-context:4.2.0.RELEASE'
  compile 'org.springframework:spring-orm:4.2.0.RELEASE'
    compile 'commons-dbcp:commons-dbcp:1.4'
    compile 'org.hsqldb:hsqldb:2.3.3'
    compile 'org.hibernate:hibernate-core:4.3.10.Final'
    compile 'org.hibernate:hibernate-entitymanager:4.3.10.Final'
    
    testCompile group: 'junit', name: 'junit', version: '4.+'
    testCompile 'org.springframework:spring-test:4.2.0.RELEASE'
    
}

Configuración de Spring:


Aquí vamos a ver las configuraciones necesarias para spring, en mi caso las colocaremos en el paquete org.dbp.core.config.

Configuración básica de spring


Vamos a ver que necesitamos para poder configurar Spring, mínimo. En este caso la verdad, lo único que le tenemos que indicar a spring en que paquete van a estar los beans con los que vamos a trabajar en nuestro caso sera (org.dbp.service).


  • @Configuration: Le indicamos a spring, que es una clase para configurar spring.
  • @ComponentScan: Le indicamos los paquetes donde se encontraran los beans del contexto, que en nuestro caso “org.dbp.service”
package org.dbp.core.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@ComponentScan(basePackages = "org.dbp.service")
public class TestConfiguracion {

}

Configuración de JPA + Hibernate:


Para la configuración de la JPA, vamos a crear otra clase independiente donde estableceremos toda la configuración necesaria para la persistencia y que inyectaremos en la configuración general.

Para la persistencia tendremos que configurar los siguientes aspectos:


  • Habilitar las transaciones: @EnableTransactionManagement
  • El datasource: Configuraremos el datasource, que realizara la conexión con la B.D, con la que vamos a trabajar. (Si el día de mañana quisiéramos poner un pool de conexión aquí es donde se establecerá).
  • El entityManagerFactory: Configuramos la factoria de los entityManager de JPA, que es donde pondremos la configuración de JPA necesarias.
  • Las transaciones: Aquí configuramos las transacciones de jpa, con spring (Tengamos en cuenta que estas transacciones serán por orientación aspectos).
package org.dbp.core.config;

import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class TestJpaConfiguration {

 @Bean
 public DataSource getDataSource() {
     BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
  dataSource.setUrl("jdbc:hsqldb:mem:pruebas");
  dataSource.setUsername("sa");
  dataSource.setPassword("");
     return dataSource;
 }
 
 /**
  * Configuramos el entity manager en JPA. - Le indicamos el data source con
  * el que va a trabajar. - Le indicamos el paquete donde se encuentran las
  * clases. - Le pasamos el adaptador de JPA que en nuestro caso sera
  * hibernate. - Por otro lado le pasamos las propiedades.
  * 
  * @return
  */
 @Bean
 public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
  LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
  em.setDataSource(getDataSource());
  em.setPackagesToScan(new String[] { "org.dbp.bom" });
  JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
  em.setJpaVendorAdapter(vendorAdapter);
  em.setJpaProperties(propiedadesAdicionalesJpa());
  return em;
 }
 
 /**
  * Le indicamos el tipo de configuraci�n que nos interesa para hibernate -
  * Le indicmaos que cada vez que entremos borre y cree las B.D. - Le
  * indicamos que utilice el dialecto con HSQL.
  * 
  * Nota: este m�todo no es parte de la configuraci�n de spring.
  * 
  * @return
  */
 private Properties propiedadesAdicionalesJpa() {
  return new Properties() {
   {
    setProperty("hibernate.hbm2ddl.auto", "create");
    setProperty("hibernate.jdbc.batch_size", "20");
    setProperty("hibernate.show_sql", "true");
    setProperty("hibernate.dialect","org.hibernate.dialect.HSQLDialect");
    setProperty("hibernate.hbm2ddl.import_files","/META-INF/inicializar.sql");
   }
  };
 }

 /**
  * Configura las transaciones en JPA.
  * 
  * @return
  */
 @Bean
 public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ 
  JpaTransactionManager transactionManager = new JpaTransactionManager();
  transactionManager.setEntityManagerFactory(emf);
  return transactionManager;
 }
 
}

Ahora simplemente tenemos que indicarle en la configuración general que importe esta configuración con @Import({TestJpaConfiguration.class})




Creamos una entidad.


Para poder probar la configuración, vamos a crear una entidad, en nuestro caso sera el típico ejemplo de usuario con los campos (id,login y descripción).

package org.dbp.bom;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Usuario implements Serializable{

 @Id
 private Long id;
 
 private String login;
  
 private String nombre;

 public String getLogin() {
  return login;
 }

 public void setLogin(String login) {
  this.login = login;
 }

 public String getNombre() {
  return nombre;
 }

 public void setNombre(String nombre) {
  this.nombre = nombre;
 }

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

}

Test, para probar la configuración


Para poder probar esta configuración básica, la vamos a poner en el entorno de test,  y llamando a un test, consultamos un registro y insertamos otro y así podemos ver que funciona correctamente.
package org.dbp.core.config;

import static org.junit.Assert.*;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.dbp.bom.Usuario;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
 * Test para validar la configuración del JPA.
 * @author david
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@ContextConfiguration(classes = TestConfiguracion.class)
public class ConfigurationJpaTest {

 @PersistenceContext EntityManager em;
 
 @Test
 public void testRecuperarUnUsuario(){
  Usuario usuario=em.find(Usuario.class, 1L);
  assertEquals("El id del usuario 1",new Long(1L),usuario.getId());
  assertEquals("El login del usaurio","dblanco",usuario.getLogin());
  assertEquals("El nombre","david blanco paris",usuario.getNombre());
 }
 
 @Test
 public void testInsertarUsuario(){
  Usuario usuario=new Usuario();
  usuario.setId(2L);
  usuario.setLogin("perico");
  usuario.setNombre("Perico el de los palotes");
  em.persist(usuario);
 }
 
}

Conclusión


En este articulo hemos visto como configurar, un proyecto de spring + jpa. Para no complicar en exceso la configuración lo he montado en un entorno de test, pero ese mismo fichero se lleva a una configuración de spring y nos vale igual.