3
Hello, I have an entity called menu in my system. A menu can be child of another menu and so on. The table has the following structure:
The entity is mapped as follows:
@Entity
@NamedQuery(name="Menu.findAll", query="SELECT m FROM Menu m")
public class Menu extends AbstractEntityDomain implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String id;
private String descricao;
private String legenda;
private String link;
private int ordem;
//bi-directional many-to-one association to Menu
@ManyToOne
@JoinColumn(name="menupai")
private Menu menu;
//bi-directional many-to-one association to Menu
@OneToMany(mappedBy="menu", fetch=FetchType.LAZY)
private Set<Menu> menus;
//bi-directional many-to-one association to Grupomenu
@OneToMany(mappedBy="menu", fetch=FetchType.LAZY)
private Set<Grupomenu> grupomenus;
public Menu() {
}
public Menu(String id, String descricao, String legenda, String link, int ordem){
this.id = id;
this.descricao = descricao;
this.legenda = legenda;
this.link = link;
this.ordem = ordem;
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getDescricao() {
return this.descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public String getLegenda() {
return this.legenda;
}
public void setLegenda(String legenda) {
this.legenda = legenda;
}
public String getLink() {
return this.link;
}
public void setLink(String link) {
this.link = link;
}
public int getOrdem() {
return this.ordem;
}
public void setOrdem(int ordem) {
this.ordem = ordem;
}
public Menu getMenu() {
return this.menu;
}
public void setMenu(Menu menu) {
this.menu = menu;
}
public Set<Menu> getMenus() {
return menus;
}
public void setMenus(Set<Menu> menus) {
this.menus = menus;
}
public Menu addMenus(Menu menus) {
getMenus().add(menus);
menus.setMenu(this);
return menus;
}
public Menu removeMenus(Menu menus) {
getMenus().remove(menus);
menus.setMenu(null);
return menus;
}
public Set<Grupomenu> getGrupomenus() {
return grupomenus;
}
public void setGrupomenus(Set<Grupomenu> grupomenus) {
this.grupomenus = grupomenus;
}
I need to execute a query where I want to bring all the menus with your children, I prepared the query as follows:
StringBuilder sb = new StringBuilder();
sb.append("SELECT m ");
sb.append("FROM Menu m ");
sb.append("LEFT JOIN FETCH m.menusFilho ");
sb.append("WHERE m.menu is null ");//esta linha é porque os menus principais não tem pai
List<Menu> lista = new ArrayList<Menu>(em.createQuery(sb.toString()).getResultList());
This query is returning duplicate values. For each menu that has children such as the permissions menu, it brings three occurrences of the menu permissions with the filled children’s Collections instead of bringing only one occurrence with a filled children’s Collections.
I have tried using distinct and the same error occurs, do you have any suggestions ? Sorry if the text got big, I tried to explain my scenario to the maximum.
Could you replace the second figure with the corresponding code? It would be easier to test and search solutions on the internet like this.
– Victor Stafusa
What is a
AbstractEntityDomain
and aGrupomenu
?– Victor Stafusa
Abstractentitydomain is a class I use to abstract toString and hashcode methods. To make them look the same in all my entities. It only has one standard hashcode implementation and one toString. Already Grupomenu would be another entity of my concept that represents a permission group that has access to some menus.
– hebertrfreitas
Well, I don’t know what I could do to help you. But I’ll give you a little suggestion: You don’t need the
StringBuilder
. The compiler is smart enough to know that concatenating fixed and determined strings gives another fixed and determined string. So you can use just the old operator+
to mount SQL/JPQL without problem and the compiler will already mount itself and put the already complete string in bytecode, which is faster and simpler than using theStringBuilder
.– Victor Stafusa
@Victorstafusa thanks for the tip, I edited. If you are going to test, grupomenu is just another entity, which represents a permission group that would have access to the menu. In the current case it can be removed for testing because it does not affect this scope.
– hebertrfreitas
I loaded the query executed by Hibernate and verified that it does not bring duplicated lines, IE, the problem occurs at the moment when it converts it to objects. I converted the result that comes in a List to a Set (does not allow duplicated data) and it worked perfectly. Based on the assumption that my query is correct I can understand that this would be a normal behavior of Hibernate ?
– hebertrfreitas
It speaks Herbert, blza? Man, I think it would be more performative and easier to debug if you use a recursive algorithm, where the first called method takes every parent then calls another who takes every child from every parent and checks if that child has a child, and so it goes... I think it will do some 3 sql. sql1 - picks up all parents, Czech sql2 if father has children and sql3 picks up the father’s children.. and so it goes back and forth.. I have done using sql native and populating DTO objects, it was very fast and easy to debug.. well it is to say.. good luck... :-D
– Murilo A. Oliveira