Flyway e Spring Boot em cenário de banco de dados não vazio.
Tags: java, springboot, programming, tutorial
Published: 2024-04-27 07:39:58

Em um projeto de programação, em que estava construindo uma API com Spring Boot (Java), precisei adicionar alguns records numa tabela do meu banco de dados. Devido ao meu repertório e também buscando me desafiar, adicionei a dependência do Flyway para adicionar migrações ao meu banco de dados. **Entretanto, ao adicionar o Flyway num projeto existente, ocorreram alguns comportamentos inesperados.**

## Mas, calma, tô voando.. O que é o Flyway?
Basicamente, o Flyway serve de versionamento de banco de dados. Suponha que existem 3 programadores rodando o mesmo projeto num ambiente de desenvolvimento. O ideal é que o banco de dados de todos (mesmo que em ambientes diferentes) possuam as mesmas tabelas.

Por meio de vários arquivos `.sql`, o Spring detecta a existência desses arquivos, roda o script SQL antes mesmo de criar as tabelas com o Hibernate. Assim, podemos criar arquivos `.sql` para criar as tabelas necessárias para o funcionamento correto da aplicação, garantindo que as tabelas possuem o mesmo formato para cada ambiente de desenvolvimento diferente. Uma forma de padronizar e manter o banco de dados atualizado para todos do time.

## E Hibernate?
Hibernate é uma implementação da JPA (Java Persistence API), responsável pela ponte entre a sua aplicação e seu banco de dados. Todo método default do `repository` é implementado pelo Hibernate. O Hibernate também é o responsável por criar suas tabelas das entidades do seu projeto (classes anotadas com o `@Entity`).

_O Hibernate já cria minhas tabelas, por qual razão eu preciso do Flyway?_

Como já comentado acima, o Flyway é usado para padronizar as tabelas do seu projeto. Dessa forma, o Spring executa as migrações do Flyway antes mesmo de executar o Hibernate, logo, o ideal é que o time defina muito bem essas tabelas por meio de arquivos `.sql` no projeto.

**A boa prática é criar as migrations antes mesmo de criar qualquer classe dentro do seu projeto.**

## Adicionando Flyway a um projeto existente
A motivação de escrever esse arquivo veio quando tentei adicionar o Flyway ao meu projeto que já possuía as tabelas mapeadas para cada entidade, criadas através do Hibernate.

Minha ideia era usar o Flyway apenas para inserir records numa tabela já existente, porém notei que as inserções não estavam acontecendo.

### Dissecando o problema
Primeiro, fui atrás de criar novas migrations no projeto, para ter certeza de que o Flyway estava configurado corretamente e funcionando quando eu criava novas tabelas. E sim, ele funcionara corretamente quando foi criada uma tabela exemplo arbitrária.

Tendo em mente que o problema não estava na configuração da dependência externa, fui atrás de saber como funciona o relacionamento do Flyway com o Hibernate. **O interessante (como já foi comentado nesse artigo) foi descobrir que o Flyway é executado antes do Hibernate.** Dessa forma, era óbvio que minha lógica não funcionaria, afinal, como eu posso inserir records numa tabela que ainda não foi carregada pelo Hibernate?

**O problema estava exatamente nessa ordem de execução das dependências.**

### Resolução
Bastou criar uma classe de configuração do Flyway para que fosse carregado o conteúdo do Hibernate antes do próprio Flyway. Para isso:

1. Defini a propriedade `spring.flyway.enabled=false` no `application.properties`
2. Criei uma classe de configuração Flyway `FlywayConfig.java`
```java
@Configuration
public class FlywayConfig {
    
    @Autowired
    public FlywayConfig(DataSource dataSource) {
        Flyway.configure()
                .baselineVersion("0.0")
                .baselineOnMigrate(true)
                .dataSource(dataSource)
                .load().migrate();
    }
}
```
O próprio Spring injeta o dataSource nesse construtor do Flyway, indicando que ele deve carregar primeiro o banco de dados `.dataSource(dataSource).load()` e apenas depois executar a migração, por meio do método `migrate()`.


Feito isso, criei uma nova migration com inserção de records `V4__insert_user.sql`
```sql
insert into tb_user(id, email, name, password, username) values
(7, 'zeldris@gmail.com', 'zeldris', 'zeldris', 'zeldris');
```

E voilà, a record está inserida na minha tabela:
![tela mostrando a inserção](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kf3xpa6xixiflcm222t9.png)

## Conclusão
O intuito desse artigo é compartilhar essa noção de funcionamento das duas dependências que lidam com banco de dados: Hibernate e Flyway, além de ajudar aqueles que estão com o projeto existente e passam pela mesma situação de mal funcionamento das migrations da ferramenta.