简体   繁体   English

primefaces jsf hibernate - 在验证阶段:懒得初始化集合

[英]primefaces jsf hibernate - in validation phase : failed to lazily initialize a collection

I'm struggling on a problem which happen in the JSF validation phase, somehow that validation is doing something that hibernates doesn't like (failed to lazily initialize a collection, no session or session was closed) and I'd like to understand what. 我正在努力解决在JSF验证阶段发生的问题,某种程度上验证正在做一些冬眠不喜欢的事情(没有懒惰地初始化集合,没有会话或会话被关闭)而且我想了解什么。

The context is this one : I have a check, which is linked to a CheckType in a many to many relationship. 上下文是这样的:我有一个检查,它与多对多关系中的CheckType相关联。 I'm in the process of editing an existing check (in that edit page many comboboxes are offered in order to allow me to link it to a particular checktype). 我正在编辑现有支票(在该编辑页面中提供了许多组合框,以便允许我将其链接到特定的支票类型)。

I'm also using Primefaces. 我也在使用Primefaces。

The problem happens at the end of the validation process, but my converter works fine (returns the right result). 问题发生在验证过程结束时,但我的转换器工作正常(返回正确的结果)。

Moreover, what I don't really get is why there is a lazily, while all these objects are loaded in EAGER mode. 而且,我没有真正得到的是为什么有懒惰,而所有这些对象都以EAGER模式加载。

Thanks for your insight, I'm quiet new to hibernate and linking it to JSF also :/ 感谢您的洞察力,我很擅长休眠并将其链接到JSF:/

Stacktrace 堆栈跟踪

11:15:08,137 INFO  [com.bdls.ids.utils.BDPhaseListener] (http-localhost-127.0.0.1-8080-3) Before PROCESS_VALIDATIONS 3
11:15:11,439 INFO  [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate: 
11:15:11,440 INFO  [stdout] (http-localhost-127.0.0.1-8080-3)     select

11:15:11,486 INFO  [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate: 
11:15:11,486 INFO  [stdout] (http-localhost-127.0.0.1-8080-3)     select

11:15:11,503 INFO  [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate: 
11:15:11,504 INFO  [stdout] (http-localhost-127.0.0.1-8080-3)     select


11:15:12,942 INFO  [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate: 
11:15:12,944 INFO  [stdout] (http-localhost-127.0.0.1-8080-3)     select

11:15:13,023 INFO  [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate: 
11:15:13,023 INFO  [stdout] (http-localhost-127.0.0.1-8080-3)     select

11:15:13,040 INFO  [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate: 

11:15:13,062 INFO  [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate: 


11:15:13,856 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (http-localhost-127.0.0.1-8080-3) 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.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:385) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:378) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:206) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel(MenuRenderer.java:382) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValue(MenuRenderer.java:129) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:315) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at org.primefaces.component.selectmanycheckbox.SelectManyCheckboxRenderer.getConvertedValue(SelectManyCheckboxRenderer.java:36) [primefaces-3.4.2.jar:]
    at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIInput.validate(UIInput.java:960) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIInput.executeValidate(UIInput.java:1233) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIInput.processValidators(UIInput.java:698) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIForm.processValidators(UIForm.java:253) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1172) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79) [primefaces-3.4.2.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at com.bdls.ids.controller.login.NoCacheFilter.doFilter(NoCacheFilter.java:39) [classes:]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at com.bdls.ids.controller.login.AuthenticationFilter.doFilter(AuthenticationFilter.java:48) [classes:]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final]
    at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
    at java.lang.Thread.run(Unknown Source) [rt.jar:1.6.0_32]

11:15:13,901 INFO  [com.bdls.ids.utils.BDPhaseListener] (http-localhost-127.0.0.1-8080-3) After PROCESS_VALIDATIONS 3

I've only put the interesting parts for brevety : 我只把有趣的部分放在一边:

Xhtml XHTML

<h:form id="new-items" prependId="false">
    <label for="typechecks">Type of checks :</label>
    <p:selectManyCheckbox id="typechecks" value="#{checksController.itemEdited.checkTypes}" layout="pageDirection" converter="checkTypeConverter">
        <f:selectItems value="#{checkTypeController.allItems}" var="checkType" itemLabel="#{checkType.name}" />
    </p:selectManyCheckbox>
<p:commandButton id="save" value="Save" action="#{checksController.persistItemEdited}" styleClass="btnFooterFormNewItems" icon="ui-icon-check" update="new-items" />

</h:form>

Converter 变流器

@FacesConverter("checkTypeConverter")
public class CheckTypeConverter implements Converter {

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (value == null || value.toString().isEmpty()) {
            return ""; 
        }
        return ((UnversionedObject) value).getId().toString();
    }

    public Object getAsObject(FacesContext context, UIComponent arg1, String value) {

        CheckTypeService checkTypeService = FacesContextUtils.getWebApplicationContext(context).getBean("checkTypeService",
                CheckTypeService.class);
        return checkTypeService.getById(Integer.parseInt(value));
    }
}

Controller 调节器

@ManagedBean
@ViewScoped
public class ChecksController implements Serializable {
    private static Logger log = Logger.getLogger(ChecksController.class);

    // -------------------------------------------------
    // properties
    // -------------------------------------------------
    @ManagedProperty(value = "#{checksService}")
    private ChecksService checksService;

    // -------------------------------------------------
    // parameters
    // -------------------------------------------------
    private List<Check> allChecks;
    private Check[] selectedItem;
    private Check itemEdited = new Check();

    // -------------------------------------------------
    // constructor
    // -------------------------------------------------
    public ChecksController() {
        log.info("Creation of checks controller");

        allChecks = new ArrayList<Check>();

    }

    // -------------------------------------------------
    // methods
    // -------------------------------------------------
    @PostConstruct
    private void load() {
        if(FacesContext.getCurrentInstance().isPostback()) return;

        log.info("loading list of checks...");

        allChecks = checksService.getAll();
        log.info(String.format("Loaded %s items", allChecks.size()));
    }

    public void delete(Check c)
    {
        try{
            checksService.delete(c);
        }catch(Exception e){
            log.error("Error when deleting object "+c);
            e.printStackTrace();
        }
    }

    public void prefillFrom(String modelID) {
        log.info("Pre-filling based on model " + modelID);

        if (modelID == null) // new item
            return;

        // in this case, the item must exist (edition)
        itemEdited = checksService.getById(Integer.parseInt(modelID));
        if (itemEdited == null) {
            UINotification.sendAsError("Item does not exist", String.format("The item %s does not exist", modelID));
            log.error(String.format("The item %s does not exist", modelID));
        }
    }

    public void persistItemEdited(){
        log.info("persisting item edited...");

//      CheckType c = new CheckType();
//      c.setName("jolitest");
//      
//      itemEdited.getCheckTypes().add(c);
//      c.getChecks().add(itemEdited);
//      
        try{
            checksService.saveOrUpdate(itemEdited);
        }catch(Exception e){
            log.error("Error when saving object "+itemEdited);
            e.printStackTrace();
        }
        itemEdited = new Check() ;
    }

    public void test()
    {
        CheckType ct = new CheckType();
        ct.setName("toto");

        itemEdited.getCheckTypes().add(ct);


        Set<CheckType> set = new HashSet<CheckType>();
        set.add(ct);
        itemEdited.setCheckTypes(set);
    }

    getters and setters...

Service 服务

@Service("checksService")
@Transactional(readOnly = true)
public class ChecksServiceImpl implements ChecksService {
    @Autowired
    CheckDAO checkDAO;

    @Autowired
    CheckTypeDAO checkTypeDAO;

    /**
     * retrieves a UnversionedObject based on its id
     */
    public Check getById(Integer id) {
        return checkDAO.getById(id);
    }

    @Transactional(readOnly = false)
    public Check saveOrUpdate(Check c) {
        //for many-to-many relations, re-attach the entities
        if (c.getCheckTypes() != null && c.getCheckTypes().size() > 0)
        {
            List<CheckType> checkTypes = new ArrayList<CheckType>(c.getCheckTypes().size());
            for (CheckType ct : c.getCheckTypes()) {
                checkTypes.add(checkTypeDAO.getById(ct.getId()));
            }
            c.getCheckTypes().clear();
            c.getCheckTypes().addAll(checkTypes);
        }

        return checkDAO.saveOrUpdateDetached(c);
    }

    /**
     * returns a list of all checks
     */
    @Override
    public List<Check> getAll() {
        List<Check> list = checkDAO.getAll();
        return list;
    }

    /**
     * empty the extra relations and deletes the object
     */
    @Override
    @Transactional(readOnly = false)
    public void delete(Check c) {
        // attach object
        c = checkDAO.getById(c.getId());

        // initialize list
        Hibernate.initialize(c.getCheckTypes());

        // first remove relations to checktype
        c.getCheckTypes().clear();

        // update
        checkDAO.saveOrUpdateDetached(c);

        // now delete
        checkDAO.delete(c);
    }

}

DAO (impl) DAO(impl)

@Repository
public class CheckDAOImpl extends BaseDAOimpl<Check> implements CheckDAO {

}

public abstract class BaseDAOimpl<T extends UnversionedObject> implements BaseDAO<T> {
    @Override
    public T saveOrUpdateDetached(T object) throws DataModelConsistencyException {
        updateUserAndTime(object);

        if (object.getId() != null) {
            return _entityManager.merge(object);
        } else {
            _entityManager.persist(object);
            return object;
        }
    }

rest omitted
}

Check 校验

@Entity
@Table(name = "IDS_SV_CHECK")
public class Check extends UnversionedObject {
    public static enum Severity {
        HIGH, MEDIUM, LOW, INFORMATIVE
    };

    @Column(length = 32)
    private String checkName;

    @Column
    private Integer subCheckID;

    @Enumerated(EnumType.STRING)
    private Severity severity;

    @Column(length = 32)
    private String description;

    @Column(length = 32)
    private String errorMsg;

    @Column(length = 32)
    private String DMVersion;

    @Column(length = 32)
    private String therapeuticArea;

    @ManyToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinTable(name = "IDS_SV_CHECK_HAS_TYPE",
                joinColumns={@JoinColumn(name="check_id")},
                inverseJoinColumns={@JoinColumn(name="checktype_id")})
    @BatchSize(size = 100)
    private Set<CheckType> checkTypes = new HashSet<CheckType>();
        ...

Checktype Checktype

@Entity
@Table(name = "IDS_SV_CHECKTYPE")
public class CheckType extends UnversionedObject {

    @Column(length=60)
    private String name;

    @ManyToMany(mappedBy="checkTypes",fetch=FetchType.EAGER)
    private Set<Check> checks = new HashSet<Check>();

It is very late to reply, though I think it's best to leave track here of one more solution. 现在回复已经很晚了,但我认为最好留下一个解决方案。

I faced the very same problem, and surfing the web I stumble upon this: 我遇到了同样的问题,网上冲浪我偶然发现:

http://zenidas.wordpress.com/recipes/avoiding-lazyinitializationexception-on-jsf-selectmanymenu/ http://zenidas.wordpress.com/recipes/avoiding-lazyinitializationexception-on-jsf-selectmanymenu/

Tried this solution for my entity with a ManyToMany property and it works like a charm. 尝试使用ManyToMany属性为我的实体解决这个问题,它就像一个魅力。

Stefano 斯特凡诺

The reason is that the backing bean lifecycle is not tied to Spring. 原因是支持bean生命周期与Spring无关。 You can have ways to do that but below is a cleaner approach. 你可以有办法做到这一点,但下面是一个更清洁的方法。

First, make sure you have listeners in your deployment descriptors for Spring Boot Strap 首先,确保在Spring Boot Strap的部署描述符中有监听器

web.xml web.xml中

<!-- Spring Bootstrap -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        a.package
        another.package
        some.other.package
    </param-value>
</context-param>

Now have a helper method for the backing beans (better in a base class) to access the context. 现在有一个辅助方法用于支持bean(在基类中更好)来访问上下文。

protected WebApplicationContext getContext()
{
    HttpServletRequest request = (HttpServletRequest) (FacesContext.getCurrentInstance().getExternalContext().getRequest());
    return WebApplicationContextUtils.getRequiredWebApplicationContext(request.getSession().getServletContext());
}

Now in the backing bean access the spring bean instance by: 现在在backing bean中通过以下方式访问spring bean实例:

ChecksService bean = getContext().getBean(ChecksService.class);

That's - you should be good to go. 那是 - 你应该好好去。

You should never ever return the entity with active hibernate proxy out of transactional scope. 您永远不应该将具有活动hibernate代理的实体从事务范围中返回。 It simply can't work. 它根本无法工作。 Hibernate is creating proxies for collections, that are bound to hibernate session, which is bound to transaction, which no longer exist. Hibernate正在为集合创建代理,这些代理绑定到hibernate会话,该会话绑定到不再存在的事务。

Possible solutions: 可能的解决方案:

1) Always detach objects from hibernate session before returing them 1)在退回之前,始终将对象从休眠会话中分离出来

2) More radical, always return the copy of the object, made for example with dozer, to remove all possible proxies you could not be aware of. 2)更激进,总是返回对象的副本,例如使用dozer,以删除所有可能无法识别的代理。

3) Even more radical, and making your code more clean and understandable: never use @OneToMany or @ManyToMany annotations, if you need to operate on 1 to n or n to n relationsshipps, provide DAO for such operations, and make always explicite read, insert, update and delete. 3)更加激进,让你的代码更加干净和易懂:永远不要使用@OneToMany或@ManyToMany注释,如果你需要操作1到n或n到n关系,请为这些操作提供DAO,并且总是使用explicite read ,插入,更新和删除。 IMHO the best solution, in theory requiring more approach, in praxis requiring less approach to fight with such errors as yours. 恕我直言最好的解决方案,理论上需要更多的方法,在实践中需要较少的方法来解决你的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM