Blazebit / blaze-persistence

Rich Criteria API for JPA providers
https://persistence.blazebit.com
Apache License 2.0
727 stars 85 forks source link

Support class field as projection target #474

Open beikov opened 6 years ago

beikov commented 6 years ago

To be able to implement domain models that hide certain information it is necessary to support injection of projections into fields. Read only views can already do that via constructor injection, but it would be lot nicer to support field injection. Note that for updatable entity views, this will require bytecode analysis to capture all writes to such a field to be replaced with proper write accessors. It should also be possible to use a normal POJO class i.e. non-abstract class which could then also change the defaults. Right now, only abstract methods are considered to be "attributes". For concrete classes it might be desireable to switch the defaults and consider all fields to be attributes unless annotated with an ignore annotation.

beikov commented 6 years ago

When using field injection, it should be allowed to use an abstract getter, but make a collection returned from the getter unmodifiable. If a user wants to expose the modifiable version of such a collection, the getter should be implemented manually.

beikov commented 6 years ago

This is actually a very big task. Being able to do dirty tracking for normal classes requires quite some effort. The most important part is to avoid loading the class into a class loader, as we can't redefine it the way we'd. Class redefinition only allows to change method bodies, but not add fields/methods/superclasses.

The view mapping API has to be extended to allow working with FQNs. Internally, it mustn't use class objects. Since we still need to process annotations, we'd likely need something like e.g. Jandex for type structure introspection.

Next, we have to think of a different discovery mechanism for the types. The current CDI/Spring extensions rely on class objects, so these won't work for updatable normal classes. In CDI we could use the BeforeBeanDiscovery event since at that point, classes aren't loaded yet. At that point, we would have to let the Jandex indexer run on the classpath so that we can find the classes and build the entity view configuration from that information. Maybe we can even use a pre-existing Jandex index from the environment. In order for this to work, the JAR containing the entity view classes mustn't have a beans.xml, otherwise CDI will load the classes during type discovery. Note that this implies that only one of the two extensions, either the one described here or the already existing CDI extension can be used. Another possibility for discovery is to introduce a XML configuration and "register" the classes by their FQN there. This still requires that the classes aren't actually loaded before bootstrap finishes.

Finally, we have to do the enhancement before doing the metamodel building, as the metamodel requires the actual class objects. A java agent should make the Instrumentation object somehow available to an application class. The java agent registers a ClassTransformer which checks if a specific class is defined and uses the supplied class loader to lookup a class on which the Instrumentation is installed as static value. The application code explicitly triggers the loading of that class to initialize the Instrumentation and afterwards registers a special ClassTransformer that knows the view mapping. That tranformer is responsible for defining the enhanced classes. After the bootstrap finished, the ClassTransformer should be removed. If HotSwap requires re-transformation of the original byte code, the transformer should be removed only before undeploying.

beikov commented 4 years ago

Note that support for read only view was added through https://github.com/Blazebit/blaze-persistence/issues/1078