4

我正在努力解决在 JSF 验证阶段发生的问题,不知何故,验证正在做一些休眠不喜欢的事情(未能懒惰地初始化一个集合,没有会话或会话被关闭),我想了解什么.

上下文是这样的:我有一张支票,它以多对多的关系链接到 CheckType。我正在编辑现有检查(在该编辑页面中提供了许多组合框,以便我将其链接到特定检查类型)。

我也在使用 Primefaces。

问题发生在验证过程结束时,但我的转换器工作正常(返回正确的结果)。

此外,我真正不明白的是为什么会有一个 lazily,而所有这些对象都以 EAGER 模式加载。

感谢您的洞察力,我对休眠和将其链接到 JSF 也很陌生:/

堆栈跟踪

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

为了简洁起见,我只放了有趣的部分:

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>

转换器

@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));
    }
}

控制器

@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("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 (实现)

@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
}

查看

@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>();
        ...

检查类型

@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>();
4

3 回答 3

3

回复已经很晚了,尽管我认为最好在这里留下另一个解决方案。

我遇到了同样的问题,在网上冲浪时我偶然发现了这个:

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

用 ManyToMany 属性为我的实体尝试了这个解决方案,它就像一个魅力。

斯特凡诺

于 2014-04-29T14:08:24.327 回答
1

原因是支持 bean 生命周期与 Spring 无关。你可以有办法做到这一点,但下面是一种更清洁的方法。

首先,确保在 Spring Boot Strap 的部署描述符中有侦听器

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>

现在有一个辅助 bean 的辅助方法(在基类中更好)来访问上下文。

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

现在在 backing bean 中通过以下方式访问 spring bean 实例:

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

那是 - 你应该很高兴。

于 2013-03-19T12:52:41.450 回答
1

您永远不应该将具有活动休眠代理的实体返回到事务范围之外。它根本行不通。Hibernate 正在为集合创建代理,这些代理绑定到休眠会话,它绑定到不再存在的事务。

可能的解决方案:

1) 在返回对象之前,总是从休眠会话中分离对象

2)更激进,总是返回对象的副本,例如用推土机制作的,以删除所有您可能不知道的代理。

3) 更激进,让你的代码更简洁易懂:永远不要使用@OneToMany 或@ManyToMany 注解,如果你需要对1 到n 或n 到n 关系进行操作,为此类操作提供DAO,并始终明确阅读,插入,更新和删除。恕我直言,最好的解决方案,理论上需要更多的方法,在实践中需要更少的方法来解决像你这样的错误。

于 2013-03-20T09:53:19.770 回答