本文介绍了在JSF中使用JPA实体。哪种防止LazyInitializationException的最佳策略?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



所以,关于这个问题有几句话要说。



想象一下,我有持久化对象 MyEntity ,然后获取它进行编辑。在DAO层,我使用

  return em.find(MyEntity.class,id); 

返回 MyEntity 父实体 - 想象其中之一是 MyParent MyParent 被作为 @Access(AccessType.PROPERTY)的代理问候语获取:

  @Entity 
public class MyParent {

@Id
@Access(AccessType.PROPERTY)
私人长ID;
// ...
}

以及MyEntity有参考:

  @ManyToOne(fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.PROXY)
private MyParent myParent;

到目前为止这么好。在UI中,我直接使用获取的对象,而不创建任何值对象,并在选择列表中使用父对象:

 < h:selectOneMenu value =#{myEntity.myParent.id}id =office> 
< / h:selectOneMenu>

一切都呈现正常,没有 LazyInitializationException 发生。但是当我保存对象时,我收到了

  LazyInitializationException:无法初始化代理 - 没有会话

on MyParent 代理 setId()

如果将 MyParent 关系更改为<$ c $,我可以轻松解决问题c> EAGER

  @ManyToOne(fetch = FetchType.EAGER)
私有MyParent myParent ;

或者使用获取对象left join fetch p.myParent (其实这就是我现在的做法)。在这种情况下,保存操作可以正常工作,并且该关系透明地更改为新的 MyParent 对象。不需要额外的操作(手动拷贝,手动参考设置)。非常简单方便。



。如果对象引用另外10个对象 - em.find(),则会导致 10个附加连接,这不是一个好的数据库操作,特别是当我根本不使用引用对象状态。我只需要 - 是指向对象的链接,而不是它们的状态。



这是一个全局问题,我想知道,JSF专家如何在应用程序中处理JPA实体,这是避免额外连接和 LazyInitializationException



的最佳策略。扩展的持久化上下文对于



谢谢!

解决方案

您应该准确提供模型如果JPA实体恰好匹配需要的模型,那么就马上使用。



如果JPA实体恰好具有太少或太多的属性,那么使用DTO(子类)和/或与更具体的JPQL查询相关联,如有必要,请使用。或者也许用Hibernate特定的或EclipseLink特定的。否则,它可能会导致所有地方出现延迟的初始化异常,或者消耗的内存超过必要。



open session in view模式是一个糟糕的设计。这意味着可以在呈现响应的同时执行业务逻辑。这与其他异常处理并没有很好的结合,其中意图是向最终用户显示自定义错误页面。如果业务异常在响应中途抛出,因此最终用户已经收到了响应头文件和HTML的一部分,那么服务器不能再清除响应,以显示出错的错误页面。另外,在getForm方法中执行业务逻辑对于JSF中的练习来说是不成熟的,正如根据。

只需在渲染响应阶段开始之前通过托管bean动作/侦听器方法中的常规服务方法调用准确准备好视图所需的模型即可。例如,一种常见的情况是使用一个懒惰的加载一对多子属性的现有(非托管)父实体,并且您想通过ajax动作在当前视图中呈现它,那么您应该只是让ajax监听器方法在服务层获取并初始化它。

 < f:ajax listener =#{bean。 showLazyChildren(parent)}render =children/> 





  public void showLazyChildren父母){
someParentService.fetchLazyChildren(parent);





  public void fetchLazyChildren(Parent parent){
parent.setLazyChildren(em.merge(parent).getLazyChildren()); //成为托管。
parent.getLazyChildren()。size(); //触发延迟初始化。
}

特别在JSF UISelectMany 组件,在保存所选项目过程中,有一个 LazyInitializationException :另一个完全出乎意料的可能原因,但JSF在填充所选项目之前需要重新创建底层集合,但是如果碰巧是一个持久层特定的延迟加载集合实现,那么这个异常也会被抛出。解决方案是显式设置 UISelectMany 组件的 collectionType 属性为所需的普通类型。

 < h:selectManyCheckbox ... collectionType =java.util.ArrayList> 

详细询问并回答于



  • Would like to hear experts on best practice of editing JPA entities from JSF UI.

    So, a couple of words about the problem.

    Imagine I have the persisted object MyEntity and I fetch it for editing. In DAO layer I use

    return em.find(MyEntity.class, id);
    

    Which returns MyEntity instance with proxies on "parent" entities - imagine one of them is MyParent. MyParent is fetched as the proxy greeting to @Access(AccessType.PROPERTY):

    @Entity
    public class MyParent {
    
        @Id
        @Access(AccessType.PROPERTY)
        private Long id;
        //...
    }
    

    and MyEntity has the reference to it:

    @ManyToOne(fetch = FetchType.LAZY)
    @LazyToOne(LazyToOneOption.PROXY)
    private MyParent myParent;
    

    So far so good. In UI I simply use the fetched object directly without any value objects created and use the parent object in the select list:

    <h:selectOneMenu value="#{myEntity.myParent.id}" id="office">
        <f:selectItems value="#{parents}"/>
    </h:selectOneMenu>
    

    Everything is rendered ok, no LazyInitializationException occurs. But when I save the object I recieve the

    LazyInitializationException: could not initialize proxy - no Session
    

    on MyParent proxy setId() method.

    I can easily fix the problem if I change the MyParent relation to EAGER

    @ManyToOne(fetch = FetchType.EAGER)
    private MyParent myParent;
    

    or fetch the object using left join fetch p.myParent (actually that's how I do now). In this case the save operation works ok and the relation is changed to the new MyParent object transparently. No additional actions (manual copies, manual references settings) need to be done. Very simple and convenient.

    BUT. If the object references 10 other object - the em.find() will result 10 additional joins, which isn't a good db operation, especially when I don't use references objects state at all. All I need - is links to objects, not their state.

    This is a global issue, I would like to know, how JSF specialists deal with JPA entities in their applications, which is the best strategy to avoid both extra joins and LazyInitializationException.

    Extended persistence context isn't ok for me.

    Thanks!

    解决方案

    You should provide exactly the model the view expects.

    If the JPA entity happens to match exactly the needed model, then just use it right away.

    If the JPA entity happens to have too few or too much properties, then use a DTO (subclass) and/or a constructor expression with a more specific JPQL query, if necessary with an explicit FETCH JOIN. Or perhaps with Hibernate specific fetch profiles, or EclipseLink specific attribute groups. Otherwise, it may either cause lazy initializtion exceptions over all place, or consume more memory than necessary.

    The "open session in view" pattern is a poor design. This implies that it's possible to perform business logic while rendering the response. This doesn't go very well together with among others exception handling whereby the intent is to show a custom error page to the enduser. If a business exception is thrown halfway rendering the response, whereby the enduser has thus already received the response headers and a part of the HTML, then the server cannot clear out the response anymore in order to show a nice error page. Also, performing business logic in getter methods is a frowned upon practice in JSF as per Why JSF calls getters multiple times.

    Just prepare exactly the model the view needs via usual service method calls in managed bean action/listener methods, before render response phase starts. For example, a common situation is having an existing (unmanaged) parent entity at hands with a lazy loaded one-to-many children property, and you'd like to render it in the current view via an ajax action, then you should just let the ajax listener method fetch and initialize it in the service layer.

    <f:ajax listener="#{bean.showLazyChildren(parent)}" render="children" />
    
    public void showLazyChildren(Parent parent) {
        someParentService.fetchLazyChildren(parent);
    }
    
    public void fetchLazyChildren(Parent parent) {
        parent.setLazyChildren(em.merge(parent).getLazyChildren()); // Becomes managed.
        parent.getLazyChildren().size(); // Triggers lazy initialization.
    }
    

    Specifically in JSF UISelectMany components, there's another, completely unexpected, probable cause for a LazyInitializationException: during saving the selected items, JSF need to recreate the underlying collection before filling it with the selected items, however if it happens to be a persistence layer specific lazy loaded collection implementation, then this exception will also be thrown. The solution is to explicitly set the collectionType attribute of the UISelectMany component to the desired "plain" type.

    <h:selectManyCheckbox ... collectionType="java.util.ArrayList">
    

    This is in detail asked and answered in org.hibernate.LazyInitializationException at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel.

    See also:

    这篇关于在JSF中使用JPA实体。哪种防止LazyInitializationException的最佳策略?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

    09-13 01:25