One way to have more control over updates is to create proxies for all objects retrieved through the ORM layer which intercept all calls to the entity object. By intercepting these we can know exactly what data is accessed and what data is changed, and this could help with easier models.
Making the proxies themselves is not hard. DomUI already uses the QDataContext/QCriteria framework for all database access, so it is very easy to layer a "special" QDataContext above the real one. The special one acts as a delegate to the original but creates suitable proxies for all entity instances retrieved. These proxies are then returned instead of the originals.
Making the proxies themselves requires the help of a bytecode library like cglib (old), javassist or bytebuddy. There is a Proxy class in the JRE which can make dynamic proxies but as usual it's severely handicapped: it can only create proxies for interfaces, not for concrete classes - so it is useless for this task.
We want proxies so that we can do one or more of the following:
Most of these are not too hard. The last one, however, needs a bit of further thought.
Once we have the proxy we need to monitor access to the data from the original. But there is trouble here...
Key concept: intercept all getters and setters, and where needed replace the data gotten with different copies. We can use this to replace lists with ObservableLists and ManyToOne relation properties to a proxy of that parent.
Take for example the wish to wrap all List properties into an Observable list, so that any change made to it propagates to the UI immediately. This can be done relatively easily:
We can do something similar when the property's setter is called: if it is called with a non-observable list we can wrap it and put that into the property. This will have side effects though: if the original list is changed after the observable is created this will not send events (as the observable list does not see the changes since they are not made through it). This however should not be an issue in most cases: setting a whole new list will cause all bindings to refresh completely, and this is done after the logic has made the changes; hence no change events are needed for this case.
We want to make sure that all entities reachable from the proxy are themselves proxies. We register these with the delegate context so that each entity instance is associated with only one proxy. This creates an "access map" of data: all data accessed through the context will become known, and this limits the data we need to check for changes at commit time.
As long as the Entity class contains only getters and setters directly mirrored by fields this approach should work fine. But it goes south very quickly when the entity object contains other methods. Take for instance an entity class that has a toString() method which renders the values of its fields as a string. There are two ways to handle this:
The only way around this seems to be the following:
The latter solves a lot of issues. We can now store the proxy data inside the real fields of the proxy, and this means that any method inside the proxy that uses those fields will now work and use the correct (replaced) data. This also saves us from the need to create separate storage for the fields in the
One way to call toString() works so we have no choice but to delegate it to the original. But the original also has its original values (not the ones replaced) so toString renders old data.