Issue
I have an entity named Menu
that has a parent
and multiple children
that I load using JPA. Everything works well, when the children
is set to @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
, but if I set it to FetchType.LAZY
, I get the error:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mydom.myapp.domain.Menu.children, could not initialize proxy - no Session
I use Hibernate 4.3.1.Final, JPA, Wildfly 10.1.0 as server, MySQL as database.
I've tried the trick with Hibernate.initialize(menu.getChildren())
and as well calling the size()-method as in menu.getChildren().size()
, but in both these cases I result in the same error.
I as well tried to set @Fetch(FetchMode.JOIN)
which works, but it's pretty much like using FetchType.Eager
, therefore not useful. Both @Fetch(FetchMode.SELECT)
and @Fetch(FetchMode.SUBSELECT)
won't work.
What must I do to lazy load menu.getChildren()
?
So, below I have the three classes that handle this matter. The method MenuManagerController.buildNodeStructureFromMenu(TreeNode root, List<Menu> menus)
does the recursive job. This is the place where the lazy loading shall take place.
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.
}
Solution
The exception says that you are trying to fetch a lazily loaded relation after your Transaction is closed.
I'd suggest you to check out my article: https://arnoldgalovics.com/lazyinitializationexception-demystified/
However, my article is written for Spring based configuration, you can easily adapt it for your case.
Answered By - galovics