Jakarta NoSQL con MongoDB perspectivas
Hace unos años conversaba con un gran amigo Otavio Santa, acerca de bases de datos NoSQL.
Es admirable el trabajo que lleva adelante Otavio y otros desarrolladores en la especificación Jakarta NoSQL.
La definición oficial en el sitio de Jakarta:
"Jakarta NoSQL is a Java API standard that streamlines the integration of Java applications with NoSQL databases. It defines a set of APIs and provides a standard implementation for most NoSQL databases. The goal is to create the specification in Jakarta EE to help Jakarta EE developers create enterprise-grade applications using Java® and NoSQL technologies. It helps them create scalable applications while maintaining low coupling with the underlying NoSQL technology."
Si leemos un poco la documentación oficial nos encontraremos que se define mediante dos capas:
- Communication Layer: Contiene 4 módulos uno para cada tipo de bases de datos NoSQL. Es como JDBC para bases de datos relacionales.
- Mapping Layer: Esta capa utiliza CDI, Bean Validaton, anotaciones para ofrecer sencillez a los desarrolladores, Se puede comparar con JPA.
Su uso es muy sencillo, definimos entidades de manera simple, y mediante interfaces usando el patrón Repository tenemos una gran funcionalidad con poco esfuerzo.
Hasta el momento de escribir este articulo es importante mencionar que no se soporta agregaciones por lo cual no podemos utilizar referencias entre documentos, tenemos que definir un modelo de datos optimizado generalmente usando documentos embebidos para el caso de MongoDB.
Su flexibilidad e integración con Microprofile es muy sencilla. Solamente agregamos al archivo micrroprofile-config, los atributos que indican el tipo de base de datos y los parámetros de conexión.
Definimos nuestras entidades: Persona.java , Pais.java, Dirección.java, que representa la colección Persona y Pais en MongoDB. Lo hacemos de esta manera a modo de ejemplo:
Ejemplo A:
Para el ejemplo definimos Pais como un entity pero le agregamos el atributo @Id
Crear la interface Repository para Persona
public interface PersonaRepository extends Repository<Persona, String> {
Stream<Persona> findAll();
Page<Persona> findAll(Pagination pagination);
//Busca en el documento embebido
@Query("select * from Persona where pais.code = @code")
Stream<Persona> findByPaisIn(@Param("code") String code);
Stream<Persona> findByName(String name);
}
Ahora creamos la interface Repository para Pais
Observe que estamos creando una consulta buscando en el documento embebido mediante pais.code
Crear el controlller utilizando Eclipse Microprofile
@ApplicationScoped
@Path("pais")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PaisController {
private static final Supplier<WebApplicationException> NOT_FOUND =
() -> new WebApplicationException(Response.Status.NOT_FOUND);
@Inject
private PaisRepository repository;
@GET
public List<Pais> findAll() {
return repository.findAll()
.collect(toList());
}
@GET
@Path("/{id}")
public Pais findById(@PathParam("id") String id) {
return repository.findById(id).orElseThrow(NOT_FOUND);
}
@GET
@Path("search/{name}")
public List<Pais> findByYounger(@PathParam("name") String name) {
return repository.findByName(name)
.collect(toList());
}
@POST
public void save(Pais hero) {
repository.save(hero);
}
@PUT
@Path("/{id}")
public void update(@PathParam("id") String id, Pais hero) {
repository.save(hero);
}
@Path("/{id}")
@DELETE
public void delete(@PathParam("id") String name) {
repository.deleteById(name);
}
}
Ahora crearmos el controller PersonaController.java
@ApplicationScoped
@Path("persona")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PersonaController {
private static final Supplier<WebApplicationException> NOT_FOUND =
() -> new WebApplicationException(Response.Status.NOT_FOUND);
@Inject
private PersonaRepository repository;
@GET
public List<Persona> findAll() {
return repository.findAll()
.collect(toList());
}
@GET
@Path("/{id}")
public Persona findById(@PathParam("id") String id) {
return repository.findById(id).orElseThrow(NOT_FOUND);
}
@GET
@Path("searchpais/{code}")
public List<Persona> findByPaisIn(@PathParam("code") String code) {
return repository.findByPaisIn(code)
.collect(toList());
}
@POST
public void save(Persona hero) {
repository.save(hero);
}
@PUT
@Path("/{id}")
public void update(@PathParam("id") String id, Persona hero) {
repository.save(hero);
}
@Path("/{id}")
@DELETE
public void delete(@PathParam("id") String name) {
repository.deleteById(name);
}
}
Insertamos algunos documentos en la colección Pais
curl -H "Content-Type: application/json" -X POST -d '{ "code": "pa","name": "Panama"}' http://localhost:8080/microjakartanosql/api/pais/
curl -H "Content-Type: application/json" -X POST -d '{ "code": "eu","name": "Estados Unidos"}' http://localhost:8080/microjakartanosql/api/pais/
curl -H "Content-Type: application/json" -X POST -d '{ "code": "cu","name": "Cuba"}' http://localhost:8080/microjakartanosql/api/pais/
Insertamos documentos en la colección Persona
curl -H "Content-Type: application/json" -X POST -d '{"id": "7","name": "Aristides","pais":{ "code": "pa","name": "Panama"},"direccion":{ "street": "Calle 1","city": "Los Santos"}}' http://localhost:8080/microjakartanosql/api/persona/
curl -H "Content-Type: application/json" -X POST -d '{"id": "8","name": "Ana","pais":{ "code": "cu","name": "Cuba"},"direccion":{ "street": "La habana","city": "La habana"}}' http://localhost:8080/microjakartanosql/api/persona/
Podemos observar una situación interesante
La colección Persona en MongoDB no genera el documento embebido pais y el campo code, es reemplazado automáticamente por _id.
. Esto ocurre porque en nuestra entidad Pais, definimos el atributo
y queríamos que se almacenar el atributo code.
Que hicimos mal o en que estamos fallando, en primer lugar si le ocurre esta situación indica que no hemos analizado bien el modelo de datos que queremos usar. Habia comentado inicialmente que Jakarta NoSQL no soporta aun documentos referenciados. Por la tanto podemos observar que dirección se almaceno correctamente los atributos street, city.
En la definición de la entidad
No contamos con ninguna columna definida con el atributo @Id
Por lo tanto si eliminamos la anotación @Id de la entidad Pais.java
Y eliminamos los documentos de nuestra colección Persona en MongoDB y detenemos nuesto microservicio y lo volvemos a ejecutar.
curl -H "Content-Type: application/json" -X POST -d '{"id": "7","name": "Aristides","pais":{ "code": "pa","name": "Panama"},"direccion":{ "street": "Calle 1","city": "Los Santos"}}' http://localhost:8080/microjakartanosql/api/persona/
curl -H "Content-Type: application/json" -X POST -d '{"id": "8","name": "Ana","pais":{ "code": "cu","name": "Cuba"},"direccion":{ "street": "La habana","city": "La habana"}}' http://localhost:8080/microjakartanosql/api/persona/
Podemos observar que se almacena correctamente
Como eliminamos el @Id del entity Pais, no podremos insertar los registros
curl -H "Content-Type: application/json" -X POST -d '{"id": "7","name": "Aristides","pais":{ "code": "pa","name": "Panama"},"direccion":{ "street": "Calle 1","city": "Los Santos"}}' http://localhost:8080/microjakartanosql/api/persona/
curl -H "Content-Type: application/json" -X POST -d '{"id": "8","name": "Ana","pais":{ "code": "cu","name": "Cuba"},"direccion":{ "street": "La habana","city": "La habana"}}' http://localhost:8080/microjakartanosql/api/persona/
ya que nos enviara el error
Indicando que solo usaremos una colección Persona, y tanto Direccion como Pais, serán embebidos
Por la tanto son los cambios importantes que debemos considerar al trabajar con bases de datos NoSQL y con las diversas implementaciones.
Muchas veces tenemos que redefinir adecuadamente para aprovechar el potencial
Comments