среда, 5 февраля 2014 г.

Vaadin 6.xx, решение проблемы с обновлением данных в таблицах

Здравствуйте!

Только что нашел решение (и успешно внедрил его в свой проект) старой (для меня) проблемы.

Суть заключалась в следующем.
Пользователь переводил таблицу в режим редактирования и менял в ней значения.
При этом, каждое изменение тут же записывалось в БД и сохранялось в ней.
Так же это значение сохранялось и на экране пользователя, в его таблице.
Но!
Как только пользователь переходил (к примеру в таблице 3 строчки) сначала от первой ко второй, от второй к третьей и затем обратно к первой, но значения менял уже в другой ячейке, данные в первой строчке первой редактируемой ячейке визуально оставались измененными, а в реальности, в представлении модели таблицы, значение в выбранной строке были все теми же. В результате, после выхода из режима редактирования, данные в ячейках, не сохранялись.
А точнее они сначала обновлялись, но затем обновлялись вновь на старые значения.

Мне удалось найти тикет, который описывает данный баг http://dev.vaadin.com/ticket/10993
Приведу скрины таблицы от исходных данных, до момента редактирования и его последовательности и получившийся результат.














Как вы видите, данные в первых двух столбцах не изменились, хотя в процессе они менялись, но при выборе первой строки, данные для объекта подтягивались не обновленные, а старые, то есть значения "1". В результате только данные из последнего столбца сохранялись в БД нужным образом.

Решение нашлось на форуме Vaadin, там же я нашел и ссылку на тикет с багом.
Я сделал просто - создал отдельный класс и стал вызывать его при каждом создании таблицы.
Привожу код класса:

import com.vaadin.data.Container;
import com.vaadin.data.Container.ItemSetChangeEvent;
import com.vaadin.ui.Table;
import java.util.Collection;

import java.util.Collections;
/**
 *
 * @author nix
 */
public class UpdateTableSelectionOnItemSetChange implements Container.ItemSetChangeListener {

    private Table table;
    
    
    public  UpdateTableSelectionOnItemSetChange(final Table table) {
        super();
        this.table = table;
    }

    @Override
    public void containerItemSetChange(ItemSetChangeEvent event) {
        final Collection<Object> itemIds = (Collection<Object>) table.getContainerDataSource().getItemIds();
        if (itemIds.isEmpty()) {
            // a previously filled table is now empty ==> remove its value since its selection is outdated this also
            // updates dependent forms via a resulting value change event
            table.setValue(null);
        } else {
            final Object tableVal = table.getValue();
            if (tableVal instanceof Collection && tableVal != null && !((Collection) tableVal).isEmpty()) {
                final Object selectedItemId = ((Collection<Object>) tableVal).iterator().next();
                // FIXME: needs to be adapted for multiselect tables

                for (final Object itemId : itemIds) {
                    if (itemId.equals(selectedItemId) && itemId != selectedItemId) {
                        table.setValue(null); // otherwise the next set value will be discarded since
                        // oldVal.equals(newVal)
                        table.setValue(Collections.singleton(itemId));
                        break;
                    }
                }
            }
        }
    }
}

Теперь в класс где у нас создается табличка просто добавляем создание экземпляра этого класса. Конечно, лучше его указывать сразу в списке переменных и затем вызывать только конструктор.

UpdateTableSelectionOnItemSetChange onItemSetChange =  new UpdateTableSelectionOnItemSetChange(table);

Можно найти более элегантное применение, если у вас оно есть - поделитесь, лишним не будет. Я же пока оставлю так. Главное, что данные теперь всегда "свежие" :)