Hibernate, EMF objects and eclipse databinding – ensure you have an equals method
Ran into a few problems the other day around creating UI forms with EMF that is backed by Hibernate and Teneo. The jface databinding snippets almost always use strings as the model object, however, most model objects have embedded objects in them that then, down the road, relate back to user-readable strings. Hence the snippets don’t always show some nuances around the model objects.
Its important to realize that some of the jface databinding support classes, such as those producing object-to-label maps (which is why there are always IObservableMaps being used for everything are good in the sense that you can create a map between domain objects and the strings that should be displayed. These can be created automatically for you using jface databinding or you can create the maps directly yourself (no one really does that though). The real issues is to realize that many of these object-to-label maps that are created automatically use a backing map (hashtable) that hashes the object to provide the key. The key of course is your domain object of some sort. Well, for strings directly entered into your application, the strings all map to the same string and hence the same object at least form an equals() perspective. Hence, the map you create with the jface databinding classes always find the same object as the key and returns the string.
But with EMF, Hibernate and teneo in the background (actually just with any complex object that does not use a static list of objects or simple string values), for objects that are generated from a repository, those objects may be proxied or have a different notion of equals in them. So while a program like the following modified snippet may work fine and make you fell confident that everything is going well:
This may not actually work correctly if you use EMF objects until you define equals() (and ideally hashcode() of course). The key code area is the following code which loosely translate into “bind the current selection in the combo viewer (which is typically a combo in database applications that wish to show a relationship to another object through a key) and have that stay in sync with property on the current selection object in the master viewer (typically a table) , allowing any of the values that are valid values from the entire list of cities".”
This is the mostly classic code of producing a ocmbo picker that tracks the object used in the domain model with a contained object, such is the City object. ViewerSupport is actually creating the observabel map behind the scenes:
This is the snipped from eclipse’s ViewerSupport. You can see that it creates an observable map label provider. This is okay and useful for simple labels that are string properties directly off the model. If you need a more clever string, or something formatted differently, you have to produce your own ColumnLabelProvider and add the label provider directly yourself in code. But this works for standard properties that return user-readable strings. For other ways to create the observable map label provider see this blog from TomS: http://tomsondev.bestsolution.at/2009/06/27/galileo-emf-databinding-part-5/ .
If the input is a hibernate persistent array, the objects may be proxied. That’s okay in general, but you have to make sure that these objects understand how to hash and understand their own sense of equality. Using the above ViewerSupport code with a hibernate list will frustrate the combo because when it goes to find the object to use as the key to obtain the (mapped) user display string, the object may be proxied. The default equals returns false for comparing the object that is used to originally create the list and the object in your “master selection” that is the embedded domain object within your master selection. Those two objects will not match and your combo will not update its display with the proper domain object (such as City in the snipped above, although the snipped above works because it does not have this problem). The trick is to define equals() and hashcode() for your EMF based domain object. Then the example works.
The moral is that when the jface databinding framework uses maps to link domain objects to labels or images, ensure that you know when your domain object will return equals in a way that makes sense for application domain.
All of the snippets are located at this link http://wiki.eclipse.org/JFace_Data_Binding/Snippets and have to be downloaded from SVN here: dev.eclipse.org and project org.eclipes.jface.examples.databinding.