5 de noviembre de 2010

Patrón DAO (Data Access Object)

Es un patrón canonizado por SUN, el cual se encuentra dentro de la definición de los J2EE Patterns.

Pero... para empezar a hablar sobre este patrón, primero deberemos entender que son los Tansfer Object (TO)

Transfer Object

El origen de este patrón surge por el uso de EJB; Los EJB son una instancia de clase remota a la cual se accede por mecanismos RMI.

¿Que, qué es un EJB?

En otro momento entraré en detalles mas técnicos, por ahora lo explicaré muy someramente.

Basado en un patrón Domain Model, se identifican todas las entidades de negocio y se transforman a clases, de las cuales se generan instancias. Las clases contienen cierta lógica del negocio o dominio, es decir, son como objetos especializados en resolver algún tema particular.

Pero... ¿qué pasa si hay muchos sistemas que necesitan utilizar estas clases?. Es entonces cuando entran en escena los EJB. Lo que se le ocurrió a SUN, fue generar un contenedor de instancias de las entidades de negocio, una cajita donde siempre estuvieran disponibles los Objetos, lo cual también significa, que deberían estar aislados del resto de aplicaciones o por lo menos separados del resto.

De esta manera, cualquiera podría utilizar la lógica de negocio que esta almacenada en los EJB (si has utilizado Servlets te sonará similar).

¿Y cuál es el problema?...
Dado que los EJB existen dentro de la cajita, y que todo el mundo juega con ellos, nadie puede quedárselos por mucho tiempo, es decir ningún cliente debería tomar un EJB y regresarlo cuando termine de usarlo, porque que pasaría con los demás clientes, tendrían que esperar a que el EJB este libre y se encolarían las solicitudes como la fila de las tortillas.

Y como los EJB están basados en un patrón Domain Model, deben tener comportamiento y propiedades de lo que están modelando, así, supongamos que tenemos un EJB que modela la entidad Usuario, un cliente hace una petición hacia este para obtener los datos de un usuario en especifico, ¿qué pasaría?,  simple, se debería de cargar el EJB con los datos del usuario que se esta buscando. Pero... si el Objeto esta en el contenedor, para traerme sus atributos debería de hacer peticiones por cada atributo, algo similar a lo siguiente:

El primer problema es que, se hace muchas peticiones por la red hacia el EJB y si el EJB tiene muchos atributos, bueno sería un calvario obtener cada uno de ellos y la red se estresaría mucho.

El segundo problema es mas complejo, para esto veamos el siguiente diagrama.

El cliente1 lanza una solicitud para obtener los datos de un usuario, el EJB se carga con esta inforación y el cliente1 comienza la tarea de sacar la información, pero si antes de que termine llega un cliente2 y lanza otra solicitud para obtener datos de otro usuario, cuando el cliente1 saque el siguiente dato ¿Los datos de quien estaría viendo? claro los de la segunda petición, esto es un problema de concurrencia.

Y como solucionar este problema: CHAN, CHAN, CHAN, CHAAAANN, es cuando aparecen en escena los Transfer Object.

La idea es guardar toda la información con la que debería de cargarse el EJB en un objeto independiente, un objeto que solo contenga los atributos de la entidad Usuario, y devolver este objeto al cliente, así dejaría en paz al EJB mas rápido, la red se estresaría menos y no habría el problema de concurrencia.

Así solo se hace un llamado al EJB, no se estresa la red y el EJB queda libre para seguir recibiendo peticiones.

¿Entonces todo este rollo fue para decir que un Transfer Object, es un objeto que solo sirve como un mecanismo para transportar información de un punto a otro y que no tiene comportamiento?

La respuesta es SI, solo es un objeto que sirve para transportar información al igual que si se hiciera mediante una estructura XML o JSON.

De regreso al patrón DAO

Ahora que ya sabemos que es un Transfer Object, podremos entender mejor lo que sigue.

Dentro de las aplicaciones empresariales, el principal objetivo es almacenar información de alguna manera y en algún lugar, este lugar puede ser una base de datos relacional, una base de datos orientada a objetos archivos planos, o alguna otra forma que se les ocurra.

Así que para cada mecanismo de almacenamiento, existen múltiples formas de acceder a la fuente física de los datos y esto es algo que al negocio no le debería de importar, por tal motivo se debe de abstraer todo este conocimiento y envolverlo en una capa.

El patrón DAO muestra una forma de envolver ese conocimiento y evitar que el negocio se manche las manos con esta ardua tarea, lo que significa que la explotación de los datos se hará mediante objetos DAO. La información que se obtiene de las fuentes de datos es convertida o encapsulada a un objeto de tipo TransferObject, o alguna colección de estos.


Así el negocio solo recibe por medio de objetos la información que necesita y no le interesa realemente que mecanismo se utilizó para obtener la información.

En realidad el funcionamiento es muy similar a lo que sucede con los EJB, ya que el Transfer Object sigue siendo quien transporta la información hacia la capa que solicita la información.

Manejando múltiples DataSources: Patrón Factory Method

Cuando por asares del destino nuestra aplicación debe hacer uso de muchas fuentes de datos, se traduce en tener varios mecanismos para obtener la informació y tendriamos un pequeño problema ¿Cómo administro todos los mecanismos?

Imaginemos que tenemos que administrar 3 fuentes de datos, una en archivos TXT otra en XLS(Excel) y la última con una BD Relacional, y las 3 tienen una estructura de datos muy similar para resguardar los datos de un usuario.

Si la capa de negocio no debe enterarse de como se estrae la información ¿qué hacer?

El patron  Factory Method nos dice como poder solucionar este problema


A la capa cliente no le interesa como leen y escriben los datos, el solo sabe que debe recibir un TO que modela un Usuario y tambien debe saber de donde quiere sacar la información. Así el cliente hace esta elección por medio del FactoryDao

Si esto lo completamos con un Tranfer Object y un patrón Singleton, tenemos ahora algo mas robusto y flexible para manejar multiples fuentes de datos.

A trabajar se a dicho

Pongamos un poco de código para reforzar esto:

Vamos a retomar el ejemplo de los diagramas, modelando una estructura de datos de Usuarios

Clase Factrory:
public abstract class FactoryDao {

    public static final int TXT_FACTORY = 1;
    public static final int MYSQL_FACTORY = 2;

    public abstract UsuarioDao getUsuarioDao();

    public static FactoryDao getFactory(int claveFactory){
        switch(claveFactory){
            case TXT_FACTORY:
                return new TxtFactoryDao();
            case MYSQL_FACTORY:
                return new MySqlFactoryDao();
            default:
                throw new IllegalArgumentException();
        }
    }
}

La clase factroy se encarga generar los objetos FactoryDao para cada una de las fuentes de datos, esto lo hace mediante el método estático getFactory(int) y obliga a quien lo implemente a sobreescribir el método getUsuarioDao().

Interfaz UsuarioDao
public interface UsuarioDao {

    UsuarioTO buscarUsuario(String nombre);
    void insertarUsuario(UsuarioTO usuario);
    void modificarUsuario(UsuarioTO usuario);

}

Se define cuales son los métodos que tendrán todos los DAO's que quieran ser un UsuarioDao

Clase MySQLFactoryDao
public class MySqlFactoryDao extends FactoryDao {

    @Override
    public UsuarioDao getUsuarioDao() {
        return new UsuarioMySqlFactoryDao();
    }

}

Extiende la clase FactoryDao e implementa el metodo getUsuarioDao, generando un objeto que consuma el data source que necesita, en esta caso una base de datos MySQL

Clase TxtFactoryDao
public class TxtFactoryDao extends FactoryDao{

    @Override
    public UsuarioDao getUsuarioDao() {
        return new UsuarioTXTFactoryDao();
    }
}

Ocurre lo mismo que la clase MySQLFactoryDao salvo que ahora se genera un objeto de UsuarioTXTFactoryDao()

Clase UsuarioMySQLFactoryDao
public class UsuarioMySqlFactoryDao implements UsuarioDao {
   
    private ConnectionDao cn;

    public UsuarioMySqlFactoryDao() {
        cn = ConnectionDao.getInstance();
    }

    public UsuarioTO buscarUsuario(String nombre) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void insertarUsuario(UsuarioTO usuario) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void modificarUsuario(UsuarioTO usuario) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}


Las operaciones que esta clase hace son dirigidas hacia una sola fuente de datos (Una Base de Datos en MySQL)

Clase UsuarioTXTFactoryDao
public class UsuarioTXTFactoryDao implements UsuarioDao {

    public UsuarioTXTFactoryDao() {
       
    }

    public UsuarioTO buscarUsuario(String nombre) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void insertarUsuario(UsuarioTO usuario) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void modificarUsuario(UsuarioTO usuario) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}


Ahora las operaciones son sobre un archivo de texto

Clase UsuarioTO
public class UsuarioTO {

    private String nombre;
    private String apellido;
    private int edad;



    //Métodos set y get
}

Es la clase mediante la cual se transformarán los datos de las diferentes fuentes de datos

Clase Cliente
public class Cliente {

    private FactoryDao txtFactory

        FactoryDao.getFactory(FactoryDao.TXT_FACTORY);

    private UsuarioDao usuarioDao = txtFactory.getUsuarioDao();

    public UsuarioTO buscarUsuario(String nombre){
        return usuarioDao.buscarUsuario(nombre);
    }

    public void insertarUsuario(String nombre, String apellido, int edad){
        UsuarioTO usuario = new UsuarioTO();
        usuario.setNombre(nombre);
        usuario.setApellido(apellido);
        usuario.setEdad(edad);
        usuarioDao.insertarUsuario(usuario);
    }

}

Ahora la clase Cliente decide que fuente de datos usar por medio del método getFactory(), de el cual se puede obtener un objeto que implemente la interfaz UsuarioDao. El resto de las operaciones sobre UsuarioDao son muy transparentes para la clase cliente, ya que no se entera de como hace su proceso para extraer y guardar información


Así concluye este articulo sobre el Patrón DAO y como extender las fuentes de datos con el patrón Factory Method

Los invito a dejar sus comentarios, dudas y por su puesto sugerencias, estamos en este proceso de mejorar así que cualquier aportación siempre es bien recibida

Un Saludo

20 comentarios:

  1. Me agrado como lo explicaste y más porque este patrón es uno de los que más me gustan, y bueno para dar una pista de por donde ir creo que es bueno

    Sigue así esta chido y pues creo que con el tiempo vas mejorando, porque empiezas a atacar temas muy específicos y así son fáciles de leer, espero no repitas lo de los patrones de negocio pues fue mucha información para una sola entrega.

    Vas chido suerte y no te detengas

    ResponderEliminar
  2. Gracias por el comentario Os, si creo que fue mucha información de un solo golpe, que te puedo decir, creo que me emocione.

    Gracias por el comentario.

    Un Saludo.

    ResponderEliminar
  3. Hola tengo una duda a mi me piden usar ese patron dao pero ademas me piden usar pojo en los servicios y no tengo ni idea de como es eso xD, sera posible que alguna parte del codigo de arriba haga el papel de servicio? espero tu respuesta saludos!

    ResponderEliminar
  4. Hola amigo, excelente articulo, me gusto tu explicacion me quedo mas claro, habia tratado de entenderlo pero nomas me cofundia, solo una pregunta disculpa mi ignorancia tan grande pero en la Clase UsuarioMySQLFactoryDao es donde tiene que ir toda la configuracion de la conexion a la base y los qwerys de CRUD?. saludos

    ResponderEliminar
    Respuestas
    1. Este comentario ha sido eliminado por el autor.

      Eliminar
    2. Hola Miguel. La respuesta es sí. En esta clase es donde se hace la implementación específica para el acceso a los datos utilizando MySQL. Por lo tanto debe ir allí el código que se encarga de conectar a la base de datos y los querys a lanzar.

      Espero te sirva.

      Eliminar
  5. Qué tal Alverik. Excelente post. Me ayudó mucho.

    Saludos.

    ResponderEliminar
  6. Hola Alverik. Dentro de poco terminaré mi pagina y pienso publicar unos cuantos post de utilidad y calidad como lo es este tu crees que puedo utilizarlo? pondre tu direccion de referencia a fuente saludos.

    ResponderEliminar
  7. Muy Buena Explicacion.

    ResponderEliminar
  8. Que pena pero alguno me puede pasar el ejemplo ya hecho en Java, entendi la explicacion pero apenas estoy iniciando el mundo de java y me esta generando errores por todos lados.

    ResponderEliminar
  9. muchas gracias por la explicacion, la verdad que no entendia muy bien este patron pero con tu ayuda ya me quedo claro. saludos

    ResponderEliminar
  10. Busque y busque explicaciones de este Patrón y no entendí hasta que llegué aquí!..

    Muchas gracias... has salvado mi vida!!

    ResponderEliminar
  11. Y como que es de ConnectionDao? y para que sirve, etc.

    ResponderEliminar
  12. Ay, Gregoria, como me gustaban tus croquetas.

    ResponderEliminar
  13. Explicación clara, sencilla y didactica. Sigue asi Alverik. Muchas gracias.

    ResponderEliminar
  14. Muy bien explicado, el único inconveniente que veo es la distribución de los componentes, por ejemplo si solo quisiera usar el DAO de txt tendría que llevarme también los demás DAO dentro de mi biblioteca. Una solución sería usar reflexión pero no lo recomiendo porque el costo es mucho en tiempo de ejecución

    ResponderEliminar
  15. La clase "ConnectionDao" quedo sin definir aparentemente.. verdad?

    ResponderEliminar
  16. Gracias muy buena la explicación :)

    ResponderEliminar