Trabalhando com JPA e enumerações

21/06/2010 | Por Samuel Delfim | Categoria: DESENVOLVIMENTO JAVA

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.

1
2
3
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:

1
2
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:

1
2
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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…

Tags: , ,

2 comments
Comente! »

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

  2. 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…

Deixar comentário