The Background Binder deals with dependencies between things. An Entity cannot be fully bound until its attributes and identifier are fully bound. An Embeddable cannot be fully bound until its sub-attributes are fully bound. One Entity might depend on another by nature of a "key many to one" or a "one to one" or a "mappedBy" or... In short, there is a lot of dependency to the order things must be bound.
Some of these dependencies are *a priori* in nature. Basic attributes should be bound before embedded and associations. Attributes for a Entity/Embeddable need to be fully bound before the Entity/Embeddable can be considered complete. The Binder tries to handle these *a priori *dependencies simply by the order in which it processes things internally. This works reasonably well. The other dependencies are *a posteriori *in nature, in that we cannot fully understand them in terms of ordering until we get into the processing of the data. This includes "key many to one" or a "one to one" or a "mappedBy" examples from above. Turns out that binding of Entity and Embeddable are both functionally *a posteriori* as well because of the fact that we first create those bindings (as a shell if you will) and add the bindings for their attributes, etc after the fact. So we can't understand that either are fully resolved until after the fact. Not a huge deal, just pointing it out. The Use Case I have been working on completing identifier handling, especially in the case of composite ids. As more "background" to where my thoughts came from, let me walk y'all through how Binder currently deals with this stuff. As I mentioned above, as part of its *a posteriori* handling, Binder attempts to cycle through attributes based on their nature: 1. Process composites 2. Process simple/basic attributes 3. Process many-to-one 4. Process one-to-one 5. Process many-to-one (mappedBy) 6. Process one-to-one (mappedBy) 7. Process plural attributes 8. Process plural attributes (mappedBy) In terms of dealing with composite ids, step (1) really just means creating the Embeddable "shells" (the EmbeddableBinding instance). But at this point the EmbeddableBinding is not done, we still need its attributes "resolved" or "bound". To accomplish this, as Binder walks through the rest of the steps, it continually checks whether the completion of the attribute it just bound completes the binding of the Embeddable. So as it is looping over every attribute, for each attribute it loops over every known incomplete EmbeddableBinding and checks whether that attribute "completes" the EmbeddableBinding and if so finalizes it's binding. First thing I noticed is that that is a lot of looping (within looping). While I am sure that has some performance impact, that is not really my concern. My concern was more the non-definitive state of things being resolved. Coming back to the EmbeddableBinding as a composite id... quite a few of those steps rely on the id of an entity being fully resolved. Events Which got me to thinking about using events to signal the completion of things, and the ability to listen for these events. Don't worry, I mean events here as fairly light weight concept :) Essentially the idea is to have producers and listeners and to have Binder act as the bus. So let me apply this to the composite id case above as an example. So we'd still have an initial step that creates the EmbeddableBinding instances. It would use a EmbeddableBindingCreator delegate (I was already in the process of breaking up the 4K line Binder class to use some delegation) to do the EmbeddableBinding creation. EmbeddableBindingCreator would also implement the "listener" contract for AttributeBinding completion; as attributes are completed, if they are part of an EmbeddableBinding we "check them off" and when an EmbeddableBinding has no more unresolved sub-attributes we would finalize the EmbeddableBinding. Here, EmbeddableBindingCreator would also play a "producer" role; when the EmbeddableBinding is finalized, it would notify the bus of that. And then registered listeners for that event could react. Consider a nested composite (Embedded w/in an Embeddable): @Entity class Person { ... @Embedded Address address; } @Embeddable class Address { ... @Embedded Zip zip; } @Embeddable class Zip { .. String code; String plus4; } The initial step has EmbeddableBindingCreator create the EmbeddableBinding(Person#address) and the EmbeddableBinding(Person#address#zip). Additionally, EmbeddableBindingCreator registers the sub-attribute roles making up each EmbeddableBinding and keeps track of them (the "unresolved ones"). The second step processes basic attributes (note to self, ideally we should make sure the nested simple attributes are ordered first here): 1) Say first we'd process Person#address#zip#code; we finalize it and fire off the "attribute bound" event which EmbeddableBindingCreator gets notified of. EmbeddableBindingCreator removes the Person#address#zip#code attribute role from the unresolved attributes for EmbeddableBinding(Person#address#zip). However, we see there is still more unresolved sub-attributes, so nothing more to do. 2) Then we process Person#address#zip#plus4, finalize it and fire off the "attribute bound" event which EmbeddableBindingCreator gets notified of. EmbeddableBindingCreator removes the Person#address#zip#plus4 attribute role from the unresolved attributes for EmbeddableBinding(Person#address#zip). Now it sees that all of the sub-attributes for EmbeddableBinding(Person#address#zip) are resolved, and so finalizes EmbeddableBinding(Person#address#zip), firing off its own event that the embeddable was finalized. That event routes back to Binder, which directs it back to a listener for the Person#address attribute (which was registered as waiting on the EmbeddableBinding(Person#address#zip) as one of its attribute types). We finalize that attribute, and fire its completion event which again EmbeddableBindingCreator gets notified of... And so on FYI, the finalization aspect is important because it signals when we are able to resolve the Hibernate Type for things that refer to these. Concerns There are some concerns with such an approach. Here are the ones I had. Maybe y'all have others? First, there is the general pros/cons of sequential processing versus event-driven processing. Some folks view event-driven processing as more convoluted, harder to follow. Another concern is cases where a "bi-directional dependency" exists, which might lead to an indefinite wait. I do think adding some "finish" up step at the end of the initial 1-8 steps is important where each listener/producer is able to report things it is still waiting on. Anyway... thoughts? comments? _______________________________________________ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev