JPA递归延迟加载失败

时间:2017-03-12 06:01:01

标签: java hibernate jpa recursion

我有一个名为Menu的实体,其中包含parent和多个children,我使用JPA加载。当children设置为@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

我使用Hibernate 4.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)都不起作用。

我应该对延迟加载menu.getChildren()做些什么?

所以,下面我有三个处理这个问题的类。方法MenuManagerController.buildNodeStructureFromMenu(TreeNode root, List<Menu> menus)执行递归作业。这是进行延迟装载的地方。

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

MenuManagerController.java:

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

MenuService.java:

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

3 个答案:

答案 0 :(得分:1)

您正在关闭相关会话后访问延迟收藏。 您需要使用事务范围加载子项。 在菜单中

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

答案 1 :(得分:1)

例外情况表示您在交易关闭后尝试获取延迟加载的关系。

我建议您查看我的文章:http://blog.arnoldgalovics.com/2017/02/27/lazyinitializationexception-demystified/

但是,我的文章是针对基于Spring的配置编写的,您可以根据自己的情况轻松调整它。

答案 2 :(得分:-2)

我认为你需要像这样初始化孩子。

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