Como gravar Enum no banco de dados

Sem dúvida os enumeradores facilitam muito o desenvolvimento de aplicações e ajudam no entendimento da regra de negócio já que podemos usar valores autoexplicativos facilitando a vida de qualquer desenvolvedor.

Mas uma dúvida sempre surge quando queremos armazenar essa Enum no banco de dados. Nesse tutorial vamos ensinar várias formas de gravar informações desses enumeradores no banco de dados, usando uma implementação do Jpa.

Caso de uso de exemplo

Temos aqui um enumerador que mostra o tipo do cartão:

Vamos simular uma aplicação que guarda informações de cartões no bando de dados, e para isso vamos criar a classe que espelha a tabela do banco com os dados que queremos armazenar, e um Enum que identifica o tipo do cartão.

package com.artefatox.example;

public enum Type {
    DEBIT, CREDIT;    
}
Type.java

E aqui a classe que representa um cartão:

package com.artefatox.example;

import java.time.LocalDate;

import javax.persistence.Entity;

@Entity
public class Card {
    
    @Id
    private Long id;
    private String holderName;
    private String number;
    private String cvv;
    private LocalDate expiration;
    private Type type;

    //getters and setters
}
Card.java

Gravando a posição do Enum

Na grande maioria das aplicações Java, onde usamos bastante o conceito de enumeradores, e implementações do Jpa, como o Hibernate por exemplo, já nos fornece um método padrão, que é gravar o sequencial da Enum no banco.

Com o exemplo acima se tentarmos gravar usando alguma implementação do Hibernate, como Spring JPA por exemplo, o que seria gravado na coluna que representa o tipo (type), seria a sequência da enum, no caso do tipo ser definido como CREDIT, seria armazenado o valor 1.

Como fica os dados gravados na tabela:

Idholder_namenumbercvvexpirationtype
1Kalli Cristina5140 6562 3810 21069492028-04-141
Exemplo dos dados no banco de dados.

Particularmente não gosto muito dessa abordagem, já que algum desenvolvedor pode vir a inserir um novo valor e não seguir a sequencia, alterando a ordem escrita no código, acrescentando o novo valor em um lugar diferente da ultima posição, dessa forma nossa aplicação pode considerar uma posição diferente para os valores que já possuímos e com prejudicando assim a integridade dos dados armazenados na aplicação.

Gravando o texto do Enum

Uma abordagem bastante utilizada, é gravar o próprio valor ao invés de gravar o valor sequencial, dessa forma fica mais legível inclusive no banco de dados, e independe da posição em que os valores estão escritos.

Entretanto essa abordagem as vezes não é possível porque você precisa definir a coluna do tipo varchar, porque irá armazenar a string em si, dependendo da modelagem do sistema isso pode não ser possível, em aplicações modernas ou que estejam nascendo isso pode não significar problema, mas para refatorar um código legado que já tem o banco de dados modelado pode complicar um pouco alterar tipo de dados de um banco já populado.

Mas caso seja possível utilizar o próprio valor, vamos mostrar em seguida como fazer.

Para essa abordagem vamos demonstrar utilizando o mesmo exemplo acima:

package com.artefatox.example;

import java.time.LocalDate;

import javax.persistence.Entity;

@Entity
public class Card {
    
    @Id
    private Long id;
    private String holderName;
    private String number;
    private String cvv;
    private LocalDate expiration;
    @Enumerated(EnumType.STRING)
    private Type type;

    //getters and setters
}
Card.java

Basta anotarmos a propriedade que contém a enum com a anotação @Enumerated, passando como parâmetro o tipo EnumType.STRING, isso fará com que no momento da persistência o valor a ser registrado na tabela e o da própria chave do enumerador.

Veja no exemplo abaixo como ficaria na tabela:

Idholder_namenumbercvvexpirationtype
1Kalli Cristina5140 6562 3810 21069492028-04-14CREDIT
Exemplo dos dados no banco de dados.

Agora e quando temos enums mais complexas? enums que possuem atributos, e desejamos persistir o valor de um desses atributos no banco de dados? Vamos mostrar como fazer no próximo tópico.

Gravando valor de um atributos do Enum

No exemplo abaixo temos uma propriedade no nosso enumerador que armazena um código numérico, e queremos que o valor persistido seja justamente o desse atributo.

Uma observação a se fazer é que embora preferimos adotar um valor numérico isso não é obrigatório, poderíamos utilizar qualquer tipo de valor.

Vamos para o exemplo:

package com.artefatox.example;

public enum Type {

    DEBIT(359), 
    CREDIT(422) 
    
    private Integer code;

    private Type(Integer code) {
        this.code = code;
    }

    public Integer getCode() {
        return this.code;
    }

}
Type.java

Para preparar o nosso enumerador para ser salvo, precisamos antes implementar um método que seja possível recuperar a chave do enum pelo valor do atributo code.

Para isso implementamos o método estático ofCode presentes no exemplo abaixo entre as linhas 18 e 21.

package com.artefatox.example;

public enum Type {

    DEBIT(359), 
    CREDIT(422) 
    
    private Integer code;

    private Type(Integer code) {
        this.code = code;
    }

    public Integer getCode() {
        return this.code;
    }
    
    public static Type ofCode(Integer code) {
        Stream.of(Type.values()).filter(type -> type.getCode().equals(code))
        .findFirst().orElseThrow(IllegalArgumentException::new);
    }

}
Type.java

Agora precisamos configurar a conversão de dados no momento da persistência e da leitura da nossa classe.

Vamos criar uma classe TypeAttributeConverter (esse nome não é obrigatório) e precisamos implementar a interface AttributeConverter<T, E> passando no lugar do T o nosso Enum, e no E o tipo do atributo que iremos utilizar.

Essa interface nos força a sobrescrever dois métodos:

  • convertToDatabaseColumn: Esse método será chamado toda vez que nossa classe for ser persistida, é responsável por transformar o enum no valor e tipo que vamos armazenar, a implementação desse método é fácil como temos o atributo dentro do nosso enumerador vamos apenas utilizar o método get para recuperar o valor do atributo.
  • convertToEntityAttribute: Esse método será chamado toda vez que vamos fazer a leitura de um dado do banco e transformar o resultado em classe, como o jpa faz isso automaticamente vai usar esse método para converter o valor vindo do banco para nossa enum, nesse momento que vamos usar a implementação que fizemos dentro do enumerador para capturar a chave através do valor.

Veja o exemplo da nossa implementação:

package com.artefatox.example;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter(autoApply = true)
public class TypeAttributeConverter implements AttributeConverter<Type, Integer> {

    @Override
    public Integer convertToDatabaseColumn(Type attribute) {
        if(attribute == null) {
            return null;
        }
        return attribute.getCode();
    }

    @Override
    public Type convertToEntityAttribute(Integer dbData) {
        if(dbData == null) {
            return null;
        }
        return Type.ofCode(dbData);
    }
    
}
TypeAttributeConverter.java

Após ter sobrescrito os dois métodos precisamos ainda anotar a classe com @Converter(autoApply = true), isso faz com que a implementação fique disponível para ser utilizada toda vez que houver alguma iteração com o banco.

Após essa implementação, utilizando o mesmo fluxo para gravar um cartão no banco, nossa tabela fica com os dados da seguinte forma:

Idholder_namenumbercvvexpirationtype
1Kalli Cristina5140 6562 3810 21069492028-04-14422
Exemplo dos dados no banco de dados.

Outras abordagens

Existem outras abordagens como utilizar o @PrePersist, mas isso envolve ter mais um atributo na sua classe para armazenar o valor que gostaria de gravar no banco, e através de um método anotado com @PrePersist converter o valor.

Entretanto não gosto muito dessa abordagem, devido a ter propriedades a mais na classe que é referência da nossa tabela, além de contribuir para a desorganização do código além de dificultar o entendimento.

Considerações

Para construir aplicações que sejam escaláveis e possíveis de manutenção, é importante construir nossas classes de forma em que o desenvolvedor possa se familiarizar facilmente com a regra de negócio e consiga ler e identificar as funcionalidades presentes no código de maneira simples.

As enums vieram para isso deixar fácil o entendimento dos desenvolvedores além de auxiliar no entendimento das regras de negócio já que os valores ficam explicito no código.

Links uteis

Mauricio Lima
Mauricio Lima

Bacharel em Ciência da Computação, profissional dedicado ao desenvolvimento de software e entusiasta da tecnologia.

Artigos: 65