Arquitectura Simple con Java Parte 1 - DAO
En este inicio de proyecto donde iremos subiendo y evolucionando nuestro proyecto de Java, hasta llegar a algo robusto.
Nuestro primer punto de partida será la capa de DAO (Data Access Object), esta capa no es solamente conexión a una base de datos, sino podemos traer datos de otros sistemas legados o proveedores externos a nuestro sistema.
Para entender correctamente lo que aquí se hace debe tener nociones básicas de spring, beans,contexto y java.
[caption id="attachment_1097" align="alignnone" width="798"]
Arquitectura base de un sistema[/caption]
Está es una conceptualización de una arquitectura básica para lo que vamos a desarrollar, evolutivamente.
Entorno:
En el proyecto:
Aquí el vídeo mostrando el funcionamiento de esta capa:
[youtube https://www.youtube.com/watch?v=F9xHahGfqFg&w=560&h=315&start=960&autoplay=1]
Para esta parte cree 3 proyectos con maven para tener la siguiente estructura
Cada uno de los proyectos los ejecute con el siguiente comandó de maven:

Y a mano cree el siguiente pom, quedando así la estructura:

El contenido del pom.xml es:

Este pom será el padre y me ayudara a compilar todos los proyectos evitando que tenga que hacerlo uno por uno.
si ejecutamos un

Ya que los hemos creado hay que importarlos a eclipse (o el IDE de su preferencia). En la opción de importar de Eclipse, es seleccionar Maven > Existing Maven Projects

Después seleccionamos la carpeta dónde esta el pom principal y lo veremos de la siguiente manera (cuando descarguen el código de github deberán importarlo de esta manera).

Ya que tenemos esto explicaré a continuación parte del código.
Aun que los proyectos están "divididos" en proyectos estan juntos por el contexto, ¿Qué quiere decir esto?
los une el nombre del paquete
Y aparte en el contexto de spring en la siguiente etiqueta de xml en el archivo que esta src/test/resources/test-context.xml

Esto hace un escaneo de componentes apartir de ahí
¿Por qué creé el contexto de spring en
Precisamente para probar solamente esta parte de DAO que hemos creado, porque en la aplicación final el contexto será cargado en la API Rest que crearemos más adelante.
Para modificar los datos de la conexión es a través del archivo properties que esta en
Las consultas de SQL están divididas en dos archivos

Lo anterior es usando las notaciones de MyBatis que con ellas de manera simple hacemos consultas a la base de datos.
Y la capa de DAO sería
Esta es la definición de contrato, que es muy útil cuando trabajamos con más gente podemos subir el código hasta aquí y decirle que el va a usar esta interfaz mientras desarrollamos la implementación
Aquí va la implementación, la cual es muy simple. A continuación solo explicaré un método y los demás son muy similares.
[code language="java"]
public UsuarioDaoDto crear(final UsuarioDaoDto usuario) {
final UsuarioSqlDto usuarioSqlDto = UsuarioDtoMaper.INSTANCE.convertir(usuario);
final Integer resultado = usuarioSqlMaper.insert(usuarioSqlDto);
if ((resultado != null) && BooleanUtils.toBoolean(resultado)) {
return UsuarioDtoMaper.INSTANCE.convertir(usuarioSqlDto);
}
return null;
}
[/code]
En nuestro proyecto son estas lineas:
[code language="java"]final UsuarioSqlDto usuarioSqlDto = UsuarioDtoMaper.INSTANCE.convertir(usuario);[/code]
[code language="java"]return UsuarioDtoMaper.INSTANCE.convertir(usuarioSqlDto);[/code]
Para ello podemos apoyarnos de herramientas como MapStruct, que nos facilitara hacer este "trabajo" de mapeo creando solo una interfaz que en este proyecto esta:
mx.rafex.blog.back.utilidades.mapeos.usuarios.UsuarioDtoMaper
[code language="java"]
package mx.rafex.blog.back.utilidades.mapeos.usuarios;
import java.util.List;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import mx.rafex.blog.back.dtos.daos.sql.usuarios.UsuarioSqlDto;
import mx.rafex.blog.back.dtos.daos.usuarios.UsuarioDaoDto;
@Mapper
public interface UsuarioDtoMaper {
UsuarioDtoMaper INSTANCE = Mappers.getMapper(UsuarioDtoMaper.class);
@Mappings({ @Mapping(source = "id", target = "identificador") })
UsuarioDaoDto convertir(UsuarioSqlDto usuario);
@InheritInverseConfiguration
UsuarioSqlDto convertir(UsuarioDaoDto usuario);
List convertirListaUsuarioSqlDto(List usuario);
List convertirListaUsuarioDaoDto(List usuario);
}
[/code]
Y lo que hará es que al compilar creara la implementación que la podemos ver a continuación:

Y así es como nos ahorramos el "trabajo" del mapeo.
Entonces en resumen nuestro método recibe el DTO de la capa de DAO, lo transforma en un DTO de la capa de MyBatis para ejecutar el método de "insert" que este regresa el identificador de la fila insertada en la base de datos y luego mapeamos el DTO de MyBatis al DTO de DAO y listo.
Ahora la parte de los TEST, pues es simple al usar el arquetipo de Maven QuickStart genero una estructura similar a esto:
[code]project
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- $package
| `-- App.java
`-- test
`-- java
| `-- $package
`-- AppTest.java[/code]
en los test hay que usar unas notaciones "especiales" por el tema de que estamos usando spring y requerimos levantar un contexto para que funcione correctamente.
[code language="java"]@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })[/code]
La segunda notación es dónde especificamos de dónde va a cargar el contexto, aparte hay que extender de la clase AbstractJUnit4SpringContextTests para poder traer el applicationContext y poder usar nuestros beans hechos en spring.
[code language="java"]
@Test
public void validarQueElBeanNoSeaNulo() {
final IUsuarioDao bean = (IUsuarioDao) applicationContext.getBean("usuarioDao");
Assert.assertNotNull("Error al crear el contexto y el bean es nulo", bean);
}
@Test
public void obtenerTodosLosUsuarios() {
final IUsuarioDao bean = (IUsuarioDao) applicationContext.getBean("usuarioDao");
final List listaUsuarios = bean.obtenerTodos();
Assert.assertNotNull(listaUsuarios);
Assert.assertFalse("Hubo un error al traer los usuarios", listaUsuarios.isEmpty());
for (final UsuarioDaoDto usuario : listaUsuarios) {
System.out.println(usuario.toJson());
}
}
[/code]
La anotación @Test será lo que ocuparemos de JUnit y la clase Assert para poder validar los resultados. Como pueden ver los test son para probar únicamente esta capa de DAO, validando que realmente funciona la interacción con la base de datos así como la transformación de objetos (mapeos) y la poca lógica que tiene esta capa.
El tiempo estimado de todo esto para un Jr serían 6 hrs., para un Sr serían 2 hrs.
Espero que esta primera parte haya quedado explicada y si tienen dudas con gusto las resolveré en los comentarios o correo electrónico. Y recuerden continuara...
Pueden ver que compila y los test funcionan en Travis-ci
Segunda parte aquí
Descargas:
Nuestro primer punto de partida será la capa de DAO (Data Access Object), esta capa no es solamente conexión a una base de datos, sino podemos traer datos de otros sistemas legados o proveedores externos a nuestro sistema.
Para entender correctamente lo que aquí se hace debe tener nociones básicas de spring, beans,contexto y java.
[caption id="attachment_1097" align="alignnone" width="798"]

Está es una conceptualización de una arquitectura básica para lo que vamos a desarrollar, evolutivamente.
Entorno:
- MacBook Pro 13''
- Core i5
- 8 GB RAM
- Eclipse (Oxygen.2 Release 4.7.2)
- Lombok v1.16.20 "Dancing Elephant"
- Maven 3.5.0
- JDK8
En el proyecto:
- Spring Framework : 4.3.14.RELEASE
- spring-core
- spring-context
- spring-tx
- spring-test
- MapStruct: 1.2.0.Final
- Lombok: 1.16.20
- JUnit: 4.12
Aquí el vídeo mostrando el funcionamiento de esta capa:
[youtube https://www.youtube.com/watch?v=F9xHahGfqFg&w=560&h=315&start=960&autoplay=1]
Para esta parte cree 3 proyectos con maven para tener la siguiente estructura
- Dtos (data transfer object - Objeto de Transferencia de Datos)
- Utilidades
- UsuariosDao
Cada uno de los proyectos los ejecute con el siguiente comandó de maven:
mvn archetype:generate -DgroupId=mx.rafex.blog.back.dtos \
-DartifactId=Dtos \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
mvn archetype:generate -DgroupId=mx.rafex.blog.back.utils \
-DartifactId=Utils \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
mvn archetype:generate -DgroupId=mx.rafex.blog.back.daos \
-DartifactId=UsersDaos \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false

Y a mano cree el siguiente pom, quedando así la estructura:
- Dtos (Carpeta)
- Utilidades (Carpeta)
- UsuariosDao (Carpeta)
- pom.xml (Archivo)

El contenido del pom.xml es:

Este pom será el padre y me ayudara a compilar todos los proyectos evitando que tenga que hacerlo uno por uno.
si ejecutamos un
mvn clean install
en la terminal verán algo así:
Ya que los hemos creado hay que importarlos a eclipse (o el IDE de su preferencia). En la opción de importar de Eclipse, es seleccionar Maven > Existing Maven Projects

Después seleccionamos la carpeta dónde esta el pom principal y lo veremos de la siguiente manera (cuando descarguen el código de github deberán importarlo de esta manera).

Ya que tenemos esto explicaré a continuación parte del código.
Aun que los proyectos están "divididos" en proyectos estan juntos por el contexto, ¿Qué quiere decir esto?
los une el nombre del paquete
mx.rafex.blog.back
Y aparte en el contexto de spring en la siguiente etiqueta de xml en el archivo que esta src/test/resources/test-context.xml

Esto hace un escaneo de componentes apartir de ahí
¿Por qué creé el contexto de spring en
src/test/resources
?Precisamente para probar solamente esta parte de DAO que hemos creado, porque en la aplicación final el contexto será cargado en la API Rest que crearemos más adelante.
Para modificar los datos de la conexión es a través del archivo properties que esta en
src/test/test.properties
Las consultas de SQL están divididas en dos archivos
mx.rafex.blog.back.daos.sql.mapers.UsuarioConsultas
<< Aquí solo es texto de la consultamx.rafex.blog.back.daos.sql.mapers.UsuarioSqlMaper
<< La interfaz que usará MyBatis para lanzar las consultas (select,insert,update,etc...)
Lo anterior es usando las notaciones de MyBatis que con ellas de manera simple hacemos consultas a la base de datos.
Y la capa de DAO sería
mx.rafex.blog.back.daos.usuarios.IUsuarioDao
Esta es la definición de contrato, que es muy útil cuando trabajamos con más gente podemos subir el código hasta aquí y decirle que el va a usar esta interfaz mientras desarrollamos la implementación
mx.rafex.blog.back.daos.usuarios.impl.UsuarioDao
Aquí va la implementación, la cual es muy simple. A continuación solo explicaré un método y los demás son muy similares.
[code language="java"]
public UsuarioDaoDto crear(final UsuarioDaoDto usuario) {
final UsuarioSqlDto usuarioSqlDto = UsuarioDtoMaper.INSTANCE.convertir(usuario);
final Integer resultado = usuarioSqlMaper.insert(usuarioSqlDto);
if ((resultado != null) && BooleanUtils.toBoolean(resultado)) {
return UsuarioDtoMaper.INSTANCE.convertir(usuarioSqlDto);
}
return null;
}
[/code]
El principio es el siguiente entre capas deben usar sus DTO's por la naturaleza propia que pueden llegar a tener, aun que hay lugares que les encanta subir y bajar el mismo DTO entre todas las "capas". Esa practica no la profeso, pero se puede, a mi experiencia es mejor usar los DTO's de la capa que corresponde por si requerimos que tenga un "comportamiento" particular a la capa entonces al usar DTO's entre capas implica "más trabajo" porque hay que crear clases de conversión o mapeo y que muchos las ven como que no agregan valor y son muy laboriosas.
En nuestro proyecto son estas lineas:
[code language="java"]final UsuarioSqlDto usuarioSqlDto = UsuarioDtoMaper.INSTANCE.convertir(usuario);[/code]
[code language="java"]return UsuarioDtoMaper.INSTANCE.convertir(usuarioSqlDto);[/code]
Para ello podemos apoyarnos de herramientas como MapStruct, que nos facilitara hacer este "trabajo" de mapeo creando solo una interfaz que en este proyecto esta:
mx.rafex.blog.back.utilidades.mapeos.usuarios.UsuarioDtoMaper
[code language="java"]
package mx.rafex.blog.back.utilidades.mapeos.usuarios;
import java.util.List;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import mx.rafex.blog.back.dtos.daos.sql.usuarios.UsuarioSqlDto;
import mx.rafex.blog.back.dtos.daos.usuarios.UsuarioDaoDto;
@Mapper
public interface UsuarioDtoMaper {
UsuarioDtoMaper INSTANCE = Mappers.getMapper(UsuarioDtoMaper.class);
@Mappings({ @Mapping(source = "id", target = "identificador") })
UsuarioDaoDto convertir(UsuarioSqlDto usuario);
@InheritInverseConfiguration
UsuarioSqlDto convertir(UsuarioDaoDto usuario);
List convertirListaUsuarioSqlDto(List usuario);
List convertirListaUsuarioDaoDto(List usuario);
}
[/code]
Y lo que hará es que al compilar creara la implementación que la podemos ver a continuación:

Y así es como nos ahorramos el "trabajo" del mapeo.
Entonces en resumen nuestro método recibe el DTO de la capa de DAO, lo transforma en un DTO de la capa de MyBatis para ejecutar el método de "insert" que este regresa el identificador de la fila insertada en la base de datos y luego mapeamos el DTO de MyBatis al DTO de DAO y listo.
Ahora la parte de los TEST, pues es simple al usar el arquetipo de Maven QuickStart genero una estructura similar a esto:
[code]project
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- $package
| `-- App.java
`-- test
`-- java
| `-- $package
`-- AppTest.java[/code]
en los test hay que usar unas notaciones "especiales" por el tema de que estamos usando spring y requerimos levantar un contexto para que funcione correctamente.
[code language="java"]@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })[/code]
La segunda notación es dónde especificamos de dónde va a cargar el contexto, aparte hay que extender de la clase AbstractJUnit4SpringContextTests para poder traer el applicationContext y poder usar nuestros beans hechos en spring.
[code language="java"]
@Test
public void validarQueElBeanNoSeaNulo() {
final IUsuarioDao bean = (IUsuarioDao) applicationContext.getBean("usuarioDao");
Assert.assertNotNull("Error al crear el contexto y el bean es nulo", bean);
}
@Test
public void obtenerTodosLosUsuarios() {
final IUsuarioDao bean = (IUsuarioDao) applicationContext.getBean("usuarioDao");
final List listaUsuarios = bean.obtenerTodos();
Assert.assertNotNull(listaUsuarios);
Assert.assertFalse("Hubo un error al traer los usuarios", listaUsuarios.isEmpty());
for (final UsuarioDaoDto usuario : listaUsuarios) {
System.out.println(usuario.toJson());
}
}
[/code]
La anotación @Test será lo que ocuparemos de JUnit y la clase Assert para poder validar los resultados. Como pueden ver los test son para probar únicamente esta capa de DAO, validando que realmente funciona la interacción con la base de datos así como la transformación de objetos (mapeos) y la poca lógica que tiene esta capa.
El tiempo estimado de todo esto para un Jr serían 6 hrs., para un Sr serían 2 hrs.
Espero que esta primera parte haya quedado explicada y si tienen dudas con gusto las resolveré en los comentarios o correo electrónico. Y recuerden continuara...
Pueden ver que compila y los test funcionan en Travis-ci
Segunda parte aquí
Descargas:
Comentarios
Error creating bean with name 'sqlSessionFactory' defined in class path resource [app-config.xml]: Cannot resolve reference to bean 'dataSourceMysql' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceMysql': Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: El nombre MySqlDSEjemplo1 no este asociado a este contexto.
Porr favor quisera saber como has configurado tu conexiónen esta capa.Saludos