我有一个名为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.
}
答案 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();