Liquibase, como ele ajuda no desenvolvimento?

Alex Santos
TOTVS Developers
Published in
5 min readNov 27, 2023

Quando falamos de banco de dados, muitas vezes podem existir alguns tipos de problemas ou impedimentos para criar ou modificar certas estruturas. Quais são os impactos de rodar algum comando no ambiente de produção e sem querer, rodar o comando que era apenas para ambiente de desenvolvimento?
Às vezes pode rolar o famoso commit and demmit, que é executar alguma instrução sem where e bagunçar a base de clientes, e por aí vai.

Photo by Stephane YAICH on Unsplash

O Liquibase é uma ferramenta open source de migrações de base de dados. Por que ele é tão poderoso? Por ter uma forma segura de controlar qual estado o banco está. Existe uma tabela chamada databasechangelog, e nela é gravada uma “foto” de como aquela base atualmente se encontra.
Quando precisamos criar uma tabela, por exemplo, e em determinado momento apagar, provavelmente precisaremos utilizar o “if exists” do gerenciador do banco de dados escolhido. Porém, com o Liquibase conseguimos manter o histórico do estado do nosso banco de dados, e isso é um controle absurdo que temos na nossa rotina de desenvolvimento.

Vamos supor, que em sua rotina de desenvolvimento você atue com um time de 5 pessoas desenvolvedoras. A sua empresa não tem recursos o suficiente, então todas essas 5 pessoas apontam para uma base em algum servidor remoto, só que algumas delas usam Postgresql e outros Mysql, pois a empresa oferece soluções para N linguagens de bancos de dados. Só de pensar nesse cenário, já dá para imaginar o caos que isso pode virar.
O cenário do desenvolvimento seria o seguinte: chegou uma tarefa nova a ser feita, e ela requer que seja alterado o modelo de persistência, a pessoa desenvolvedora teria que fazer um script diferente para os dois bancos com as suas devidas especificidades, e se alguma delas rodar antes da outra, ou, ao mesmo tempo, poderia gerar alguns erros.

O Liquibase vem para atacar justamente esse problema: São criados changesets com identificadores e autores, que ao rodar, eles demarcam que já foram executados, e o melhor: eles são compatíveis com vários DBMS’s (Database Management System), o que o torna portátil, não sendo necessário escrever instruções diferentes para Oracle e para Postgres, por exemplo, a não se que sejam muito específicas.

Os changesets são conjuntos de instruções a serem executadas no banco, e eles são aninhados em um changelog. É possível criar o arquivo do changelog em várias extensões, como JSON, XML e YAML, e podemos ter vários changelogs diferentes, podendo separar por contexto de testes, SGBD, artefato e assim por diante.

Qualquer problema, saberemos com quem precisaremos conversar para efetuar a resolução, e também temos a segurança que enquanto o Liquibase estiver executando as migrações, ele irá gerar um lock na base de dados, não deixando que outra migração seja executada concorrentemente. Isso é um ponto importante, pois caso esteja rodando uma rotina de teste de base ou deploy, por exemplo, aquela instância irá travar o estado atual da base de dados, o que não fará que outro processo “encavale” o atual.

Inclusive para cenários mais complexos como a subida de ambientes para testes E2E, seria necessário construir a estrutura e gerar os cenários de testes. O que aconteceria caso outra pessoa rodasse o mesmo JOB? Sim, provavelmente ele quebraria, se ele alterasse a estrutura ou até mesmo chegasse na parte de fazer inserções que já existissem na base. Ter alguém gerenciando o estado da base por nós é, basicamente, termos um conselheiro para nos guiar no que podemos ou não fazer naquele momento.

Outra configuração muito interessante que pode ser feita com o Liquibase, é a execução das migrações antes da aplicação subir, isso fará com que a base de dados esteja atualizada para que os nossos modelos estejam condizentes com as nossas tabelas, e que caso sejam incompatíveis, o nosso ORM (Object Relational Mapper) irá quebrar.

Além da marcação do autor, que é de extrema importância, temos também em qual contexto ou até em qual DBMS que aquela migração será executada. Podemos criar inserções que devem ser executadas em apenas contexto de teste, para efetuarmos as nossas validações, ou até mesmo criação de procedures com as sintaxes específicas em determinados DBMS. São infinitas as possibilidades de construção das nossas migrações.

Um exemplo de criação de uma tabela:

<changeSet id="Create Person Table" author="alex.santos">
<comment>Define a tabela pessoa, segundo a issue SC-321</comment>

<createTable tableName="person">
<column name="id" type="integer" autoIncrement="true">
<constraints primaryKey="true" primaryKeyName="person_pk" />
</column>
<column name="name" type="varchar(100)">
<constraints nullable="false" />
</column>
<column name="surname" type="varchar(100)">
<constraints nullable="false" />
</column>
</createTable>

<createSequence sequenceName="person_sequence"/>
</changeSet>

Porém, se em determinado momento alguém precisou alterar esse changeSet, como o Liquibase reage para informar que ele foi alterado?
Ele informará um erro que o validCheckSum foi modificado, e irá cancelar a execução da migração.
CheckSums são hashs gerados a partir dos changesets e persistidos na nossa tabela databasechangelog, ele considera o contexto, formatação, comentários, condições, e a estrutura da migração em si.
Ao gerarmos um novo CheckSum, essa migração não será executada novamente, exceto se fizermos uma pré-validação da mesma:

<changeSet id="Create person table-fix" author="alex.santos">
<preConditions onFail="MARK_RAN">
<changeSetExecuted id="Create Person Table" author="alex.santos"/>
</preConditions>
<modifyDataType tableName="person" columnName="surname" newDataType="varchar(50)"/>
</changeSet>

<changeSet id="Create person table" author="alex.santos">
<validCheckSum>8:b4fd16a20425fe377b00d81df722d604</validCheckSum>
<addColumn tableName="person">
<column name="surname" type="varchar(50)"/>
</addColumn>
</changeSet>

Pode parecer verboso, mas imagine que precisaremos fazer várias condições de validação no nosso script, para vários DBMS’s? Analise como poderia ser bastante inviável:


CREATE TABLE IF NOT EXISTS person(
id integer PRIMARY KEY,
name VARCHAR ( 100 ) NOT NULL,
surname VARCHAR ( 50 ) NOT NULL,
);

-- Alter table with exists...

CREATE SEQUENCE IF NOT EXISTS person_sequence;

Outra coisa que podemos fazer é indicar contextos específicos, para executar de acordo com o cenário que a nossa aplicação subir.

Essa inserção rodará apenas em contexto de teste, e não precisaremos separar os scripts de teste dos de produção, e se quisermos deixar eles mais organizados, podemos separá-los:

<changeSet id="Insert Test Scenário Person" author="alex.santos" context="test">
<comment>Insere cenários de teste para Person.</comment>
<insert tableName="person">
<column name="id" valueSequenceNext="person_sequence" autoIncrement="true"/>
<column name="name" value="Alex"></column>
<column name="surname" valueComputed="Santos"></column>
</insert>
</changeSet>

Também existe a possibilidade de especificar qual DBMS que essa migração irá executar:

<changeSet id="procedures-person" author="alex.santos">
<comment>Criação de procedures em bancos suportados - SC325</comment>
<sql dbms="postgresql">
select myProcedurePostgres('name');
</sql>
<sql dbms="oracle" splitStatements="false">
BEGIN
myProcedureOracle('name');
END;
</sql>
<sql dbms="mssql">
EXECUTE dbo.myProcedureMSSql'name';
</sql>
</changeSet>

O que eu mais gosto do Liquibase é que o aprendizado é feito de uma forma equitativa, onde no próprio site são oferecidos cursos e, inclusive, certificações ao final deles. Então, minha sugestão é que, aproveite para além de aprender, se certificar, pois sabemos que hoje isso é muito importante no mercado de trabalho.

--

--

Alex Santos
TOTVS Developers

Um amante de música e tecnologia, apaixonado por desenvolver soluções.