Posted on June 25, 2015
Active Record, Data Mapper, ORM, SQL Wrapper
Un ORM o Mapeo Relacional de Objetos es la capa responsable de analizar la información de las tablas de las bases de datos convirtiendo esos datos en objetos, así en un catálogo de usuarios cada registro de esa tabla se traduciría a instancias u objetos de tipo Usuario y los campos de la tabla serían los atributos de nuestra instancia (aunque no necesariamente un campo de una tabla se traduciría a un atributo de una instancia), el ORM se encarga además de hacer el proceso inverso ya sea cuando se guarda o actualiza la instancia, donde el objeto es persistido en la base de datos y para ello se necesita que sea analizado, generando las consultas necesarias para guardarlo en la tabla de la base de datos.
Un ORM puede ser implementado comúnmente utilizando el patrón Active Record o también el patrón Data Mapper.
Active Record
Definición de la wiki:
“Active record es un enfoque para aceso de datos en una base de datos. Una tabla de la base de dados o vista (view) está envuelta en una clase. Por lo tanto, una instancia de un objeto está ligada a un único registro (tupla) en la tabla. Después de crear y grabar un objeto, un nuevo registro es adicionado a la tabla. Cualquier objeto cargado obtiene su información a partir de la base de datos. Cuando un objeto es actualizado, un registro correspondiente en la tabla también es actualizado. Una clase de envoltura implementa los métodos de acceso (setter e getter) o propiedades para cada columna en la tabla o vista”
Un ejemplo de Active Record:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public class Usuario { private int id; private String nombre; private int edad; public boolean guardar() { ConexionDB db = new ConexionDB(); if (this.id == 0) { this.id = db.executeQuery("INSERT INTO usuarios (nombre, edad) VALUES ('" + nombre + "', '" + edad + "')"); } else { db.executeQuery("UPDATE usuarios SET nombre = '" + nombre + "', edad = '" + edad + "' WHERE id = " + this.id); } return true; } public static Usuario getUsuario(int id) { Usuario usuario = null; ConexionDB db = new ConexionDB(); ArrayList<Object> result = db.query("SELECT nombre, edad FROM usuarios WHERE id = '" + id + "'"); if (result != null) { usuario = new Usuario(); usuario.nombre = (String) result.get(0); usuario.edad = (int) result.get(1); } return usuario; } public void setNombre(String nombre) { this.nombre = nombre; } public String getNombre() { return this.nombre; } public void setEdad(int edad) { this.edad = edad; } public int getEdad() { return this.edad; } public boolean esMayorDeEdad() { return this.edad >= 18; } } |
Cabe mencionar que cuando es implementado este patrón de diseño, la entidad del dominio es responsable de persistirse en la base de datos, esto conlleva que dicha entidad tenga además de tener reglas de la capa de dominio (Método esMayorDeEdad), también quede acoplada a la capa de persistencia (Métodos guardar y obtener), esto trae una gran desventaja, si queremos en un futuro cambiar la forma de persistencia, que ahora en lugar de guardarse en una base de datos se guarde en archivos de texto plano (lo sé, es una locura), habría que hacer una refactorización de código inmensa, pero para proyectos donde la capa de persistencia se tiene por sentado que no cambiará es una forma buena de abstraer el cómo se gestionan las entidades de nuestros sistemas.
La forma en que utilizaríamos la clase:
1 2 3 4 5 6 7 8 |
Usuario usuario = new Usuario(); usuario.setNombre("Pedro"); usuario.setEdad(26); usuario.guardar(); Usuario usuario2 = Usuario.getUsuario(1); usuario2.setEdad(56); usuario2.guardar(); |
Data Mapper
Definición de la wiki:
“Data Mapper es una capa de acceso a los datos que realiza una transferencia bidireccional de la información entre la persistencia (frecuentemente una base de datos relacional) y los datos que se encuentran en memoria (La capa del dominio). El objetivo de este patrón es mantener la capa del domino y de la persistencia independientes”
Como vimos hace rato, Data Mapper ataca el punto débil de Active Record donde la capa de persistencia queda acoplada a la capa de dominio, su objetivo es mantener las dos capas independientes, es por ello que este patrón de diseño es ideal para sistemas donde las entidades del dominio son persistidas en diferentes medios, haciendo uso de este patrón no habría mayor problema si en un futuro se cambia la persistencia, que podría ser una base de datos a una fuente de información distinta.
Ejemplo de Implementación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class Usuario { private int id; private String nombre; private int edad; public void setNombre(String nombre) { this.nombre = nombre; } public String getNombre() { return this.nombre; } public void setEdad(int edad) { this.edad = edad; } public int getEdad() { return this.edad; } public boolean esMayorDeEdad() { return this.edad >= 18; } } |
Como vemos, nuestra entidad del dominio usuario ya no tiene conocimiento de la capa de persistencia.
Ahora vamos a implementar la capa de abstracción responsable de guardar las entidades del dominio desde cualquier fuente de información:
1 2 3 4 |
public interface Mapeador { public boolean guardar(Usuario usuario); public Usuario obtener(int id); } |
Implementamos el Data Mapper en caso de que nuestra persistencia sea una base de datos:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class BaseDeDatosMapeador implements Mapeador { public boolean guardar(Usuario usuario) { // Consultas a la base de datos return true; } public Usuario obtener(int id) { // Consultas a la base de datos Usuario usuario = null; return usuario; } } |
O si en un futuro cambia el sistema para que la fuente de datos sean documentos XMLs:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class XMLMapeador implements Mapeador { public boolean guardar(Usuario usuario) { // Guardar en archivo XML return true; } public Usuario obtener(int id) { // Leer archivo XML Usuario usuario = null; return usuario; } } |
Ahora nuestro sistema dependería de una abstracción, y el modo de persistir los datos puede ser cambiado de base de datos a documentos XML, o inclusive otras fuentes:
1 2 3 4 5 6 7 8 9 10 11 |
public class System { public void run(Mapeador mapeador) { Usuario usuario = new Usuario(); usuario.setNombre("Pedro"); usuario.setEdad(26); mapeador.guardar(usuario); } } |
Como conclusión, el patrón DataMapper las entidades como es la de Usuario son independientes, estas no conocen como persistirse en la base de datos, el que tiene la responsabilidad de hacer esto es otra clase que llamamos manejador (mapeador), la gran ventaja de esto es que nuestros objetos del dominio quedan totalmente desacoplados, el manejador se encarga de guardar las instancias en una base de datos, en un archivo de texto, en memoria, etc. Al final no importa dónde, el manejador hace lo necesario para que esa entidad sea persistida.
SQL Wrapper
Un SQL Wrapper es un envoltorio para generar consultas de una forma orientada a objetos, utilizando los métodos del wrapper o envoltorio se construyen las consultas dinámicamente, un ejemplo de cómo implementarlo vale más que mil palabras:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class SQLWrapper { private String sql; public SQLWrapper select(String fields) { this.sql = "SELECT " + fields; return this; } public SQLWrapper from(String from) { this.sql += " FROM " + from; return this; } public String toString() { return this.sql; } } |
La forma en que utilizaríamos la clase:
1 2 |
SQLWrapper sqlWrapper = new SQLWrapper(); String sql = sqlWrapper.select("edad").from("usuarios").toString(); |
Eso es todo por hoy, saludos 🙂