Gestión de cambios de la base de datos – Liquibase

| 2014-06-24 | No hay comentarios »

Una de las prácticas habituales en los desarrollos, es la creación de script sql incrementales. Con lo que comenzamos con script de creación de tablas, script de inserción de datos etc etc … y, a medida que avanza el desarrollo, cada uno de los desarrolladores va creando scripts con las modificaciones oportunas que le exige la tarea que está llevando a cabo. Esta práctica hace del proceso de cambios de base de datos un proceso totalmente manual, que puede llevar a confusiones, sobre todo debido a que es muy posible no saber que scripts se han ejecutado y cuales faltan por ejecutar en cada uno de los entornos que estemos manejando.

Una de las herramientas que nos puede hacer más sencillo estas modificaciones que se producen a la base de datos es Liquibase. Los scritps que actualizan la base de datos se definen en un XML (o json entre otros) de cambios que es independiente del sistema de base de datos que usemos aunque se pueden incluir sentencias SQL específicas para uno de ellos, también se pueden definir las sentencias rollback que permiten volver a un estado anterior. Soporta las principales bases de datos entre ellas: 

  • MySQL
  • PostgreSQL
  • Oracle
  • MS-SQL
  • SQLite
  • Y otras entre ellas Sybase Enterprise, Sybase, DB2, Apache Derby, HSQL, H2, Informix, InterSystems, Firebird, SAPDB.

Liquibase es un librería Open Source (bajo licencia Apache 2.0), totalmente independiente del DBMS, que nos permitirá realizar el seguimiento, gestión y aplicación de cambios en el modelo de datos. Hoy en día, en cualquier desarrollo profesional de software no puede faltar un sistema de control de versiones. Esta práctica esta totalmente arraigada en los desarroladores que, sin embargo, en muy pocas ocasiones la usamos en cuanto al modelo de datos se refiere.

Liquibase es una librería diseñada para trabajar principalmente desde línea de comandos, pero también se integra muy fácilmente con Maven y Spring. Con ésta herramienta no tendremos que preocuparnos de estar ejecutando diferentes scripts, ni el orden en que se ejecutan. Además cada miembro del equipo de desarrollo dispondrá de información sobre que cambios se han realizado y quien los ha realizado.

Entorno

El tutorial está escrito usando el siguiente entorno:

  • Maven (Utilizando Netbeans 7.4 – JAVA EE)
  • Liquibase 3.0.8
  • PostgreSQL 9.2

Puesta en marcha

Antes de empezar me gustaría dejarles el enlace a la página oficial de la librería. Liquibase. En ella van a poder encontrar toda la documentación disponible sobre el proyecto ya que el objetivo del tutorial es darles a conocer esta librería y enseñarles como ponerla en marcha.

En nuestro caso no utilizaremos la librería de forma directa ya que decidí utilizar MAVEN y dicha herramienta ya posee un plugin para implementar Liquibase fácilmente, por más info, click aqui

liquibase

Paso 1: Modificar el archivo pom.xml

Obs: Agregar la dependencia de PostgreSQL dentro del tag  dependencies y el plugin Liquibase dentro del tag build.


 <!-- PostgreSQL Java Connector -->
 <dependency>
 <groupId>org.postgresql</groupId>
 <artifactId>postgresql</artifactId>
 <version>9.2-1003-jdbc4</version>
 </dependency>

<!-- Plugin Liquibase -->

<build>
 <plugins>

<plugin>
 <groupId>org.liquibase</groupId>
 <artifactId>liquibase-maven-plugin</artifactId>
 <version>3.0.8</version>
 <configuration>
 <changeLogFile>src/main/resources/db/db.changelog-master.xml</changeLogFile>
 <driver>org.postgresql.Driver</driver>
 <username>username</username>
 <password>password</password>
 <url>jdbc:postgresql://localhost:5432/db</url>
 </configuration>
 <executions>
 <execution>
 <phase>process-resources</phase>
 <goals>
 <goal>update</goal>
 </goals>
 </execution>
 </executions>
 </plugin>

 </plugins>
 </build>

Los parámetros a modificar son:

<username>username</username> -> Usuario
<password>password</password> -> Contraseña
<url>jdbc:postgresql://localhost:5432/db</url> -> IP, PUERTO Y NOMBRE DE BASE DE DATOS.

Paso 2: Crear el arhivo db.changelog-master.xml dentro de un package “db” en la sección “Other Sources”

db.changelog-master.xml


<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
 xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.6"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.6
 http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.6.xsd">

 <!-- <changeSet id="1" author="Rodrigo">
 <comment>Agregar tabla test_table para prueba</comment>
 <createTable tableName="test_table">
 <column name="id" type="bigserial">
 <constraints primaryKey="true" nullable="false"/>
 </column>
 <column name="name" type="varchar(50)">
 <constraints nullable="false"/>
 </column>
 </createTable>
 </changeSet>
-->

</databaseChangeLog>

Con eso ya está lista la implementación de Liquibase, como verán es muy sencilla, en el último archivo XML dejé comentado un ejemplo del uso del changelog, siempre debe de tener un id secuencial y el nombre del desarrollador, también es recomendado agregar comentarios.

Al ejecutar el proyecto los cambios en la base de datos se realizarán de forma directa, si se trabaja con cualquier control de versiones asegurarse de tener un responsable en la administración del archivo changelog para no tener conflictos.

Ahora quiero dejarles una base teórica de todo lo que se puede hacer con la herramienta Liquibase.

Liquibase Tags

Liquibase nos proporciona una serie de etiquetas con las que construir nuestro conjunto de cambios. Lo cierto es que es bastante intuitivo, y como veremos a lo largo del tutorial, tanto el nombre de las etiquetas, como el nombre de los atributos de las mismas nos van a dar pistas sobre cual es la operación que van a realizar.

En un primer momento, el hecho de tener que aprender un nuevo “lenguaje”, distinto de SQL para realizar cambios en la base de datos es una característica de liquibase que no me gustó en un principio. Principalmente dudaba de si con las etiquetas que Liquibase nos proporciona, se podrían realizar todos los cambios que con SQL estamos acostumbrados a realizar, sobre todo aquellos cambios que pudiesen resultar más complejos.

Después de realizar las primeras pruebas las dudas se disiparon. Liquibase nos va a permitir en todo momento seguir trabajando con script SQL, con la ventaja añadida de que todos lo cambios quedaran reflejados en el histórico.

  • <databaseChangeLog>

    Es la etiqueta raiz a partir de la cual Liquibase comienza a analizar y ejecutar cada uno de los cambios.

  • <preConditions>

    Comprueba que se cumplan cada una de las condiciones definidas. Si alguna de las precondiciones fallase Liquibase acabaría con un mensaje de error donde se indicará cual ha sido la condición que ha fallado. Las precondiciones son muy útiles para comprobar por ejemplo, el tipo de base de datos y el usuario que realizará las distintas modificaciones.

  • <changeSet>

    Con esta etiqueta se definen cada uno de los cambios que se realizan en la base de datos. Por cada changeSet se insertará una nueva fila en el histórico de cambios. Por ello es recomendable hacer un solo cambio por cada chageSet, teniendo en cuenta que se permiten más y puede tener sentido por ejemplo, si se van ha insertar varias filas que forman parte de una misma transacción.

    Un changeset es un conjunto de cambios que se identifica por dos atributos, el “id” y el “author”. Si solo se utilizase el “id” para identificar un changeset, sería demasiado facil duplicar un conjunto de cambios de manera accidental, sobre todo cuando se está trabajando dentro de un grupo de desarrollo.

    • <createTable>

      Nos permite crear nueva tabla en base de datos:


 <createTable tableName="client">
 <column name="id" type="int">
 <constraints primaryKey="true" nullable="false"/>
 </column>
 <column name="firstname" type="varchar(255)"/>
 <column name="lastname" type="varchar(255)"/>
 <column name="username" type="varchar(255)">
 <constraints unique="true" nullable="false"/>
 </column>
 <column name="testid" type="int" />
 </createTable>

<dropTable>

Nos permite eliminar una tabla en base de datos.


<dropTable tableName="client" schemaName="mySchema"/></p>

<addColumn>

Nos permite añadir una columna a una tabla.


 <addColumn tableName="client">
 <column name="firstname" type="varchar(255)"/>
 </addColumn> 

<renameColumn>

Nos permite renombrar una columna de una tabla.


 <renameColumn tableName="client"
 oldColumnName="fname" newColumnName="firstName"/>

<modifyColumn>

Nos permite modificar una columna de una tabla.


 <modifyColumn tableName="client">
 <column name="firstname" type="varchar(5000)"/>
 </modifyColumn>

<dropColumn>

Nos permite eliminar una columna de una tabla.


<dropColumn tableName="client" columnName="firstname"/>  

 

<sqlFile>

Nos permite ejecutar un script SQL dentro del proceso de migración de liquibase.


<sqlFile path="/Users/sgdiaz/liquibase/insertAutentiaMasterClients.sql" /> 

<include>

El principal uso de esta etiqueta es poder romper el fichero principal de cambios (changelogs.xml) en pedazos más manejables. Esto supone una gran ventaja ya que a medida que avanzan los desarrollos el número de modificaciones suele ser muy elevado.

Una buena práctica es trabajar con un fichero de cambios por sprint o por versión, en función de las necesidades. Algo como :


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
xmlns:xsi="http://www.w3.org/01XMLSchemainstance"
xsi:schemaLocation="http:www.liquibase.org/xml/ns/dbchangelog/1.9 http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">

<include file="db/db.changelog-1.0.xml" />
</databaseChangeLog>

  • <includeAll>

    Esta etiqueta es muy similar a la anterior, pero en vez de incluir un listado de ficheros específicos, se indica un directorio en el que se incluyen todos los archivos *.xml y *.sql que conformarán nuestro conjunto de cambios. Todos los archivos que se encuentren en el directorio indicadado se ejecutan en orden alfabético. Según la documentación el uso de esta etiqueta puede provocar errores futuros, posiblemente porque el orden de ejecución puede provocar conflictos en nuestra base de datos. Pos eso es recomendable usar <include>

  • <context>

    Esta etiqueta básicamente nos permite distinguir entre los distintos entornos de ejecución. Normalmente en cualquier desarrollo suele haber distintos entornos en los que la aplicación es ejecutada (desarrollo, preproducción y producción por ejemplo). Mediante esta etiqueta podremos indicar a un changeSet a que entorno de ejecución pertenece, después, al ejecutar liquibase, indicaremos por parámetro cual es el entorno en el que se realizarán los cambios, de esta manera Liquibase solo ejecutará los changeSet que pertenezcan al entorno indicado como parámetro.

Liquibase-Funcionalidad

A continuación os muestro cuales son las funciones que podremos realizar con Liquibase:

  • Update

    El update es la funcionalidad principal de la librería. Nos va a permitir aplicar los cambios en base de datos en función al changelogs.xml generado con anterioridad. Como ya hemos visto, cada uno de los cambios viene definido por los changeset, que se identifican por el author y el id. Cada vez que se ejecuta un update Liquibase verifica cada uno de los identificadores de los changeset. Si el identificador no existe se inserta una nueva fila en el histórico de cambios (DATABASECHANGELOG) que contiene el identificador del chageset más un MD5Sum. De esta manera Liquibase puede identificar de manera unívoca cada uno de los cambios realizados.

Tras realizar el update el chageset se ejecuta correctamente pero nos damos cuenta de que nos hemos equivocado en el nombre de una de las columnas. Es fácil caer en el error de modificar el changeset directamente, lo que nos provocará el error anteriormente mencionado (Los MD5Sum son distintos).

  • Ejecutando Update

La primera vez que ejecutemos este comando contra una base de datos Liquibase creará dos tablas en el schema correspondiente. Una llamada DATABASECHAGELOG y otra DATABASECHAGELOGLOCK. La primera es donde se guardaran todos los cambios y la segunda es usada por Liquibase para controlar que solo se realicen un conjunto de cambios al mismo tiempo.

  • Rollback

    Liquibase nos va a permitir deshacer los cambios realizados en base de datos, ya sea de manera automática o de manera manual. Algunos de los tags de Liquibase como <careateTable>, <addColumn>, <renameColumn> llevan implícitamente un rollback, lo que quiere decir que, si por algún motivo se produce un error, se desharan los cambios de manera automática.

  • Diffs

Aunque la mejor manera de controlar los cambios en la base de datos es mediente la adición de conjuntos de cambios durante el desarrollo, hay ocasiones en que será muy valioso poder realizar diffs de distintas bases de datos, sobre todo cuando se está llegando al final del proyecto, pudiendo así comprobar que todos los cambios necesarios están incluidos en el changelog. Es importante tener en cuenta que la ejecución del diff solo está disponible desde la línea de comandos.

En la actualidad Liquibase realiza las siguientes comparaciones:

-Diferencias de versión
-Ausencia de tablas
-Ausencia de vistas
-Ausencia de columns
-Ausencia de primary keys
-Ausencia de unique constraints
-Ausencia de foreign Keys
-Ausencia de secuencias
-Ausencia de índices
-Diferencias en la definición de las columnas (data type, auto-increment, etc.)
-Diferencias en la definición de las vistas.

  • Generate Change Logs

Esta funcionalidad de Liquibase es muy útil cuando empezamos a trabajar con una base de datos que ya tiene creadas una serie de tablas. De tal manera que podremos generar nuestro changelogs.xml a partir del modelo de datos existente. Eso si, hay que tener en cuenta que esta funcionalidad tiene actualmente algunas limitaciones, como por ejemplo que no exporta procedimientos almacenados, funciones y triggers.

  • DBDoc

Utilizando la información existente en el histórico y una base de datos existente, Liquibase puede generar documentación sobre los cambios realizados en base de datos.La documentación generada es al estilo JavaDoc.

  • SQL Output

Depenpdiendo del proceso de desarrollo y de las necesidades que tengamos a la hora de liberar versiones, es posible que no queramos utilizar Liquibase para realizar los cambios sobre la base de datos de manera directa.

Por esta razón tanto los comandos Update y Rollback tienen un “sql output” de modo que no se ejecute nada contra la base de datos sino que se genera un script.sql que el desarrollador ejecutará manualmente.

Conclusiones

Liquibase es una herramienta muy útil que da un valor añadido a los desarrollos. Hay que tener en cuenta que llevar control estricto sobre el modelo de datos es algo muy importante que no siempre se realiza con la rigurosidad que requiere. Por tanto Liquibase puede convertirse en la herramienta perfecta para llevar acabo esta tarea.

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Electron, framework Javascript para crear aplicaciones de escritorio
  • JSF 2 con Maven – Hola Mundo
  • Java RMI
  • Corba con Java e IDL



SEGUÍNOS EN FACEBOOK


GITHUB