Trabalhando com JPA e enumerações

Samuel Martins Delfim 21 de junho de 2010 4
Trabalhando com JPA e enumerações

Olá pessoal,

A bastante tempo não escrevo nada, mas hoje surgiu um assunto que acho ser uma pedra no sapato de vários desenvolvedores e arquitetos.

Enumerações, como o nome indica, são listas de valores associados a um conceito. Em java enumerações são subclasse da classe java.lang.Enum. Trabalhar com enumeração facilita bastante o trabalho do desenvolvedor, uma vez que fornece uma maneira de trabalhar com uma lista de valores finitas e trabalhar com tipos fortes em java.

Vejamos um exemplo simples de como isto pode ser um problema em sistemas.

public enum Sexo {
    MASCULINO, FEMININO;
}

Ao realizar o mapeamento desta enumeração com JPA 1.0 utilizamos normalmente a anotação @Enumerated. Com esta anotação sem nenhum parâmetro ou passando o parâmetro EnumType.ORDINAL, o valor a ser persistido no banco seria:

MASCULINO.ordinal()   == 0
FEMININO.ordinal() == 1

Uma das desvantagens desta abordagem é o problema do valor persistido não ser significante para o negócio, o que gera a necessidade de dicionário de dados. Outra desvantagem é o fato de não poder adicionar novos valores no começo e no meio da enumeração uma vez que já existem dados gravados com estes valores no banco.

Outra abordagem seria utilizar a anotação @Enumerated passando como parâmetro o EnumType.STRING. Com esta abordagem o valor a ser persistido passa a ser o método name da enumeração que é mostrado abaixo:

MASCULINO.name = "MASCULINO"
FEMININO.name = "FEMININO"

Esta abordagem também possui alguns problemas. O maior deles é a briga que vc terá com qualquer DBA a respeito do que deveria ser gravado no campo. O que qualquer DBA falará é que deveria ser gravado no banco os valores ‘M’ ou ‘F’ para ter menos gasto de memória e melhor indexação do campo.

Uma abordagem que acho a melhor seria gravar no banco o valor ‘M’ ou ‘F’, mas continuar trabalhando com a enumeração com os nomes MASCULINO e FEMININO.

Uma maneira de realizarmos esta modificação no código para funcionar desta maneira é mostrada abaixo:

Enumeração:

public enum SexoEnum {
	MASCULINO("M"), FEMININO("F");

	private final String value;

	SexoEnum(String value) {
		this.value = value;
	}

	public static SexoEnum fromValue(String value) {
		if (value != null) {
			for (SexoEnum sexo : values()) {
				if (sexo.value.equals(value)) {
					return sexo;
				}
			}
		}
		throw new IllegalArgumentException("Sexo invalido: " + value);
	}

	public String toValue() {
		return value;
	}
}

Na classe a ser mapeada o código ficaria próximo ao exemplo abaixo:

@Entity
public class Pessoa {

	@Column(length = 1)
	private String sexo;

	@Transient
	public SexoEnum getSexoEnum() {
		return SexoEnum.fromValue(sexo);
	}

	public void setSexoEnum(SexoEnum sexoEnum) {
		this.sexo = sexo.toValue();
	}
}

Mapeando desta maneira trabalhamos então com o melhor dos dois mundos. Outra maneira bastante interessante de realizar o mapeamento seria a utilização do UserType do Hibernate, mas falamos disto em outro post.

Mais informações podem ser encontradas em http://www.vineetmanohar.com/2010/01/3-ways-to-serialize-java-enums.

É isto aí pessoal. Qualquer problema avisem…

4 Comentários »

  1. Renan Reis 25 de junho de 2010 de 1:29 pm - Reply

    Perfeita sua abordagem !!!
    como vc ja usa a anotaçao no campo, entao realmente seria necessaria a anotaçao transiente no metodo ?

    • admin 2 de julho de 2010 de 12:31 am - Reply

      Olá Rena,

      Veja que o método deve ser transiente, pois ele não tem o mesmo nome do campo persistido. Este método apenas abstrai o campo persistido. Qualquer problema avise…

  2. Ricardo 30 de maio de 2012 de 8:50 am - Reply

    Samuel,

    E em casos onde já tenho uma base criada onde o campo onde quero aplicar o enum é númerico?

    Por exemplo:
    1 (Carro), 2(Caminhão)…

  3. samuelmd 11 de junho de 2012 de 10:45 pm - Reply

    public enum Sexo {
    MASCULINO, FEMININO;
    }

    Ao realizar o mapeamento desta enumeração com JPA 1.0 utilizamos normalmente a anotação @Enumerated. Com esta anotação sem nenhum parâmetro ou passando o parâmetro EnumType.ORDINAL, o valor a ser persistido no banco seria:

    MASCULINO.ordinal() == 0
    FEMININO.ordinal() == 1

    Você tem que fazer deste jeito.

Deixe uma resposta »