19 de noviembre de 2010

ORM (Object Relation Mapping) Parte 1

¿Que es ORM?

Al surgir la programación orientada a objetos, todo pareció ser alegría hasta el momento de tener que enfrentarse con un dilema, "Bases de datos"

Cuando la POO comenzó a tener un gran empuje, las personas comenzaron a preguntarse porque no tener también una Base de Datos Orientada a Objetos (BDOO), así que algunos comenzaron a investigar al respecto, pero... cual fue el problema.

Las Bases de Datos Relacionales dominaban el mercado, había compañías muy sólidas respaldaban sus productos, el lenjuage SQL ya era un estándar que todos conocían y las BDOO apenas estaban en plena concepción, dado que lo mas importante para las compañías es su información (Datos), no iban a apostar por una nueva herramienta en pleno desarrollo y dejar a un lado las ya maduras Bases de Datos Relacionales. Así que el paradigma de las BDOO se fue rezagando hasta quedar prácticamente en el olvido actualmente.

En el mundo de caramelo de la POO, trabajar con una BDOO sería exelente por no decir magnifico, ya que la explotación de los datos sería mediante Objetos, es decir, se lanzaría una petición a la BD y esta en lugar de regresar un conjunto de datos como lo hace una BDR, regresaria un conjunto de objetos, representando una Entidad muy particular, así todo se manejaría a nivel de Objetos y no de datos.

Tristemente eso no paso así, lo cual presento un nuevo problema para la POO ya que no podía interactuar naturalmente con una base de datos relacional, al menos no a nivel de Objetos.

Así que para solventar este problema se creo el concepto de ORM ( algo así como "Objeto Relación Mapeo" en español). La idea es simple obtener un conjunto de datos de una base de datos relacional y convertirla en Objetos por algún mecanismo.

Para ello se idearon algunos patrones y en la actualidad existen varios frameworks para los distintos lenguajes OO que nos facilitan mucho la chamba.

Sin embargo, considero que primero hay que entender los patrones en los que se basaron estos frameworks, para tener una perspectiva mas amplia a la hora de elegir uno u otro y no solo hacer caso de la popularidad.

Así que sin mas preámbulo aquí vamos:

Nota: En los fragmentos de código, se hace uso de la clase ConnectionDao creada en el artículo Patrón Singleton

Active Record

Este patrón fue creado con la intención de solventar la deficiencia del Domain Model con las bases de datos relacionales y parece hacer lo que sería mas lógico, es decir, ofrece un mecanismo para que cada objeto de la capa de dominio modele únicamente y exclusivamente un registro de una tabla en particular y que el objeto tenga métodos para poder modificar dicho registro.



Así cada uno de los objetos Alumno, estaría representando un registro de la tabla ALUMNO y el Cliente trabajaría con cada uno de estos objetos de forma transparente.

Para obtener un mejor resultado podríamos utilizar métodos estáticos para poder obtener cada uno de los objetos Alumno, de esta manera los objetos son un tanto mas transparentes a la hora de obtener información de la base de datos

Veamos el siguiente diagrama de secuencia


Por medio del método estático buscarAlumno, se obtiene un objeto de tipo Alumno, con la información cargada de un alumno en particular y se podrían hacer operaciones con este alumno, como modificar o borrar. De esta misma manera al crear un nuevo objeto alumno estaríamos tratando por debajo con un solo registro

Un Ejemplo

Vamos a pone un pequeño ejemplo de como se vería el código hecho en java para este patrón.

TABLA Alumno
  CREATE TABLE ALUMNO (
    ID INT ID PRIMARY KEY,
    NOMBRE VARCHAR(50),
    EDAD INT
  );


Ahora a crear el clase que modelará dicha tabla

Alumno.java

  public class Alumno {

    private int id;
    private String nombre;
    private int edad;

    //set's y get's
    ...
 }


Agregamos los métodos estáticos por los cuales obtendremos los datos
  public class Alumno {
    // Propiedades
    ...

    private final static String FIND_BY_ID="select * from alumno where id=?";
    public static Alumno buscarPorId(int id) throws SQLException{
        ConnectionDao cn = ConnectionDao.getInstance();
        PreparedStatement prepared = cn.getPreparedStatement(FIND_BY_ID);
        prepared.setInt(1, id);
        return loadData(prepared.executeQuery());
    }

    private static Alumno loadData(ResultSet rs) throws SQLException{
        Alumno alumno = new Alumno();
        alumno.id = rs.getInt(1);
        alumno.nombre = rs.getString(2);
        alumno.edad = rs.getInt(3);
        return alumno;
    }
}

Al final solo faltaría agregar los métodos para poder modificar la información del registro que estamos modelando

public class Alumno {
  // Propiedades
  ...

  // Métodos estáticos
  ...

  private final static String UPDATE "update alumno set nombre = ?, " +
                                       "edad = ? where id = ?";
  public void actualizar() throws SQLException{
    ConnectionDao cn = ConnectionDao.getInstance();
    PreparedStatement prepared = cn.getPreparedStatement(UPDATE);
    prepared.setString(1, nombre);
    prepared.setInt(2, edad);
    prepared.setInt(3, id);
    prepared.executeUpdate();
  }

  private final static String INSERT "insert into alumno values(?,?,?)";
  public void insertar() throws SQLException{
    ConnectionDao cn = ConnectionDao.getInstance();
    PreparedStatement prepared = cn.getPreparedStatement(INSERT);
    prepared.setInt(1, id);
    prepared.setString(2, nombre);
    prepared.setInt(3, edad);
    prepared.execute();
  }
}


De tal forma que ahora la clase puede modificar, insertar o borrar exactamente 1 registro de la tabla Alumno

Cuando alguna clase haga uso de este se vería así

public class Cliente {

    public void modicarAlumno(int id,String nombre,int grupo){
        try {
            Alumno alumno = Alumno.buscarPorId(id);
            alumno.setNombre(nombre);
            alumno.setEdad(id);
            alumno.actualizar();
        } catch (Exception ex) {
            ...
        }
    }

    public void nuevoAlumno(int id, String nombre, int grupo){
        try {
            Alumno alumno = new Alumno();
            alumno.setId(id);
            alumno.setNombre(nombre);
            alumno.setEdad(id);
            alumno.insertar();
        } catch (SQLException ex) {
            ...
        }
    }
}

Las operaciones que hacen los objetos alumno son muy transparentes

Pros y Contras

- Lo bueno
  • Es un patón bastante simple de utilizar
  • Resuelve de una manera muy simple la deficiencia de BDOO
  • Es una forma muy elegante de encapsular el manejo de las BD
  • Si el modelo de domino es muy similar a la estructura de la BD, el funcionamiento es muy transparente
  • Si la estructura de una tabla cambia, el impacto solo es en la clase que modela dicha tabla

- Lo malo
  • Si las operaciones con la BD son complejas, tenderá a ser mas difícil de dar mantenimiento
  • El patrón no impide generar 2 objetos del mismo registro, lo cual quiere decir que si se ejecutan una modificación en ambos, el segundo sobre escribirá al primero, generando un problema de concurrencia 
- La conclusión

Es realmente simple integrar el patrón cuando la base de datos es prácticamente idéntica al diagrama de clases, pero si las diferencias entre ambos son grandes, es muy posible que te meta en mas problemas que de los que te saque, también se debe tomar en cuenta la concurrencia sobre los registros por generar dos objetos del mismo registro.

Hasta aquí llega la primera parte de los ORM.

Cualquier duda o comentario, no duden en hacermelo saber, ya que solo así aprenderemos los unos de los otros

Un Saludo

2 comentarios: