I have a class which models all categories and they can be ordered hierarchically.
@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;
@OneToOne
@JoinColumn(name="idfather")
private Category father;
}
I need to get all categories ordered hierarchically (I mean every father followed by its children and fathers ordered alphabetically on each level) as they could be made for example with PRIOR in oracle. Is it possible to do this with a JPA Query (not a SQL one)?
Thanks.
The short answer is; no there isn't a standard way to do this.
You have to use native sql.
You may be able to extend the Oracle Hibernate Dialect and add some user function/extension to get hibernate to generate PRIOR or CONNECT BY clauses, but this will prevent your app from being strict JPA and database independent.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With