01.19.08

¿Herencia en BBDD?

Posted in Bases de Datos, Programación at 11:35 am by Miguel

El otro día en un curso de Business Intelligence surgió un caso como este.

Imaginaos que tenemos una base de datos donde almacenamos información al respecto de proveedores, clientes, trabajadores, representantes y convocatorias.

En un momento dado se quieren enviar invitaciones a los diferentes proveedores, clientes… y se quiere tener constancia a nivel de base de datos de quiénes han sido invitados a cada convocatoria.

No sé por qué me vino a la cabeza inmediatamente una solución orientada a objetos, así que intenté plasmar la idea a nivel de modelo relacional, surgiendo algo similar a esto:

Herencia en BBDD

Como véis, la herencia se ve a simple vista. La clase invitado es una clase abstracta de la cual heredan tanto el representante, como el proveedor, cliente y trabajador. Para resolver la herencia a nivel de modelo relacional las cuatro tablas inferiores guardan foreign key contra invitado. Además podréis ver que en la tabla LINEA_CONVOCATORIA se tiene referencia a la tabla INVITADO, con esto hacemos algo similar a pasar a un método por parámetro la clase abstracta, para que luego en tiempo de compilación, y según el objeto que se pase (y que obligatoriamente tendrá que heredar de invitado), se actue en consecuencia.

¿Problemas de esto? Mantener a base de datos puede ser como mínimo extraño. El campo CO_INVITADO de todas las tablas debería admitir NULL (por cierto hay una errata en el diagrama al respecto ya que como podéis ver es NOT NULL, y por eso la cardinalidad hacia INVITADO aparece siempre como 1, cuando debería ser 0..1). Además, fijaros en el detalle de que la tabla INVITADO tiene un campo llamado TABLA. Allí almacenaríamos el nombre de la tabla donde realmente está el invitado… jejeje, aquí es donde se pierde la historia y me deja de gustar el tema, pero es la única manera que se me ocurrió resolver de dónde sacar de dónde viene el invitado cuando empezamos a buscar a partir de la tabla LINEA_CONVOCATORIA, es decir, lo que sería en tiempo de compilación que se te pasara un objeto INVITADO y resolver dependiendo de si el objeto es un proveedor, cliente…

Esto solución resulta como mínimo curiosa. ¿Se os ocurre cómo mejorarla? ¿Proponéis alternativas?

En el curso se dijo de resolver esto a nivel de Triggers, y dejarse de historias. A mi sinceramente, no me gustan los Triggers, pero eso no quiere decir que no sean la mejor forma de resolver este tipo de problemas.

Saludos!

Miguel.

Rating 3.00 out of 5
[?]

10.23.07

Patrón de Diseño: Data Access Object (DAO) + Data Transfer Object (DTO)

Posted in Bases de Datos, Patrones, Programación at 10:14 pm by Miguel

Y como no, sigo dándole vueltas a mejorar el acceso y la protección de los datos.

Antes de empezar a haceros un breve resumen de lo que es un DAO y un DTO os dejo primero unos links donde podréis verlos en marcha de forma mucho más concreta.

http://en.wikipedia.org/wiki/Data_Access_Object
http://en.wikipedia.org/wiki/Data_Transfer_Object
http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html

El chiste del DAO es que nos provee de una forma totalmente abstracta de acceso a datos. Es decir, puedo acceder a mi modelo de datos, pero únicamente a través del conjunto de métodos que me brinda un DAO. Si tengo un DAO para coches, por ejemplo CocheDAO, en él deberé tener definido por ejemplo un método Count() que me retornará el total de coches que tengo en mi base de datos. No sé cómo está implementado, ni qué base de datos utiliza (si es que accede a una base de datos), lo único que sé es que resuelve mi problema.

Por lo tanto, desde la parte del Controlador de mi aplicación, puedo acceder a mi Modelo utilizando el patrón de diseño que ofrece DAO, de forma totalmente transparente. Esto por ejemplo nos llevaría a pensar que nuestro CocheDAO podría contener otros métodos, como el método Delete (que borraría un coche de la base de datos), Update (que actualizaría los datos de un coche), GetCochesByMarca (que a partir de una marcha retornaría los coches de la marca que hay en la base de datos), etc.

Todo esto suena en principio a lo mismo que sería tener una clase llamada Coche llena de métodos static a los cuales llamaría cuando fuera necesario. Esto, pues claro, resuelve el problema de tener todo el acceso a la tabla Coche centralizado, olé, bien, somos ordenados, pero DAO va mucho más allá. ¿Cómo resolveríais el problema de devolver un listado de coches a través de vuestra clase con métodos static? Pues a lo mejor vuestro método GetCoches() retorna un dataset rellenado con los coches, o un datareader/recordset, o una lista separada por comas, o una estructura xml, o un array… Pero los DAO incluyen otro concepto que es del los DTO. Los DTO son un tipo de objetos que sirven únicamente para transportar datos. Por ejemplo, el objeto de transferencia de datos de un coche podría ser un CocheDTO (o CocheBean para los javeros). Entonces, DAO, lo que devuelve es una lista genérica de CocheBean, o tal vez un CocheBean si estamos buscando información de un solo coche. Para que os hagáis una idea, un CocheBean contiene únicamente propiedades de un coche como por ejemplo Marca, NumeroPuertas, Matrícula, etc. El DAO, tras una petición del Controlador, rellena un DTO o un conjunto de ellos, y lo devuelve al Controlador.

¿Y todo este lío para qué? Pues muy sencillo, si vuestro controlador ataca al modelo utilizando DAO y DTO estáis consiguiendo 100% la separación entre el Controlador y el Modelo. Ante cualquier cambio que se diera en la forma de acceder a los datos, pongamos por ejemplo que a partir de ahora en lugar de sacar los coches de una tabla coches se hace de un servicio web que retorna coches, vuestro modelo no se va a enterar, ya que lo que le va a seguir llegando van a ser siempre CocheBean. Conseguís un impacto cero patatero en vuestro Controlador (y en la Vista claro, pero eso ya lo habíamos conseguido separando la Vista del Controlador).

Y aún más, si cada uno de vuestros DAO lo preparáis para seguir el patrón Singleton, tendréis siempre una única instancia de la clase CocheDAO en memoria, por lo que aunque tengáis 200 usuarios tirando de dicho DAO tendréis una única copia en memoria, con el ahorro que esto supone. Esto no ocurre por ejemplo si usáis static en todos los métodos de vuestras clases de acceso a datos (y os recuerdo que en un proyecto mediano el número de métodos que llegan a acumularse para acceso a datos suele ser bastante grande).

¿Un ejemplo de código? Venga, vale, aceptamos barco, imaginaos que atacamos a una hipotética clase CocheDAO:

// GetInstance pide la instancia de la clase mediante el Singleton, CocheDAO tiene su constructor en modo privado.
CocheBean coche = CocheDAO.GetInstance().GetCocheByMatricula(‘IB-3234-ZX’);

// ahora tenemos un CocheBean, si no es nulo es que ha llegado bien, cogemos sus datos
if (coche != null)
{
    txtMatricula.Text = coche.Matricula;
    txtMarca.Text = coche.Marca;
    txtNumeroPuertas.Text = coche.NumeroPuertas;
}

¿Y qué ocurre en el Controlador si cambiamos el nombre del campo matrícula en la tabla coches? Pues nada, ni se entera.

Para finalizar os añado un link a un rar que he preparado con un DAO y un DTO preparados, que acceden a una hipotética tabla de Coches. Fijaros en el DAO lo que hace el Singleton, que además de forzar la utilización del método GetInstance, utiliza exclusión mútua para evitar que se tenga más de una instancia del DAO en el caso de no haberse creado aún la instancia y dos usuarios accedieran al mismo tiempo al método GetInstance. El método GetCoche retorna un CocheBean, pero el método GetCoches lo que hace es retornar un lista genérica de CocheBean. Veréis además que CocheDAO hereda de BaseDAO, todos los DAOS deberían heredar de allí ya que BaseDAO les ofrece entre otras cosas la conexión a la base de datos y una serie de métodos comunes que pueden utilizar (BaseDAO lo he dejado algo básico sólo para que se entienda conceptualmente qué es lo que se supone que debe hacer). También tenéis la definición de CocheBean. Por último, veréis que existe un archivo llamado Excepciones donde se tipifican las excepciones que puede generar un DAO, una de las buenas prácticas de las que ya hemos hablado en otros artículos.

Os contaría además que DAO puede implementarse junto a DTO, pero además utilizando el modelo de Factoría, pero esto, lo dejaremos para más adelante.

Ejemplo DAO (esta vez os lo preparo en VB.NET, por variar un poco).

¿Interesante?

Saludos.

Miguel.

Rating 4.57 out of 5
[?]

10.22.07

Protegiendo la Base de Datos mediante procedimientos almacenados

Posted in Bases de Datos, Programación at 11:02 pm by Miguel

Y es que últimamente le doy muchas vueltas a la protección del modelo de datos. Me quedan aún en la manga almenos un par de artículos más al respecto, siendo este uno de ellos.

Hoy hablaremos un poco de cómo proteger la base de datos mediante un método que me parece bastante curioso, y un poco peñazo sobre todo. Este método lo utilizan sobre todo empresas que dan acceso a una de sus bases de datos a un equipo de trabajo externo, ajeno a la empresa.

Resumiendo la historia un poco, la “técnica” se basa en que todo acceso a la base de datos (SELECT, INSERT, UPDATE, DELETE) se lleve a cabo mediante un procedimiento o función almacenada. Desde una SELECT algo pesada, a cualquier inserción modificación y borrado simple de un registro. ¿Y por qué trabajar de esta manera? Pues es para asegurarse de que sólo se va a acceder a la base de datos a través de dichos procedimientos almacenados. Se crea un usuario de base de datos que tenga permiso de ejecución sobre dichos procedimientos, y sólo para dichos procedimientos, y ese usuario se le da a la empresa externa para que haga login contra la base de datos mediante ese usuario.

Este sistema, a parte de la protección tiene alguna cosa más buena, y es que todos los procedimientos de acceso a datos están centralizados en un único punto… y que además, los procedimientos que sean pesados, al llevarse a cabo directamente sobre la base de datos, se ejecutan mucho más rápido que si se estuvieran lanzando sobre el cliente.

¿Cosas malas? Pues como siempre alguna hay. Por ejemplo, si trabajáis con SQL Server, los procedimientos almacenados no pueden agruparse en Paquetes (así como sí puedes hacerlo en ORACLE), por lo que si tienes un número grande de procedimientos almacenados, al final tienes una lista interminable que cuelga de una rama y que es un rollo de gestionar. Otra cosa mala, que parecerá una chorrada pero a mi me molesta especialmente es que para hacer una select simple que devuelve un registro o un grupo de registros o incluso un valor… tener que crear un procedimiento almacenado la verdad es que me parece un verdadero engorro. Además, tened en cuenta una cosa, a la hora de actualizar cualquier procedimiento almacenado vais a tener que generar un script de actualización y otro con las contramedidas, para luego desplegarlo en el servidor de pruebas… preproducción, producción… (vale, en otro caso tendríais que actualizar aplicación, pero bueno, que está claro que lleva trabajo).

¿Y qué os parece el utilizar una mezcla entre ambos extremos, es decir, parte del acceso a datos definido en el cliente y parte en procedimientos almacenados? Pues… una vez leyendo un informe de una persona Certificada por Sun lei que no era una forma adecuada el tener parte del acceso a datos definido en la base de datos y parte en la aplicación, porque a la hora de desarrollar no sabes muy bien por donde buscar, al tener repartido los accesos en diferentes componentes de la aplicación… ¿tenía razón esta persona? Pues la verdad es que aún le sigo dando vueltas…

Saludos.

Miguel.

Rating 2.50 out of 5
[?]

10.10.07

Malas Prácticas: Obtener la secuencia de una Clave Primaria con un Select Max()…

Posted in Bases de Datos, Malas Prácticas, Programación at 2:27 pm by Miguel

Hace no demasiado inaguré la sección de Buenas Prácticas, sección en la que ya se han añadido algunas entradas con recomendaciones al desarrollador.

Hoy dándole vueltas a una nueva buena práctica para añadir (y después de haber oído una barbaridad por la oficina), he pensado que tal vez sería interesante hablar también de las cosas que no hay que hacer bajo ningún caso, de ahí surge esta nueva sección llamada “Malas Prácticas”.

Como primera mala práctica vamos a hablar de una forma que se utiliza para obtener la secuencia de la primary key de una tabla. Insisto es una mala práctica, la cual no recomiendo para nada usar, más adelante se explicará por qué. 

Como siempre, lo describo con un ejemplo, que es la forma en la que creo yo que todo queda más claro.

Pongamos el caso de que tenemos una tabla llamada COCHE que consta de dos campos, COCH_CODIGO Y COCH_NOMBRE. COCH_CODIGO es la clave primaria de la tabla, y es de tipo numérico. COCH_NOMBRE es un texto de tamaño 50 caracteres. 

Un requerimiento de nuestra aplicación exige que al dar de alta un nuevo coche en nuestra base de datos, asociemos el código del coche al usuario que ha insertado el coche (imaginemos que la tabla USUARIO contiene un campo llamado COCH_CODIGO donde se guarda la referencia al coche del usuario).

Una MALA PRÁCTICA es intentar resolver este problema de la siguiente manera (utilizo pseudocódigo)

/* Obtenemos el código del usuario */
codigoUsuario = obtieneCodigoUsuarioActual();

/* Obtenemos la Id del último coche que añadimos */
int ultimaId = devuelvePrimerRegistro(“SELECT MAX(COCH_CODIGO) +1 FROM COCHE”);

/* Insertamos el registro */
ejecutarSQL(INSERT INTO COCHE (COCH_CODIGO, COCH_NOMBRE) VALUES (ultimaId, ‘Coche Fantástico’));

/* Actualizamos los datos del usuario */
ejecutarSQL(UPDATE USUARIO SET COCH_CODIGO = ultimaId where USUA_CODIGO = codigoUsuario);

¿Dónde está aquí el problema? …

¿Que ocurriría si dos usuarios al mismo tiempo fueran a dar de alta un nuevo coche? Fijaros en la secuencia

1) El primero haría el SELECT MAX y obtendría el código 23.
2) Antes de que el primero haga el INSERT en la tabla COCHE, el segundo haría el SELECT MAX y obtendría también 23
3) El primero hacer el INSERT, todo va bien.
4) El segundo hacer el INSERT… y PUM, CONSTRAINT ERROR, Clave Primaria duplicada.

¿Soluciones? Afortunadamente existen unas cuantas.

1) Secuencias ORACLE

Si usáis como base de datos ORACLE estáis de suerte, ya que aquí existe el concepto de secuencia. Puedes crearte una secuencia para cada tabla, y antes de realizar el insert pedir el siguiente número de dicha secuencia.

int secuencia = devuelveEscalar(“SELECT SEQ_COCHE.NEXTVAL”);
ejecutarSQL(INSERT INTO COCHE (COCH_CODIGO, COCH_NOMBRE) VALUES (secuencia, ‘Coche Fantástico’));
ejecutarSQL(UPDATE USUARIO SET COCH_CODIGO = secuencia where USUA_CODIGO = codigoUsuario);

Cuando el segundo usuario que ataque al código concurrentemente pida la secuencia, nunca será la que ha tenido el primero, por lo que nunca saltará la constraint de la foreign key.

2) Secuencias a media

Si trabajáis por ejemplo en SQL Server no tenéis secuencias. Pero eso no quiere decir que no podáis currároslas vosotros. Por ejemplo, podríais crear una tabla llamada SECUENCIA, donde tengas campos por cada una de las tablas de vuestra base de datos. Los campos serían de tipo entero.

Os creais una función que le paséis el nombre de la tabla, y que os busque en la tabla SECUENCIA el campo correspondiente a esa tabla, os lo retorne y actualice el campo en la tabla SECUENCIA.

Realmente con esta forma de trabajar podríais hacer igual que con Oracle, pero currado por vosotros.

3) Procedimientos Almacenados

Los procedimientos almacenados os proveen de herramientas para devolveros los datos producidos en un INSERT. Por ejemplo en PL/SQL tenéis la instruccion RETURNING, y en Transact/SQL tenéis el comando @@IDENTITY que te devuelve la última identidad obtenida para la conexión activa.

Saludos a todos.

Miguel

Rating 3.00 out of 5
[?]

09.15.07

PowerDesigner, algo más que mucho más allá

Posted in Bases de Datos, Herramientas at 5:05 pm by Miguel

Para los que necesitéis en el día a día de vuestro trabajo dedicar largas horas al modelado de datos, procesos, diagramas UML… existen bastantes herramientas en el mercado. La más famosa (que no la mejor) es Visio, pero existen otras como por ejemplo SmartDraw, y la que ocupa la cabecera de esta entrada, el PowerDesigner.

Hablando sólo de la parte que tiene que ver con el modelado de datos, a parte de las funcionalidades que ya existen en Visio, tenemos algunas otras muy interesantes que me han llamado mucho la atención, como por ejemplo que él mismo es capaz de generarte clases (Java, C#…) para gestionar las tablas que hayamos creado en el modelo de datos.

Links relacionados:

http://www.sybase.com
http://www.sybase.com/products/modelingmetadata/powerdesigner

Rating 3.00 out of 5
[?]

09.13.07

La importancia de la protección del modelo de datos en ámbitos de Software-Factory

Posted in Bases de Datos, Metodologías, Motivación, Programación at 8:25 pm by Miguel

A medida que voy conociendo nuevos proyectos y aplicaciones desarrolladas por otras personas, me doy aún más cuenta de la importancia de proteger y abstraer al desarrollador del modelo de datos en el que asienta una aplicación.

Proteger y abstraer siempre es algo básico, pero sobre todo, si el desarrollo de una aplicación va a llevarse en un modelo de desarrollo de Software-Factory. A continuación expongo alguna de las razones más importantes.

1) La volatidad de los profesionales que trabajan en este tipo de empresas es muy alta.

2) Es habitual que los proyectos estén integrados por un mayor porcentaje de profesionales con experiencia media/baja (becarios, programadores con un año o dos de experiencia…) elevado, liderados por un grupo reducido de profesionales con mayor experiencia (en proyectos de 4 o 5 personas estamos hablando de 1 profesional o 2 realmente qualificado).

3) La formación en las herramientas/tecnologías a utilizar por parte de los miembros del proyecto con menor experiencia es proporcional a los años de experiencia profesional que acumulan.

Con todo este panorama por delante, veo totalmente esencial que el acceso y manipulación del modelo de datos se deba llevar a cabo por los profesionales con más experiencia del grupo, abstrayendo al resto del problema, y centrándolos en el desarrollo de la vista y el controlador. Por supuesto que el desarrollo del controlador se va a basar en llamadas al modelo de datos que hemos protegido, siguiendo unos flujos que el analista funcional debe encargarse de transmitir a los programadores.

¿Cómo llevar a cabo dicha protección y abstracción? A mi modo de ver la mejor forma de realizar esta labor es llegando a crear la sensación al programador de que el modelo de datos no existe, que no hay base de datos. Únicamente disponemos de un conjunto de clases (agrupadas en un NameSpace, Package…), que representan el modelo de negocio y que cuentan ya con una serie de operaciones asociadas.

Y por supuesto dichas clases son una caja negra, de la cual sólo conocemos su estructura, pero para nada cómo funcionan por dentro. Con mi clase “Coche” podré realizar todas las operaciones asociadas a un Coche, es decir, Arrancar, Aparcar, Acelerar, Frenar, PonerFrenodeMano, BajarVentanilla, y podré acceder además a otra información como NumeroDePuertas, VelocidadActual, KilometrosRecorridos, Consumo, Marca, Modelo, Conductor…

Si necesito crear una aplicación que a partir de una ruta por una carretera calcule el consumo de diferentes modelos de coche dependiendo de su velocidad media y de si tiene las ventanillas bajadas, no tendría más que utilizar dos de mis clases Coche y Ruta de la siguiente manera.

// Definimos modelo de coche, abrimos la puerta
Coche.Modelo(Seat.SeisCientos);
Coche.AbrirPuerta();

// Creamos una nueva ruta a partir de alguna de las que tengamos definidas
Ruta.DefinirRuta(Rutas.Zaragoza-Barcelona-PorAP2); 

// Asociamos la ruta al coche, marcamos velocidad, puertas y definimos que el viaje se hará
// con las ventanillas bajadas… arrancamos y ala, a viajar
Coche.EstablecerRuta(Ruta);
Coche.VelocidadMedia = 120;
Coche.NumeroPuertas = 3;
Coche.BajarVentanilla();
Coche.Arrancar();
Coche.RealizarViaje();

// Guardamos los datos asociados al viaje
Coche.AlmacenarDatosConsumo();

Como podemos ver la protección y la encapsulación es máxima. Sabemos que para poder realizar un viaje primero hay que arrancar el coche, pero no sabemos si el arrancar el coche guardará en alguna tabla alguna información al respecto. Por supuesto no sabemos cómo se realiza el cálculo del consumo de carburante respecto a la velocidad media, ruta y estado de las ventanillas… llamando a “RealizarViaje” se calculará todo. Y lo que es más importante, se habrá consultado información sobre la ruta, modelo del coche… tal vez entren en juego 5, 6 o 20 tablas, pero a nosotros nos trae sin cuidado. Y finalmente, a la hora de almacenar los datos de consumo, tal vez tengamos un histórico de consumos asociados al coche, a la ruta… no lo sabemos, de eso se encargará la clase.

¿Por cierto, quién nos asegura que la información se esté guardando en una base de datos relacional? ¿tal vez se esté llevando acabo en un excel, o en un fichero plano separado por comas… o en una mysql, en un sql server o un oracle? ¿Es necesario si quiera que el desarrollador sepa qué es una select o un insert o un update?

Nuestro modelo de datos está 100% protegido a modificaciones llevadas de forma incorrecta.

Y en cuanto a la seguridad, podríamos dar acceso a un modelo de negocio a una tercera empresa para que se encargara del desarrollo de la aplicación, pero sin mostrarle realmente lo que hay detrás (¿en una dll, en un servicio web?)

Y en cuanto a la motivación del equipo de desarrollo, siguiendo esta forma de desarrollo, ¿quién siente realmente que está moviendo al coche? ¿todos?

Saludos.

Miguel.

Rating 3.00 out of 5
[?]

08.13.07

Integration Services

Posted in Bases de Datos, Libros at 10:24 am by Miguel

Y siguiendo con la formación que voy tomando con las nuevas herramientas, nuevas para mi claro, que vienen usando en mi nuevo trabajo, voy dejando por aquí la documentación en forma de libros que van llegando a mi poder.

 En este caso la formación viene al respecto de los Integration Services de SQL Server 2005, los cuales pasaré a contaros un poco la historia un poco más adelante. Resultan algo verdaderamente interesante, aunque tal vez lo mejor sea la facilidad con la que el entorno de desarrollo te deja llevar a cabo tareas de alta complejidad extracción, transformación y carga de datos (ETL) entre diferentes fuentes. Como decía un conocido presentador… “Ay, si te hubiera conocido antes…”

Aquí os dejo los datos sobre el libro, la verdad es que es muy claro y con ejemplos suficientes para entender la herramienta.

Título: Professional SQL Server 2005 Integration Services
Autor: Brian Knight et al. 
Editorial: Wrox Press
ISBN:0764584359
Idioma: Inglés

Rating 3.00 out of 5
[?]

08.10.07

Transact-SQL

Posted in Bases de Datos, Libros, Programación at 2:03 pm by Miguel

Por motivos de curro me ha tocado meterme a fondo con Transact-SQL. Hasta ahora había trabajado ya con procedimientos almacenados, pero siempre bajo Oracle.

Toca reciclarse con los procedimientos almacenados para SQL Server 2005, y éste utiliza Transact-SQL, por lo que no ha quedado más remedio que tirar de libro y manual para ver cómo funciona la cosa.

He estado consultado un libro sobre Transact-SQL que la verdad es que me ha sido de gran ayuda para refrescar conceptos y, sobre todo, ver cómo se aplican los conocimientos que tenía ya con PL/SQL a Transact.

Os lo recomiendo.

Nombre del libro: Beginning Transact-SQL with SQL Server 2000 and 2005
Autores: Paul Turley and Dan Wood 
Editorial: Wrox Press
ISBN: 076457955X
Idioma: Inglés

Rating 3.00 out of 5
[?]

« Previous Page « Previous Page Next entries »