Java JPA递归延迟加载失败

Java JPA递归延迟加载失败,java,hibernate,jpa,recursion,Java,Hibernate,Jpa,Recursion,我有一个名为菜单的实体,它有一个父项和多个子项,我使用JPA加载它们。当子对象设置为@OneToMany(mappedBy=“parent”,fetch=FetchType.EAGER)时,一切正常,但如果我将其设置为FetchType.LAZY,则会出现错误: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mydom.myapp.domain.Me

我有一个名为
菜单
的实体,它有一个
父项
和多个
子项
,我使用JPA加载它们。当
子对象
设置为
@OneToMany(mappedBy=“parent”,fetch=FetchType.EAGER)
时,一切正常,但如果我将其设置为
FetchType.LAZY
,则会出现错误:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mydom.myapp.domain.Menu.children, could not initialize proxy - no Session
我使用Hibernate4.3.1.Final、JPA、Wildfly 10.1.0作为服务器,MySQL作为数据库

我尝试了使用
Hibernate.initialize(menu.getChildren())
和调用size()-方法的技巧,就像在
menu.getChildren().size()
中一样,但在这两种情况下,我都会导致相同的错误

我还尝试设置了
@Fetch(FetchMode.JOIN)
,这很有效,但它与使用
FetchType.Eager
非常相似,因此没有什么用处。
@Fetch(FetchMode.SELECT)
@Fetch(FetchMode.SUBSELECT)
都不起作用

要延迟加载
菜单.getChildren()
,我必须做什么

下面我有三个类来处理这个问题。方法
MenuManagerController.buildNodeStructureFromMenu(树节点根目录,列表菜单)
执行递归作业。这是延迟加载发生的地方

Menu.java:

@Entity
@NamedQueries({
    @NamedQuery(name = Menu.FIND_MENU_ROOT,
                query = "SELECT m FROM Menu m WHERE m.parent.id IS NULL")
})
public class Menu implements Serializable {
    private static final String PREFIX = "Menu.";
    public static final String FIND_MENU_ROOT = PREFIX + "findMenuRoot";

    @Id
    @GeneratedValue
    private long id;
    @ManyToOne
    private Languages languageType;
    @Column(length = 100)
    private String nameDisplay;
    private String nameAddress;
    private int position;
    @ManyToOne
    private Menu parent;
    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
    private List<Menu> children;

    // Getters, setters, etc.
}
@Named
@ViewScoped
public class MenuManagerController implements Serializable {
    @Inject
    private MenuService menuService;
    private Languages selectedLanguage;
    private List<Languages> languages;
    private String menuName;
    private TreeNode rootMenu;
    private TreeNode selectedMenu;

    @PostConstruct
    private void init() {
        // Find root menu.
        Menu menu = menuService.findRootMenu();
        // Create root node.
        this.rootMenu = new DefaultTreeNode(menu, null);
        // Lazy load children.
        Hibernate.initialize(menu.getChildren());
        // Build Node from Menu.
        buildNodeStructureFromMenu(this.rootMenu, menu.getChildren());
    }

    private void buildNodeStructureFromMenu(TreeNode root, List<Menu> menus) {
        App.printWithTrace("Add all the " + menus.size() + " children to \"" + root.getData().toString() + "\".");

        for(Menu menu : menus) {
            App.printWithTrace("Create node for \"" + menu.getNameDisplay() + "\".");
            // Create child node of root node.
            TreeNode child = new DefaultTreeNode(menu, root);
            // Lazy load children.
            Hibernate.initialize(menu.getChildren());
            // Create all children of this menu.
            List<Menu> children = menu.getChildren();
            // Recursion.
            buildNodeStructureFromMenu(child, children);
        }
    }
}
@Stateless
public class MenuService {
    @PersistenceContext(unitName = "MyappPU")
    private EntityManager em;

    public enum Layer {
        All,
        FirstOnly
    }

    public Menu findRootMenu() {
        return this.em.createNamedQuery(Menu.FIND_MENU_ROOT, Menu.class)
                      .getSingleResult();
    }

    // More methods, etc.
}

在关联会话关闭后,您正在访问惰性集合。 您需要加载具有事务作用域的子级。 在菜单上

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public Menu findRootMenu() {
        Menu menu = this.em.createNamedQuery(Menu.FIND_MENU_ROOT, Menu.class)
                      .getSingleResult();
    //Lazy loading children with the transaction scope.
    menu.getChildren().size();
    return menu();
    }
2) 您还可以使用hibernate条件查询更改要加入的惰性集合的获取模式

Criteria criteria = session
                .createCriteria(Menu.class)
                .add(Restrictions.eq("parent.id", id))
                .setFetchMode("children", JOIN);
return this.em.createQuery(criteria)
                      .getSingleResult();

在关联会话关闭后,您正在访问惰性集合。 您需要加载具有事务作用域的子级。 在菜单上

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public Menu findRootMenu() {
        Menu menu = this.em.createNamedQuery(Menu.FIND_MENU_ROOT, Menu.class)
                      .getSingleResult();
    //Lazy loading children with the transaction scope.
    menu.getChildren().size();
    return menu();
    }
2) 您还可以使用hibernate条件查询更改要加入的惰性集合的获取模式

Criteria criteria = session
                .createCriteria(Menu.class)
                .add(Restrictions.eq("parent.id", id))
                .setFetchMode("children", JOIN);
return this.em.createQuery(criteria)
                      .getSingleResult();

异常表示您正试图在事务关闭后获取延迟加载的关系

我建议你看看我的文章:


然而,我的文章是为基于Spring的配置而写的,您可以根据您的情况轻松地进行调整。

例外情况是,您试图在事务关闭后获取一个延迟加载的关系

我建议你看看我的文章:


然而,我的文章是为基于Spring的配置而写的,您可以根据您的情况轻松地进行调整。

我认为您需要像这样初始化子级

@OneToMany(mappedBy=“parent”,fetch=FetchType.LAZY)
private List children=new ArrayList()

我想你需要像这样初始化孩子

@OneToMany(mappedBy=“parent”,fetch=FetchType.LAZY)
private List children=new ArrayList()

对初始问题没有影响。错误保持不变。我假设@TransactionAttribute通常需要使用事务,对吗?在我看来,我不使用交易。是的。它通常需要使用事务。您需要在应用程序上下文xml中定义以下内容。标签
在Spring框架中使用,我没有在上述项目中使用。有没有办法解决标准Hibernate/JPA的延迟加载问题?您也可以使用Hibernate标准来解决这个问题,我已经相应地编辑了答案。您的Hibernate标准的第二个示例是CriteriaQuery吗?我在尝试实现您的代码示例时遇到问题。变量
id
从何处获取?那是一根绳子吗?同样,如果我将
条件
放入
…createQuery(条件).getSingleRes…
中,我会得到一个隐藏的错误,阻止我编译。对初始问题没有影响。错误保持不变。我假设@TransactionAttribute通常需要使用事务,对吗?在我看来,我不使用交易。是的。它通常需要使用事务。您需要在应用程序上下文xml中定义以下内容。标签
在Spring框架中使用,我没有在上述项目中使用。有没有办法解决标准Hibernate/JPA的延迟加载问题?您也可以使用Hibernate标准来解决这个问题,我已经相应地编辑了答案。您的Hibernate标准的第二个示例是CriteriaQuery吗?我在尝试实现您的代码示例时遇到问题。变量
id
从何处获取?那是一根绳子吗?同样,如果我将
条件
放入
…createQuery(条件).getSingleRes…
中,我会得到一个隐藏的错误,阻止我编译。您应该一次获取
MenuService
中已经存在的所有菜单实例,否则您可能会延迟加载不起作用的关系,因为您的实体管理器在退出
findrootmenus()
后关闭。您需要在每个菜单中引用根目录,以便有效地加载所有项,或者执行递归查询。查看Blaze Persistence编写递归查询:您应该立即获取
MenuService
中已经存在的所有菜单实例,否则您可能会延迟加载无法工作的关系,因为您的实体管理器在退出
findRootMenu()
后关闭。您需要在每个菜单中引用根目录,以便有效地加载所有项,或者执行递归查询。看一看Blaze Persistence用于编写递归查询: