Even though the callback system in Cayenne 3 is simple to explain (http://cayenne.apache.org/doc/lifecycle-callbacks.html ) there are some details which can bite. I'd like to create an overview of how I think they currently work and what issues remain:

How we use them
---------------
* set createdOn attribute in all entities
* set modifiedOn attributed in all entities
* update calculated denormalised summary fields. For example, invoice lines are added and payments subtracted to create an invoiceOwing attribute. This needs to be updated whenever a relevant record is created or updated. * audit trail records. Every time a change is made to a record, a log entry is created (we actually use this for database replication). * firing special events - in our system when a new payment is created, special credit card processing code runs, all before the ROP client gets a return value from the context.commit().


Now, I understand that lifecycle is tied up tightly with JPA so some of the naming reflects names imposed by JPA. But hopefully Cayenne functionality could go further.


Cayenne 2 tier
--------------

For new objects :
context.newObject() -> prePersist()
context.commitChanges() -> postPersist()

For existing objects:
context.commitChanges() -> preUpdate(), postUpdate()



Cayenne 3 tier (ROP)
--------------------

For new objects :
context.commitChanges() -> prePersist(), postPersist()

For existing objects:
context.commitChanges() -> preUpdate(), postUpdate()


Object state
------------
prePersist() in 2 tier: object attributes and relationships are all null
prePersist() in ROP: object attributes populated but relationships always invalid

preUpdate(): attributes and relationships are all dependable. In addition, within the context being committed, we can follow a relationship from one object to another and see its new updated attributes.



Summary
-------

* prePersist() is only useful as a place to set object attributes (such as creationDate) since you cannot follow relations reliably in a ROP environment.

* postUpdate() and postPersist() are useful for changes which do not need to be committed atomically with the original commit. So good for creating log records, but not ideal for updating invoiceOwing.

* postPersist() is badly named. It is really postInsert()

* we need preInsert()


Additional callbacks
--------------------
preCommit() and postCommit() listeners on the context itself would be really useful. They should run AFTER the per object listeners have all run for that phase. This would be one per context and allow a single entry point which can examine and touch all objects in the context before commit or just after.


Additional ROP feature
----------------------
If an object is touched within a pre-commit phase on the server, the ROP client does not get the updated object refreshed automatically. It appears the response the client is either just the validation exception (if there is one) or "all OK". We are currently remembering to set the object on the client to be hollow, but it would be nice for the server to return all committed objects to the client.


I hope that I haven't tried to cover too much ground here. My purpose is to get some feedback about the above ideas, and about how hard they are to implement in Cayenne. Since this is a blocking issue for our main commercial project we are want to focus and getting these things clear in the Cayenne docs and making appropriate improvements.


Cheers
Ari Maniatis


-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001   fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A


Reply via email to