It is been a while since I meant to have this implemented, but Cloudfier’s Expert4J (which if you don’t know is a gap-free code generator for JavaEE) finally got support for generating JPA queries that have subqueries.
For example, take this query in TextUML, which returns all clients that have at least one invoice that has yet to be submitted to the client:
static query withInvoicesToIssue() : Client[*];
begin
return Client extent.select((client : Client) : Boolean {
client.invoices.exists((i : Invoice) : Boolean {
i.open
})
});
end;
where Invoice#open is defined as a derived attribute:
class Invoice
...
derived attribute open : Boolean := { self.status == Status#Preparation };
...
end;
(which if you couldn’t grasp it it means an invoice is considered “open” if it is in the Preparation state)
For that query, Expert4J will generate the following Java/JPA code:
public Collection withInvoicesToIssue() {
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Client.class);
Subquery invoiceSubquery = cq.subquery(Invoice.class);
Root client_ = cq.from(Client.class);
Root invoices = invoiceSubquery.from(Invoice.class);
return getEntityManager().createQuery(
cq.distinct(true).where(
cb.exists(
invoiceSubquery.select(invoices).where(
cb.equal(invoices.get("client"), client_),
cb.equal(invoices.get("status"), Invoice.Status.Preparation))
)
)
).getResultList();
}
The main challenge here was around generating the variable names when accessing the query roots. That required beefing up Expert4J with deeper data flow analysis. It was tough, and almost overwhelming (given the short windows I had to work on this between client work), but it is finally done, and I am proud of the result. You can see tests for this and other query-related code generation features in QueryActionGenerator.xtend.
I am no JPA expert, so if you are familiar with JPA and you have feedback on where the generated code could be improved, please let me know (and if you know people that grok JPA, please pass this post on to them). The entire code for the example Time Tracking and Invoicing application is available here.