LazyInitializationException с управляемым компонентом CDI и сеансовым компонентом с отслеживанием состояния

У меня есть управляемый компонент CDI (компонент с аннотацией @Named, который используется в JSF), в который введен сеансовый компонент с отслеживанием состояния. Этот сессионный компонент похож на службу, у него есть менеджер сущностей (аннотированный @PersistenceContext(type= PersistenceContextType.EXTENDED)) и предоставляющий методы come для управления некоторыми сущностями. Эти объекты находятся в управляемом компоненте, который имеет ConversationScoped. Затем JSF вызывает метод управляемого компонента, а управляемый компонент вызывает некоторый метод «службы» (сеансовый компонент с отслеживанием состояния). Я не знаю, лучший ли это дизайн, но он работал хорошо. Но есть сущность, у которой есть коллекции, которые нужно получить с помощью LAZY. И когда я впервые открываю страницу, кажется, что она работает хорошо, но когда я пытаюсь нажать любую кнопку или выполнить какое-либо действие, у меня возникает LazyInitializationException. У кого-нибудь есть совет? Я не знаю, есть ли что-то не так. Я поместил сеансовый компонент как с сохранением состояния, а контекст сохранения был расширен. Этот сеансовый компонент внедряется в управляемый компонент, содержащий сущности. Почему он бросает это исключение? Как можно было закрыть управляющего сущностью?

Это код сущности, которая попала в беду:

@Entity
public class ParametrosVingentes implements Serializable {

    public static final String ID = "settings";

    private static final long serialVersionUID = 1L;

    @Id
    private String id;
    @OneToOne
    private ValorHora valorHora;
    @OneToMany
    @JoinTable(
            name="BuscaSistecVingentes",
            joinColumns = @JoinColumn( name="parametros_vingentes_fk"),
            inverseJoinColumns = @JoinColumn( name="agendamento_fk")
    )
    private List<AgendamentoBuscaSistec> agendamentosBuscaSistec;
    @OneToMany
    @JoinTable(
            name="ExportacaoZeusVingentes",
            joinColumns = @JoinColumn( name="parametros_vingentes_fk"),
            inverseJoinColumns = @JoinColumn( name="agendamento_fk")
    )
    private List<AgendamentoExportacaoZeus> agendamentosExportacaoZeus;
    @OneToMany
    @JoinTable(
            name="ImportacaoZeusVingentes",
            joinColumns = @JoinColumn( name="parametros_vingentes_fk"),
            inverseJoinColumns = @JoinColumn( name="agendamento_fk")
    )
    private List<AgendamentoImportacaoZeus> agendamentosImportacaoZeus;

    public ParametrosVingentes() {
    this.id = ID;
    }
// getters and setters...

Это Stateful Session Bean:

@Stateful
@LocalBean
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class ParametrosService implements Serializable {

    @PersistenceContext(type= PersistenceContextType.EXTENDED)
    private EntityManager entityManager;

    public void cadastrar(ValorHora valorHora){
        ValorHoraDao valorHoraDao = new ValorHoraDao(entityManager);
        valorHoraDao.salvar(valorHora);
    }

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
        public List<ValorHora> listarValorHora(){
        ValorHoraDao valorHoraDao = new ValorHoraDao(entityManager);
        return valorHoraDao.getAll();
    }

    public boolean excluir(ValorHora valorHora){
        if(valorHora.getRemessas() != null && !valorHora.getRemessas().isEmpty()){
            return false;
        }
        ValorHoraDao valorHoraDao = new ValorHoraDao(entityManager);
        valorHoraDao.remover(valorHora);
        return true;
    }

    public void cadastrar(AgendamentoBuscaSistec agendamentoBuscaSistec){
        AgendamentoBuscaSistecDao agendamentoBuscaSistecDao = new AgendamentoBuscaSistecDao(entityManager);
        agendamentoBuscaSistecDao.salvar(agendamentoBuscaSistec);
    }

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List<AgendamentoBuscaSistec> listarAgendamentoBuscaSistec(){
        AgendamentoBuscaSistecDao agendamentoBuscaSistecDao = new AgendamentoBuscaSistecDao(entityManager);
        return agendamentoBuscaSistecDao.getAgendamentos();
    }

    public void excluir(AgendamentoBuscaSistec agendamentoBuscaSistec){
        AgendamentoBuscaSistecDao agendamentoBuscaSistecDao = new AgendamentoBuscaSistecDao(entityManager);
        agendamentoBuscaSistecDao.remover(agendamentoBuscaSistec);
    }

    public void cadastrar(AgendamentoExportacaoZeus agendamentoExportacaoZeus){
        AgendamentoExportacaoZeusDao agendamentoExportacaoZeusDao = new AgendamentoExportacaoZeusDao(entityManager);
        agendamentoExportacaoZeusDao.salvar(agendamentoExportacaoZeus);
    }

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
        public List<AgendamentoExportacaoZeus> listarAgendamentoExportacaoZeus(){
        AgendamentoExportacaoZeusDao agendamentoExportacaoZeusDao = new AgendamentoExportacaoZeusDao(entityManager);
        return agendamentoExportacaoZeusDao.getAgendamentos();
    }

    public void excluir(AgendamentoExportacaoZeus agendamentoExportacaoZeus){
        AgendamentoExportacaoZeusDao agendamentoExportacaoZeusDao = new AgendamentoExportacaoZeusDao(entityManager);
        agendamentoExportacaoZeusDao.remover(agendamentoExportacaoZeus);
    }

    public void cadastrar(AgendamentoImportacaoZeus agendamentoImportacaoZeus){
        AgendamentoImportacaoZeusDao agendamentoImportacaoZeusDao = new AgendamentoImportacaoZeusDao(entityManager);
        agendamentoImportacaoZeusDao.salvar(agendamentoImportacaoZeus);
    }

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List<AgendamentoImportacaoZeus> listarAgendamentoImportacaoZeus(){
        AgendamentoImportacaoZeusDao agendamentoImportacaoZeusDao = new AgendamentoImportacaoZeusDao(entityManager);
        return agendamentoImportacaoZeusDao.getAgendamentos();
    }

    public void excluir(AgendamentoImportacaoZeus agendamentoImportacaoZeus){
        AgendamentoImportacaoZeusDao agendamentoImportacaoZeusDao = new AgendamentoImportacaoZeusDao(entityManager);
        agendamentoImportacaoZeusDao.remover(agendamentoImportacaoZeus);
    }

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public ParametrosVingentes getParametrosVingentes(){
        return ParametrosUtil.getParametrosVingentes(entityManager);
    }

    public void atualizarParametrosVingentes(ParametrosVingentes parametrosVingentes){
        ParametrosVingentesDao parametrosVingentesDao = new ParametrosVingentesDao(entityManager);
        parametrosVingentes = parametrosVingentesDao.atualizar(parametrosVingentes);
    }

}

Управляемый компонент вызывает метод getParametrosVingentes() сеансового компонента. Я использую статический метод ParametrosUtil для получения (если он существует) или создания (если он не существует) ParametrosVingentes. Это потому, что необходимо, чтобы в приложении был только один ParametrosVingentes. Это bean-компонент, параметры которого используются другими компонентами. Это код ParametrosUtil:

public class ParametrosUtil {

    public static synchronized ParametrosVingentes getParametrosVingentes(EntityManager entityManager){
        ParametrosVingentesDao parametrosVingentesDao = new ParametrosVingentesDao(entityManager);
        ParametrosVingentes parametrosVingentes = parametrosVingentesDao.buscar(ParametrosVingentes.ID);
        if(parametrosVingentes == null){
            parametrosVingentes = new ParametrosVingentes();
        }
        return parametrosVingentes;
    }

    public static synchronized ParametrosVingentes atualizarParametrosVingentes(ParametrosVingentes parametrosVingentes, EntityManager entityManager){
        ParametrosVingentesDao parametrosVingentesDao = new ParametrosVingentesDao(entityManager);
        return parametrosVingentesDao.atualizar(parametrosVingentes);
    }
}

Это управляемый компонент:

@Named(value = "parametros")
@ConversationScoped
public class Parametros implements Serializable {

    public static final int VISAO_PARAMETROS_VINGENTES = 1;
    public static final int VISAO_VALOR_HORA = 2;
    public static final int VISAO_AGENDAMENTO_SISTEC = 3;
    public static final int VISAO_AGENDAMENTO_EXPORTACAO_ZEUS = 4;
    public static final int VISAO_AGENDAMENTO_IMPORTACAO_ZEUS = 5;

    private int visaoAtual;

    @EJB
    private ParametrosService parametrosService;

    @Inject
    private Conversation conversation;

    private ValorHora valorHora;
    private AgendamentoBuscaSistec agendamentoBuscaSistec;
    private AgendamentoExportacaoZeus agendamentoExportacaoZeus;
    private AgendamentoImportacaoZeus agendamentoImportacaoZeus;

    private List<ValorHora> listaValorHora;
    private boolean listaValorHoraModificada;
    private List<AgendamentoBuscaSistec> listaAgendamentoBuscaSistec;
    private boolean listaAgendamentoBuscaSistecModificada;
    private List<AgendamentoExportacaoZeus> listaAgendamentoExportacaoZeus;
    private boolean listaAgendamentoExportacaoZeusModificada;
    private List<AgendamentoImportacaoZeus> listaAgendamentoImportacaoZeus;
    private boolean listaAgendamentoImportacaoZeusModificada;

    private ParametrosVingentes parametrosVingentes;

    public Parametros() {
        this.visaoAtual = VISAO_PARAMETROS_VINGENTES;
    }

    @PostConstruct
    public void init(){
        this.conversation.begin();
        this.parametrosVingentes = this.parametrosService.getParametrosVingentes();    
    }

    public ParametrosVingentes getParametrosVingentes() {
        return parametrosVingentes;
    }

    public List<ValorHora> getListaValorHora(){
        if(this.listaValorHora == null || this.listaValorHoraModificada){
            this.listaValorHoraModificada = false;
            this.listaValorHora = this.parametrosService.listarValorHora();
        }
        return this.listaValorHora;
    }

    public List<AgendamentoBuscaSistec> getListaAgendamentoBuscaSistec(){
        if(this.listaAgendamentoBuscaSistec == null || this.listaAgendamentoBuscaSistecModificada){
            this.listaAgendamentoBuscaSistecModificada = false;
            this.listaAgendamentoBuscaSistec = this.parametrosService.listarAgendamentoBuscaSistec();
        }
        return this.listaAgendamentoBuscaSistec;
    }

    public List<AgendamentoExportacaoZeus> getListaAgendamentoExportacaoZeus(){
        if(this.listaAgendamentoExportacaoZeus == null || this.listaAgendamentoExportacaoZeusModificada){
            this.listaAgendamentoExportacaoZeusModificada = false;
            this.listaAgendamentoExportacaoZeus = this.parametrosService.listarAgendamentoExportacaoZeus();
        }
        return this.listaAgendamentoExportacaoZeus;
    }

    public List<AgendamentoImportacaoZeus> getListaAgendamentoImportacaoZeus(){
        if(listaAgendamentoImportacaoZeus == null || this.listaAgendamentoImportacaoZeusModificada){
            this.listaAgendamentoImportacaoZeusModificada = false;
            this.listaAgendamentoImportacaoZeus = this.parametrosService.listarAgendamentoImportacaoZeus();
        }
        return this.listaAgendamentoImportacaoZeus;
    }

    public void atualizarParametrosVingentes(){
        this.parametrosService.atualizarParametrosVingentes(this.parametrosVingentes);
    }

    // Other methods

А это JSF:

<p:fieldset>
    <h:panelGrid columns="2">
        <h:outputLabel value="Valor da hora:" for="valorHoraVingente" />
        <p:selectOneMenu id="valorHoraVingente" value="#{parametros.parametrosVingentes.valorHora}">
            <f:selectItem itemLabel="Selecione" itemValue="#{null}" />
            <f:selectItems value="#{parametros.listaValorHora}" />
        </p:selectOneMenu>
        <h:outputLabel value="Agendamento da Busca do Sistec:" for="agendamentoBuscaSistecVingente" />
        <p:selectManyCheckbox id="agendamentoBuscaSistecVingente" value="#{parametros.parametrosVingentes.agendamentosBuscaSistec}">
            <f:selectItem itemLabel="Selecione" itemValue="#{null}" />
            <f:selectItems value="#{parametros.listaAgendamentoBuscaSistec}" />
        </p:selectManyCheckbox>
        <h:outputLabel value="Agendamento da Exportação para o Zeus:" for="agendamentoExportacaoZeusVingente" />
        <p:selectManyCheckbox id="agendamentoExportacaoZeusVingente" value="#{parametros.parametrosVingentes.agendamentosExportacaoZeus}">
            <f:selectItem itemLabel="Selecione" itemValue="#{null}" />
            <f:selectItems value="#{parametros.listaAgendamentoExportacaoZeus}" />
        </p:selectManyCheckbox>
        <h:outputLabel value="Agendamento da Importação para o Zeus:" for="agendamentoImportacaoZeusVingente" />
        <p:selectManyCheckbox id="agendamentoImportacaoZeusVingente" value="#{parametros.parametrosVingentes.agendamentosImportacaoZeus}">
            <f:selectItem itemLabel="Selecione" itemValue="#{null}" />
            <f:selectItems value="#{parametros.listaAgendamentoImportacaoZeus}" />
        </p:selectManyCheckbox>
    </h:panelGrid>
    <p:commandButton value="Atualizar" action="#{parametros.atualizarParametrosVingentes}" update="@form" />
</p:fieldset>

Как вы можете видеть, есть поля, связанные с коллекциями, которые должны быть получены с помощью LAZY.

А это трассировка стека:

WARNING: failed to lazily initialize a collection, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
    at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:122)
    at org.hibernate.collection.PersistentBag.isEmpty(PersistentBag.java:255)
    at javax.faces.component.UIInput.isEmpty(UIInput.java:1257)
    at javax.faces.component.UIInput.validateValue(UIInput.java:1144)
    at javax.faces.component.UISelectMany.validateValue(UISelectMany.java:581)
    at javax.faces.component.UIInput.validate(UIInput.java:967)
    at javax.faces.component.UIInput.executeValidate(UIInput.java:1233)
    at javax.faces.component.UIInput.processValidators(UIInput.java:698)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
    at org.primefaces.component.fieldset.Fieldset.processValidators(Fieldset.java:197)
    at javax.faces.component.UIForm.processValidators(UIForm.java:253)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
    at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1172)
    at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
    at java.lang.Thread.run(Thread.java:722)

Я думаю, что в конце метода getParametrosVingentes службы EntityManager очищается и все сущности отсоединяются. Может ли это быть проблемой?

Я запускаю это на Glassfish 3, используя Mojarra 2.1.2 и EJB 3.1. Спасибо за любую помощь.


person Fernando Camargo    schedule 10.01.2012    source источник
comment
Я думаю, что здесь важны некоторые из других методов Parametros, особенно atualizarParametrosVingentes. Кроме того, поскольку ошибка возникает во время проверки выбора, может быть важен код, управляющий списком параметров (listaValorHora и т. д.). Наконец, будьте осторожны, когда начинаете разговор. См. stackoverflow.com/questions/ 8552478/.   -  person Brian    schedule 11.01.2012
comment
@ Брайан, я разместил код, который вы просили. Я отладил управляемый компонент, и он просто вызвал метод инициализации (с аннотацией PostContruct, где начинается диалог) один раз. И разговор вроде нормальный. Но когда я попытался самостоятельно управлять EntityManager, произошли те же ошибки. Я также протестировал SessionBean, чтобы убедиться, что он не сохраняет состояние, но сохранял. Тогда не знаю в чем может быть проблема.   -  person Fernando Camargo    schedule 12.01.2012


Ответы (1)


Ну наконец-то я решил проблему! Я видел, что LazyInitializationException находится на этапе проверки selectManyCheckbox. Затем я начал искать это и нашел эту ссылку: http://old.nabble.com/-jira---Created--(MYFACES-3306)-%3Ch%3AselectManyCheckBox%3E-%2B-JPA-с-Hibernate-создает-Hibernate-PersistentCollection-где-это-не-должно-вызывать-td32463262.html

Проблема заключалась в том, что JSF пытался использовать PersistentBag, созданный Hibernate, но не использовал его. Решением был добавлен атрибут, указывающий JSF использовать ArrayList вместо PersistentBag.

Это можно сделать, добавив это в selectManyCheckbox:

<f:attribute name="collectionType" value="java.util.ArrayList" />
person Fernando Camargo    schedule 13.01.2012