[RESOLVIDO] Hibernate com lista de Enumerations

Olá Desenvolvedores, estou enfrentando um problema no desenvolvimento de um sistema aqui e as diversas soluções que encontrei na internet não sanaram minha dúvida.

A questão é relacionada a Enums, especificamente uma lista de Enums, List<Enum>. Andei lendo que não é necessário gravar Enums no banco pelo fato de ser constantes. Porém eis minha dúvida, em determinado momento o usuário informará quais valores da List<Enum> ele quer vincular a determinado objeto, e futuramente terei que recuperar esses valores para apresenta-los em um relatório.

Adentrando no contexto da minha aplicação (ambiente agrícola), existe as inspeções de pragas (vistorias nos pomares afim de verificar a existência de pragas), com isso existe uma determinada praga que apresenta alguns estágios de desenvolvimento, são eles: OVO, LARVA, NINFA e ADULTO. Estes quatro valores constituem minha Enum, e possuo uma entidade InspecaoPraga que possui como atributo uma lista desses enum, sendo que o usuário informará a existência de um, alguns ou todos os estágios.

Atualmente, meu código está assim:
Enum:

public enum EstagioPraga {

    OVO("O"),
    LARVA("L"),
    NINFA("N"),
    ADULTA("A");

    private final String sigla;

    EstagioPraga(final String sigla) {
        this.sigla = sigla;
    }

    public String getSigla() {
        return sigla;
    }

}

Entidade:

public class InspecaoPraga implements Serializable {
...

    @ElementCollection(fetch = FetchType.EAGER, targetClass = EstagioPraga.class)
    @Enumerated(EnumType.STRING)
    @CollectionTable(name = "estagioPraga_inspecaoPraga")
    @Column(name = "estagios_praga")
    private Collection<EstagioPraga> estagiosEncontrados;

.....
}

Minha intenção é criar um <p:selectManyCheckbox> e exibir os valores da Enum, deixando o usuário escolher quais valores encontrou, depois preciso exibir isso em um relatório, ou seja, preciso recuperar os valores selecionados no <p:selectManyCheckbox>.

Porém, não consigo, com as annotations utilizadas o hibernate cria uma tabela no banco com o nome especificado na anotação @CollectionTable mas ao persistir um objeto InspecaoPraga a tabela criada não é populada e consequentemente não consigo recuperar os valores da lista para aquele objeto.

Alguém poderia me auxiliar em como recuperar esses valores ? E além disso esclarecer se essa List<Enum> deve ou não ser persistida no banco ??

Grato, desde já!

Alguém ?

Opa!

Tenta colocar um Cascade ai.

@ElementCollection(targetClass = Days.class)
@CollectionTable(name = "TINVITE_TYPE_DAY", joinColumns = @JoinColumn(name = "INVITE_TYPE", referencedColumnName = "ID"))
@Enumerated(EnumType.STRING)
@Column(name = "DAY", length = 20)
private Set<Days> days;

Basicamente é isso, mas tente mapear um java.util.List

Valeu pelo suporte @sidronio mas em nenhuma das anotações me permite colocar o cascade.

Agradeço a você também @DarkElf porém, já tentei utilizar estas anotações que você indicou em minha list. E ainda não consegui.

Só uma observação: tentei colocar direto o atributo estagiosEncontrados da entidade InspecaoPraga na página xhtml (coloquei no value de <p:selectManyCheckbox>), então eu consigo submeter o formulário sem erro. Porém, não consigo recuperar esses valores numa futura consulta ao banco (e olhando as tabelas criadas pelo hibernate nenhuma recebe esse valores).

Quando crio um List<ComOTipoDoEnum> e tento seta-lo na InspecaoPraga no momento do submit do form me deparo com o erro:

java.lang.String cannot be cast to com.delago.matta.enums.EstagioPraga

Ja alterei o tipo das listas para string; ja tentei passar a sigla do enum ao invés de passar o enum todo e nada adiantou :confused:

Seu problema não está nas anotações então, o problema está no JSF.

Mostra como está sendo feito na página .xhtml

collectionType=“java.util.HashSet” tente adicionar esse atributo ao seu selectManyCheckbox, se não funcionar será necessário adicionar também um converter

@DarkElf segue o página xhtml, ela ta meio grande então vou tirar alguns inputs e deixar apenas a parte que estamos discutindo! Vale ressaltar que no momento estou tentando manipular essa lista de enum por List<> e não mais Collection<>.

JSF:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:c="http://java.sun.com/jsp/jstl/core"
                xmlns:p="http://primefaces.org/ui" template="../main.xhtml">

    <ui:define name="content">
        <div style="width: fit-content;">
            <h:form id="fmLancaInspec">
                <p:focus />
                <div style="margin: 0 auto; width: fit-content;">
                    <h2>Lançamento de Inspeção</h2>
                </div>
                <!--Cadastro de praga -->        
                <p:panelGrid>
                   <p:row>
                        <p:column colspan="8">
                            <p:selectBooleanCheckbox id="boolCheckLeprose" required="#{param['fmLancaInspec:btnConfirm']==false}"
                                                     value="#{lancamentoPlanilhaInspecao.leprose}" >
                                <f:ajax render="novas"/> <!--tentativa de habilitar inputs apenas quando checkbox estiver clicado. erro!-->
                            </p:selectBooleanCheckbox>
                            <p:spacer width="6"/>
                            <p:outputLabel value="Leprose" for="boolCheckLeprose" />

                            <br/>

                            <p:spacer width="6"/>
                            <p:outputLabel value="Plantas Novas >>" />
                            <p:spacer width="10"/>
                            <p:outputLabel id="qtdElevadoNovaLeprose" value="Qtde. + elevado: " for="qtdMaisElevadoNovaLeprose"/>
                            <p:inputText id="qtdMaisElevadoNovaLeprose" value="#{lancamentoPlanilhaInspecao.leprose_qntdeElevado_nova}" style="width: 60px;"/>
                            <p:spacer width="5"/>
                            <p:outputLabel id="qtdTotalNovaL" value="Qtde. Total: " for="qtdTotalNovasLeprose"/>
                            <p:inputText id="qtdTotalNovasLeprose" value="#{lancamentoPlanilhaInspecao.leprose_qntdeTotal_nova}" style="width: 60px;"/>

                            <br/>

                            <p:spacer width="6"/>
                            <p:outputLabel value="Plantas Velhas >>" />
                            <p:spacer width="8"/>
                            <p:outputLabel id="qtdElevadoVelhas" value="Qtde. + elevado: " for="qtdMaisElevadoVelhasLeprose"/>
                            <p:inputText id="qtdMaisElevadoVelhasLeprose" value="#{lancamentoPlanilhaInspecao.leprose_qntdeElevado_velha}" style="width: 60px;"/>
                            <p:spacer width="5"/>
                            <p:outputLabel id="qtdTotalVelhaL" value="Qtde. Total: " for="qtdTotalVelhasLeprose"/>
                            <p:inputText id="qtdTotalVelhasLeprose" value="#{lancamentoPlanilhaInspecao.leprose_qntdeTotal_velha}" style="width: 60px;"/>

                            <br/>
                            <!--É aqui  a parte que renderizo as opções de Enum a serem escolhidos-->
                            <p:outputLabel value="Estágios encontrados:"/>
                            <p:selectManyCheckbox value="#{lancamentoPlanilhaInspecao.estagiosEncontrados}" layout="grid" columns="4">
                                <f:selectItems value="#{lancamentoPlanilhaInspecao.estagiosExistentes}" var="#{e}" itemLabel="#{e}" itemValue="#{e}"/>
                                <p:spacer width="5"/>
                            </p:selectManyCheckbox>
                        </p:column>
                    </p:row>
                    <!--********* FIM *********-->
                 </p:panelGrid>
                <div style="margin: 0 auto; width: fit-content;">
                    <p:commandButton id="btnConfirm" class="btn btn-success btn-md" value="Cadastrar" icon="fa fa-check" action="#{lancamentoPlanilhaInspecao.lancar()}" ajax="false"/>
                    <p:commandButton id="btnCancel" class="btn btn-danger btn-md" value="Cancelar" icon="fa fa-ban" action="#{lancamentoPlanilhaInspecao.cancelar()}" />
                </div>
            </h:form>
        </div>
    </ui:define>
</ui:composition>

O itemValue você pode remover, não é necessário.

Você precisa adicionar qual o tipo da coleção, collectionType=“java.util.ArrayList”

@DarkElf cara, te agradeço muito o suporte, ta me ajudando mesmo… mas ainda não obtive o resultado esperado. O formulário foi submetido, mas ainda nao consigo recuperar a lista de enum e procurando no banco parece que a mesma ainda não foi salva.

segue como esta o xhtml:

                    <p:outputLabel value="Estágios encontrados:"/>
                    <p:selectManyCheckbox value="#{lancamentoPlanilhaInspecao.estagiosEncontrados}" layout="grid" columns="4" class="java.util.ArrayList">
                        <f:selectItems value="#{lancamentoPlanilhaInspecao.estagiosExistentes}" var="#{e}" itemLabel="#{e}"/>
                        <p:spacer width="5"/>
                    </p:selectManyCheckbox> 

Note que em nenhuma tag eu consigo por o atributo collectionType=“java.util.ArrayList”

Fiz algo parecido entre duas entidades, Usuario e Permissao:

@CollectionTable(name="usuario_permissoes", joinColumns = { @JoinColumn(name="usuario_codigo")}) @ElementCollection(targetClass=Permissao.class, fetch=FetchType.EAGER) @Enumerated(EnumType.STRING) @Column(name="permissao_codigo") private List<Permissao> permissoes = new ArrayList<Permissao>();

JSF:
`


                                            <label for="select-permissao">Permissões<span class="required">*</span></label>
                                              <h:selectManyCheckbox 
                                                  id="select-permissao"
                                                  value="#{cadastroUsuarioBean.usuario.permissoes}"
                                                  required="true"
                                                  requiredMessage="Ao menos uma permissão deve ser concedida."
                                                  converter="permissaoConverter">
                                                  
                                                  <f:attribute name="collectionType" value="java.util.ArrayList"/>
                                                  
                                                  <f:selectItems
                                                      value="#{cadastroUsuarioBean.permissoes}"
                                                      var="permissao"
                                                      itemLabel="#{permissao.descricao}"
                                                      itemValue="#{permissao}"/>
                                                  
                                                  <f:ajax
                                                      event="select"
                                                      render="m_select-permissao"/>
                                              </h:selectManyCheckbox>  
                                              
                                            <h:message 
                                                   id="m_select-permissao" 
                                                   for="select-permissao"
                                                   class="alert-danger fade in"/>                                              
                                              
                                        </div>
                                    </div>        

`

Espero te ajudar. Qualquer coisa só falar.

Valeu pela ajuda @sidronio, vou me basear no seu aqui e ver como fica.

De antemão poderia me explicar uma coisa ? entendo que permissoes é um atributo de sua classe Usuario e consequentemente uma coluna na tabela “usuario” com referência para outra tabela, qual a necessidade de no proprio atributo instancia-lo ? Nas minhas classes model não instanciei nenhum atributo ate hoje.

Outra coisa, poderia publicar o código do permissaoConverter que você utiliza ?

Testei aqui e ainda não consegui salvar a lista, essa sua List<Permissao> é persistida no banco né ?!

Grato.

Só vou ter acesso a esse código amanhã. Vejo e posto aqui.

Mas posso lhe adiantar que a tabela de usuario_permissao tem o código do usuário e o valor String da permissão.

O converter implementa a interface FacesConverter, básica, que recebe uma String e retorna o valueOf se não me engano. O outro método recebe a Permissao e devolve o toString(). Não sei se confundi os métodos mas é muito simples.

Abç.

Meus amigos, é com satisfação que venho lhes comunicar que encontrei a solução para o problema!

Agradeço os amigos que tiraram um pouco de seu tempo para prestar suporte aqui no fórum @sidronio @DarkElf.

Na solução realmente precisei de um converter, e na página xhtml não coloquei diretamente o atributo da classe, mas sim criei um atributo no bean e depois setei o atributo da classe com o atributo do bean.

Abaixo seguem os códigos utilizados:

SelectManyCheckBox (na página xhtml):

                    <p:selectManyCheckbox value="#{lancamentoPlanilhaInspecao.estagiosEncontrados}" 
                                          layout="grid" columns="4" converter="estagioPragaConverter">
                        <f:attribute name="collectionType" value="java.util.ArrayList"/>
                        <f:selectItems value="#{lancamentoPlanilhaInspecao.estagiosExistentes}" var="#{ep}" itemLabel="#{ep}" itemValue="#{ep}"/>
                    </p:selectManyCheckbox>

Converter:

@FacesConverter(value = "estagioPragaConverter")
public class EstagioPragaConverter implements Converter {

    private static final String ATTRIBUTE_ENUM_TYPE = "GenericEnumConverter.enumType";

    @Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        Class<Enum> enumType = (Class<Enum>) component.getAttributes().get(ATTRIBUTE_ENUM_TYPE);
        try {
            return Enum.valueOf(enumType, value);
        } catch (Exception e) {
            throw new ConverterException(new FacesMessage("Exception:" + e.getMessage() + "\n" + "Value is not an enum of type: " + enumType));
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (value instanceof Enum) {
            component.getAttributes().put(ATTRIBUTE_ENUM_TYPE, value.getClass());
            return ((Enum<?>) value).name();
        } else {
            throw new ConverterException(new FacesMessage("Value is not an enum: " + value.getClass()));
        }
    }

}

Quando vou persistir no banco, antes de chamar o método da camada DAO, eu invoco outro método do bean que armazena os valores do formulário nos objetos InspecaoPraga (praticamente chamo os setters dos atributos) e depois associo os objetos InspecaoPraga no objeto Inspecao correspondente.

Mais uma vez, agradeço a todos.