First of all, assuming in this hierarchy a "father" can have more than one child, then the father
field should be annotated as @ManyToOne
.
If you have a field that all the members of a tree share, or if the tree contains the entire table, then it is possible to do it with JPA in an efficient way, though not through a single JPA query.
You simply need to prefetch all the members of the tree, and then traverse the tree:
@Entity
@Table(name="categories")
public class Category {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequence")
@SequenceGenerator(name="sequence", sequenceName="categories_pk_seq", allocationSize=1)
@Column(name="id")
private Long id;
@Column
private String name;
@ManyToOne
@JoinColumn(name="idfather")
private Category father;
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY,
mappedBy = "idfather")
@OrderBy("name")
private List<Category> subCategories;
}
Notice the @OrderedBy
annotation on the subCategories field.
Now you can get the entire tree by first loading all the categories into a jumbled list, just so that they'd all be in memory, and then traverse the tree.
public List<Category> getTree() {
List<Category> jumbled =
entityManager.createQuery("from Category", Category.class).getResultList();
Category root = null;
for(Category category : jumbled) {
if(category.getFather() == null) {
root = category;
break;
}
}
List<Category> ordered = new ArratList<Category>();
ordered.add(root);
getTreeInner(root, ordered);
}
private void getTreeInner(Category father, List<Category> ordered) {
for(Category child : father.getSubCategories()) {
ordered.add(child);
getTreeInner(child, ordered);
}
}
I'm only learning JPA myself right now, so I may be missing something crucial, but this approach seems to work for me.