I like the direction of this api. My comments here might have been expressed by others; I don't claim ownership of them.
I like the naming of the Q classes generated during pre-processing. I consider the requirement to have the persistent classes annotated to be a distraction though.
I think we need to consider reuse of the query artifacts by different persistence managers, and also the possibility that the query definition might be stored as a named query. This leads to needing a factory defined on the PMF, not just the PM. The factory might not be needed on the PM depending on other considerations (discussed below).
The same query definition could be used by different projections. We could make this explicit in the api by adding a method to the query definition that returns a different interface like QueryProjection<R> where R is the class of the result. And the query definition interface contains a superset of the methods of the query projection interface, reflecting that the default query projection returns instances of the candidate class.
It isn't clear from the external view whether there is a check to be sure that the filter applies to properties of the candidate class. I expect that we can make sure that this is the case by templating the expressions on both the type of the candidate class and the type of the property.
In order to execute a query, three things are needed: the query definition (including the projection definition), the persistence manager, and the parameters. I believe that there is value in allowing parameters to be bound very late, during the execution of the query. But I also see value in allowing parameters to be bound to a different instance entirely.
If set individually, properties should be type-checked as well. Using the setParameter(name, value) method on the query definition, I don't see how to check the type. Instead of name, perhaps a method taking the parameter itself instead of the name.
It will be good to integrate this new query api into the existing query, but I'll leave that discussion for later (perhaps once we agree what the new query should look like).
Thinking out loud,
QueryDefinition<T> extends QueryProjection<T>
QueryProjection<P> project(Class<P>, projectionList...);
List<P> executeList(Object... parameters);
P executeUnique(Object... parameters);
List<P> executeList(Map parameters);
P executeUnique(Map parameters);
get/set ReadTimeout(int millis);
get/set WriteTimeout(int millis);
get/set Range(int low, int high);
QueryDefinition<T> newQueryDefinition(Class<T> candidateClass), boolean unique);
void storeNamedQuery(String name, QueryProjection<?> query);
QueryProjection<?> getNamedQuery(String name);
List<P> executeList(QueryProjection<P>, Object... parameters);
P executeUnique(QueryProjection<P>, Object... parameters);