To accomplish this, I do two things:
- Find the paths that begin with the parent's path, including trailing slash, for example /labs/. This selects all descendants of the parent.
- Of those, find the ones who have NO OTHER SLASHES beyond the parent. This selects only the direct descendants.
Using JPQL
@Query("select w from WikiPage w " +
" where w.path like concat(:path, '/', '%') " +
" AND LOCATE('/', w.path, length(:path)+2) = 0")
public List<WikiPage>
findDirectChildrenOfUsingAnnotationQuery(@Param("path") String path);
Using the Java-based Query DSL
@Override
public List<WikiPage> findDirectChildrenOfUsingQueryDSL(String searchPath) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuer<WikiPage> query = builder.createQuery(WikiPage.class);
Root<WikiPage> wikiPage_root = query.from(WikiPage.class);
Expression<String> wikiPage_path = wikiPage_root.get("path");
Predicate startsWithPath = builder.like(wikiPage_path, searchPath + "/%");
Expression<Integer> slashLocation = builder.locate(wikiPage_path, "/",
searchPath.length()+2);
Predicate noMoreDescendants = builder.equal(slashLocation, 0);
query = query.where(builder.and(startsWithPath, noMoreDescendants));
return em.createQuery(query.select(wikiPage_root)).getResultList();
}
Reasons to use the Java DSL
- It is more refactorable ... but, a good IDE like IntelliJ can refactor JPQL just fine
- It's more typesafe ... but not completely so, and again, IntelliJ does a pretty good job. You could even argue that it does better, because the Java DSL can't tell that "path" is a valid attribute of WikiPage, but in JPQL it could
- Compiler-time errors if you screw up ... but again, IntelliJ already tells me if my JPQL is invalid
- Certain operations - like the concat and strlen - are arguably better handled in java than by the db engine
The Java DSL is definitely harder to read. But I have a distaste for 'magical strings' which includes JPQL in a @Query annotation.
Thoughts?