enums by teach4uin 569 views
1. Enums em Java: utilização e persistência Jadson José dos Santos jadsonjs@gmail.com Nas primeiras versões da linguagem Java, a única forma que existia para se utilizarconstantes enumeradas era através de tipos primitivos: public static final int STATUS_ALUNO_APROVADO = 0; public static final int STATUS_ALUNO_REPROVADO = 1; public static final int STATUS_ALUNO_EM_RECUPERACAO = 2; private int statusAluno = STATUS_ALUNO_APROVADO; Existem alguns problemas com essa abordagem. Abaixo, enumeram-se apenas três. 1- pode-se atribuir qualquer valor inteiro para a variável statusAluno, levando o aluno para ostatus inconsistente. 2 – Não existe uma representação textual direta para as constantes, isto é, ao tentar imprimi-las, será exibido um número que não tem significado para o usuário. 3 – É preciso utilizar prefixos nas constantes para indicar a quem elas pertencem. Para solucionar esses problemas, a linguagem Java disponibilizou uma maneira maispoderosa de se definir constantes enumeradas, os enums. public enum StatusAluno { APROVADO, REPROVADO, EM_RECUPERACAO }; private StatusAluno status = StatusAluno.APROVADO ; Com a declaração acima, os três problema citados são solucionados. Somente um dos trêsstatus declarados pode ser atribuídos à variável status. Ao tenta-se imprimir o valor da variável, onome declarado da constante será impresso, não o seu valor numérico. System.out.println(status); // imprime "APROVADO" Os namespace das constantes estão bem definidos dentro do enum StatusAluno. Mas enums são ainda mais poderosos, como as classes, eles podem conter construtores,declarações de outras variáveis e métodos. Para demostrar o poder do uso dos enums, considere oexemplo da representação da situação de um usuário em um sistema de biblioteca. A princípio,vamos utilizar tipos primitivos para modelar a situação dos usuários.public class ConstantesBiblioteca { // Constantes da situação do Usuário. /** Situação quando o usuário não possui empréstimos em atraso e não está suspenso. */ public static final int SITUACAO_USUARIO_SEM_PENDENCIAS = 0; /** Situação quando o usuário possui empréstimos mas não estão atrasados.*/ public static final int SITUACAO_USUARIO_POSSUI_EMPRESTIMOS_ATIVOS = 1; /** Situação quando o usuário possui empréstimos e eles estão atrasados.*/
2. public static final int SITUACAO_USUARIO_POSSUI_EMPRESTIMOS_ATRASADOS = 2; /** Situação quando o usuário está suspenso. */ public static final int SITUACAO_USUARIO_ESTA_SUSPENSO = 3; /** Situação quando o usuário está multado. */ public static final int SITUACAO_USUARIO_ESTA_MULTADO = 4; /** Situação quando o usuário está com empréstimo atrasados. */ public static final int SITUACAO_USUARIO_ESTA_BLOQUEADO = 5;} Vamos imaginar que queremos mostrar essas situações para o operador do sistema escolher,em um relatório, quais usuários da biblioteca em uma determinada situação, ele deseja visualizar nomomento. É necessário criar uma listagem para o operador escolher. Essa listagem precisa conter ovalor a ser buscado e a descrição a ser mostradas ao operador. Para pode realizar essa operação,temos que criar uma entidade extra no sistema, que contenha a valor e a descrição de cada possívelsituação do usuário.public class SituacaoUsuarioBiblioteca { /** Retorna o valor inteiro que representa a situação. */ private int situacao = -1; /** Descreve a situação que o usuário se encontra */ private String descricaoSituacao = null; /** * As possíveis situações de um usuário da biblioteca, preenchidas com descrições básicas. */ public static final Set<SituacaoUsuarioBiblioteca> POSSIVEIS_SITUACAO_DO_USUARIO = new HashSet<SituacaoUsuarioBiblioteca>( Arrays.asList(new SituacaoUsuarioBiblioteca[] { new SituacaoUsuarioBiblioteca( ConstantesBiblioteca.SITUACAO_USUARIO_SEM_PENDENCIAS, "Sem Pendências"), new SituacaoUsuarioBiblioteca( ConstantesBiblioteca.SITUACAO_USUARIO_ESTA_SUSPENSO, "Suspenso"), new SituacaoUsuarioBiblioteca( ConstantesBiblioteca.SITUACAO_USUARIO_ESTA_MULTADO, "Multado"), new SituacaoUsuarioBiblioteca( ConstantesBiblioteca.SITUACAO_USUARIO_POSSUI_EMPRESTIMOS_ATIVOS, "Possui Empréstimos Ativos"), new SituacaoUsuarioBiblioteca( ConstantesBiblioteca.SITUACAO_USUARIO_POSSUI_EMPRESTIMOS_ATRASADOS, "Possui EmpréstimosAtrasados"), new SituacaoUsuarioBiblioteca( ConstantesBiblioteca.SITUACAO_USUARIO_ESTA_BLOQUEADO, "Bloqueado") }) ); }} Utilizando um enum para modelar a situação do usuário, poderíamos ter algo como:public enum SituacaoUsuarioBiblioteca { SEM_PENDENCIA(1, "Usuário Sem Pendências"), POSSUI_EMPRESTIMOS_ATIVOS(2, "Usuário Possui Empréstimos Ativos"), ESTA_SUSPENSO(3, "Usuário está Suspenso"), ESTA_MULTADO(4, "Usuário está Multado"), POSSUI_EMPRESTIMOS_ATRASADOS(5, "Usuário Possui Empréstimos Atrasados"), ESTA_BLOQUEADO(6, "Usuário está Bloqueado"); /** * O valor que representa a situação
3. */ private int valor; /** * A descrição da situação do usuário */ private String descricao; private SituacaoUsuarioBiblioteca(int valor, String descricaoResumida){ this.valor = valor; this.descricao = descricao; } public boolean isSituacaoImpedeEmprestimos(){ if(this.equals(ESTA_SUSPENSO) || this.equals(ESTA_MULTADO) || this.equals(POSSUI_EMPRESTIMOS_ATRASADOS) || this.equals(ESTA_BLOQUEADO) ) return true; return false; } /** * Usado os relatórios */ public static final Set<SituacaoUsuarioBiblioteca> POSSIVEIS_SITUACAO_DO_USUARIO = new HashSet<SituacaoUsuarioBiblioteca>( Arrays.asList( SituacaoUsuarioBiblioteca.SEM_PENDENCIA, SituacaoUsuarioBiblioteca.ESTA_SUSPENSO, SituacaoUsuarioBiblioteca.ESTA_MULTADO, SituacaoUsuarioBiblioteca.POSSUI_EMPRESTIMOS_ATIVOS, SituacaoUsuarioBiblioteca.POSSUI_EMPRESTIMOS_ATRASADOS SituacaoUsuarioBiblioteca.BLOQUEADO ) );} A variável estática POSSIVEIS_SITUACAO_DO_USUARIO do próprio enum já nos retornaria a valor e adescrição das possíveis situações do usuário, sem a necessidade de criar uma nova entidade pararepresentar isso. O que deixa nosso código mais enxuto e legível. Em vez de fazemos comparaçõesde inteiros como mostrado abaixo: int situacao = ConstantesBiblioteca.SITUACAO_USUARIO_ESTA_MULTADO; if(situacao == ConstantesBiblioteca.SITUACAO_USUARIO_ESTA_SUSPENSO || situacao == ConstantesBiblioteca.SITUACAO_USUARIO_ESTA_MULTADO || situacao == ConstantesBiblioteca.SITUACAO_USUARIO_POSSUI_EMPRESTIMOS_ATRASADOS || situacao == ConstantesBiblioteca.SITUACAO_USUARIO_ESTA_BLOQUEADO ){ } A própria constante do enum pode disponibilizar um método que mostre se ela é umasituação que permite ao usuário realizar empréstimos na biblioteca, por meio do métodoisSituacaoImpedeEmprestimos(). Assim nosso código de verificação se resumira a: SituacaoUsuarioBiblioteca situacao = SituacaoUsuarioBiblioteca.ESTA_MULTADO; if(situacao.isSituacaoImpedeEmprestimos()){ } A simplificação do código também poderia ser utilizado em páginas web:
4. <td> <c:if test="${not situacao.situacaoImpedeEmprestimos}"> <h:outputText value="#{situacao.descricao}" style="color:green;"/> </c:if> <c:if test="${ situacao.situacaoImpedeEmprestimos}"> <h:outputText value="#{situacao.descricao}" style="color:red;"/> </c:if> </td>Persistência Um problema que pode-se encontrar com a utilização de enums é com relação a suapersistência. Como os dados das constantes enumeradas são definidos pelos usuário, não há comoos frameworks de mapeamento objeto relacional saberem de ante mão, como persisti-los. É precisoutilizar soluções específicas para a implementação JPA utilizada, como por exemplo a interfaceUserType do Hibernate. Para os casos mais comuns, onde o valor das constantes enumeradas representam númerosinteiros e apenas esse valor precisa ser persistido, é possível utilizar o mapeamento ORDINAL.Esse mapeamento utiliza como valor do enum a ser persistido, o valor sequencial da ordem em queas constantes dos enum são declaradas. Por exemplo, verificaremos o código da entidadeReservaMaterialBiblioteca, que represente uma reserva de um material feita em um sistema debiblioteca.public class ReservaMaterialBiblioteca { public enum StatusReserva { SOLICITADA(0), EM_ESPERA(1), CANCELADA(2), CANCELADA_MANUALMENTE(3), CONCRETIZADA(4); private int valor; private StatusReserva(int valor){ this.valor = valor; } /** Método chamado pelo hibernate para pegar o valor do enum nas consultas HQLs */ @Override public String toString() { return String.valueOf(valor); } } /** * O status da reserva */ @Enumerated(EnumType.ORDINAL) @Column(name="status", nullable=false) private StatusReserva status = StatusReserva.SOLICITADA; /** * Retorna os status que são considerados ativos. * * @return */ public static StatusReserva[] getReservasAtivas(){ return new StatusReserva[]{StatusReserva.SOLICITADA, StatusReserva.EM_ESPERA}; }}
5. A entidade ReservaMaterialBiblioteca contém a declaração do enum público1 StatusReserva quepossui uma variável valor que representa o valor da constantes do enum, um construtor que recebe ovalor da constante e um médodo toString() que retorna a representação textual do valor da constantedo enum. Essa entidade possui também uma variável status do tipo StatusReserva que é mapeada coma anotação @Enumerated com o tipo EnumType.ORDINAL. Assim no momento de realizar a persistência, O framework de mapeamento objetorelacional vai persistir o valor ordinal das constantes ( começado do valor zero ) na coluna2correspondente à variável status no banco. A representação textual do enum StatusReserva é retornada no método toString(), essarepresentação textual é usada para gerar as consultas à base de dados. Um exemplo de como realizar consultas utilizando enuns está descrito no métodofindAllReservasAtivas() mostrado abaixo: /** * <p>Retorna a listagem de todas as reservas ativas. </p> * * @param dataSolicitacao * @param idTitulo * @return */ public List<ReservaMaterialBiblioteca> findAllReservasAtivas(int idTitulo, Date dataSolicitacao){ final String projecao= " r.id, r.dataPrevisaoRetiradaMaterial "; StringBuilder hql = new StringBuilder(" SELECT "+projecao); hql.append(" FROM ReservaMaterialBiblioteca r "); hql.append(" WHERE r.tituloReservado.id = :idTitulo "); hql.append(" AND r.status in " +HibernateUtils.gerarStringIn( ReservaMaterialBiblioteca.getReservasAtivas() ) ); hql.append(" AND r.dataSolicitacao > :dataSolicitacao"); hql.append(" ORDER BY r.dataSolicitacao, r.id "); Query q = getSession().createQuery(hql.toString()); q.setInteger("idTitulo", idTitulo); q.setTimestamp("dataSolicitacao", dataSolicitacao); @SuppressWarnings("unchecked") List<Object[]> linhas = q.list(); return new ArrayList<ReservaMaterialBiblioteca> ( HibernateUtils.parseTo(linhas, projecao, ReservaMaterialBiblioteca.class, "r")); } Esse método faz uso do método estático getReservasAtivas() que retorna um array de todas asreservas que são consideradas ativas no sistema. O valor utilizado na consulta gerada, será o valorda representação textual do enum retornado pelo método toString(), que é a representação textual davariável valor do enum, que por sua vez, contém o mesmo valor ordinal persistido. Logo, aconsulta acima gerará um SQL do tipo: AND r.status in (1, 2) , retornando as reservas com statusdesejado.1 É necessário ser público para o framework JPA conseguir acessá-lo.2 No banco, a coluna status precisa ser de um tipo que aceite valores inteiros.
6. Referênciashttp://download.oracle.com/javase/1.5.0/docs/guide/language/enums.htmlhttp://www.hibernate.org/265.htmlhttp://www.guj.com.br/java/144147-persistencia-de-enum-enumtypeordinalhttp://www.cs.wustl.edu/~schmidt/CACM-frameworks.htmlhttp://www.java2s.com/Code/Java/JPA/EnumeratedEnumTypeORDINAL.htm