Прежде всего, если в этой иерархии «отец» может иметь более одного потомка, то поле father
должно быть аннотировано как @ManyToOne
.
Если у вас есть поле, которое разделяют все члены дерева, или если дерево содержит всю таблицу, то можно эффективно сделать с JPA, но не через одиночный запрос JPA.
Вам просто нужно предварительно выбрать всех членов дерева, а затем пройти по дереву:
@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;
}
Обратите внимание на аннотацию @OrderedBy
в поле подкатегории.
Теперь вы можете получить все дерево, сначала загрузив все категории в беспорядочный список, чтобы они все были в памяти, а затем перебрав дерево.
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);
}
}
Я только сейчас изучаю JPA, так что, может быть, я упускаю что-то решающее, но этот подход мне подходит.