Using the same tricks of annotation processors as we applied for Infinispan as proof of concept (project Jokre), we can dynamically replace the invocation of - for example - createQuery() with a trap method which is going to trigger an inspection of the bytecode of the invoker.
The inspection will result in two different actions:
replace the trap method with one which is a different method (normally not public), so this feature doesn't have any overhead as the replacement basically removed the inspection trigger.
the newly invoked method will pass on some more information about the execution context of the requested query.
The upcoming query engine will have a "planning" component; this will be simple initially but will need to have basic logic to pick among different execution options; it seems natural to assume that this planning component will evolve over time to implement smarter techniques, possibly borrowing from the Teiid project.
I'm not an expert of bytecode analysis but having talked to some, it seems it would be easy to implement for example escape analysis and make decisions in the query about the actual need of the invoker; for example if the query is loading User entities we might automatically infer that the client code is actually only interested in the User's birth dates, or that a different fetch strategy would be desirable to pre-load some relations.
Another example is unused relations: today we create proxies so that in case the relation is "walked" by the user code appropriate loading is triggered; it would be possible to proof that in some cases we don't need any proxy and a null would be good enough.
Initial optimizations could be simple: overhead is zero so one could just implement a couple of simple tricks, and let the concept evolve with gradual iterations.
The simplest optimization would be to simply identify which use case (method) is invoking the query, and read some external resource which lists fetch plans on a per-usecase base; such a resource could be easily generated by a profiling tool and help with performance tuning operations: today the same tuning is possible but requires several iterations between code changes and re-deploys, restarting stress tests on a very time consuming process.
In a longer term vision, the internal method could become smart enough to not just pick an appropriate static plan but also collect runtime information and implement ergonomic decisions. Besides fetch plans, this could be picking caching strategies, transactional isolation levels, different lock levels (lock removal?), appropriate batch sizes, maybe even generation of prepared statements.
Proof of concept and low level explanation: