GeneratedColumnExample: immediate update of generated columns broken ?

I am looking at com.vaadin.demo.featurebrowser.GeneratedColumnExample

When I type values and tab between editable cells, the generated columns are
not
updated immediately.
They are only updated when the table leaves editable mode, contrary to what I read in the book at top of page 85.

Is this a regression ? Have things changed since the book came out and is there different magic to get column recalculation to trigger immediately ?

Only non-generated columns are listened to for property value change events.
This normally makes sense, as change events from generated columns (affecting other generated columns etc.) could trigger a cascade of updates.

To have the generated columns updated immediately, use normal columns instead of generated ones for the source data, and use a TableFieldFactory when needed.

I created a ticket (http://dev.vaadin.com/ticket/3158) to clarify the documentation on this.

I was not referring to changing the generated columns (I understand the danger of cascades). However I still believe that the example is actually broken.

In the book example, the editable cells
are
indeed normal columns. But under recent builds, there are no updates at all in the generated columns when the editable columns are changed. Note that toggling the editable status does cause the recalculation.

You are correct: the Table component no longer listens to property value change notifications from Component instances used as table cell values. This is always the case when a FieldFactory is used. The behavior changed in
http://dev.vaadin.com/ticket/2823
.

Here is one way to force updates also when in editing mode (borrowed from
PropertyValueChange test
):

public Component generateCell(Table source, Object itemId,
        Object columnId) {
    final Label l = new Label();
    final Property integer = source.getContainerProperty(itemId,
            "integer");
    l.setValue(getMultipliedValue(integer));

    // we must hook value change listener to ensure updates in all use
    // cases (eg. edit mode)
    if (integer instanceof Property.ValueChangeNotifier) {
        Property.ValueChangeNotifier notifier = (Property.ValueChangeNotifier) integer;
        notifier.addListener(new ValueChangeListener() {
            public void valueChange(ValueChangeEvent event) {
                l.setValue(getMultipliedValue(integer));
            }
        });
    }
   return l;
}

It should be easy to generalize this into a base class implementing ColumnGenerator to be used in all generated columns that should be updated immediately. Note that this only updates the value for the changed row, not the whole table.

Thanks. The wording around pages 84 and 85 of the book needs a serious rewrite then. The new behaviour breaks a useful piece of scaffolding magic, sigh.

I believe this code may be flawed. You are fetching the value from the container. But there is no guarantee that the container will give me the same Item as was used when constructing the table. In fact, this dangerous behaviour is exactly what happens with HbnContainer, as far as I can tell.

Adding traces to EntityItemProperty to see why my listeners were NOT being triggered, I noticed that there was indeed a value change event firing, on a property with the correct value, in the correct container, but that property was not the one that I had retrieved with Property genderProp = table.getContainerProperty(itemId, "gender"); and on which I had set my listener. In order to determine this, I used System.identityHashCode(obj) which gives a different identifier to different objects which hashcode might confuse.

The problem is that HbnListener uses “getItem” to retrieve the property, which ultimately does a “new EntityItem()”. So getContainerProperty is not a reliable way to set a listener on the property that underlies a table cell, as this creates a new set of properties, connected to the same pojo managed by Hibernate.

Hi!

I’d say the issue is in HbnContainer. It should return existing EntityItem if on just exists. But on the other hand it should effectively release memory, so simple hashmap caching is not the way to go. We’d probably need some WeakReference magic here to make this right, but I have heard that Terracotta does not like at least WeakHashMap.

If you find a good solution for this, please post me a patch!

cheers,
matti

In the case of Hibernate, it is actually even worse! as far as I have read, and as far as I have seen, the pojos underlying two different HbnContainers are actually different in two queries, even if obtained by the same session manager. The magic takes place in the “merge” routine, as far as I can tell.

In my specific case, I made my business objects’s setter routines fire an event which lists the updated properties. Listeners take that and update themselves as they wish. In my application, there are many things to update (up to 10 items) when any one input is changed, and the rules are pretty complicated. It is much simpler to let the business layer know all these rules, rather than having to duplicate the listening logic in the user interface.