table.addValueChangeListener returns id not bean when using jpacontainer

Hi,
I’m adapting a master detail example to use jpacontainer, but I get error when I try to get my bean calling event.getProperty().getValue();

table.addValueChangeListener(new ValueChangeListener() {
    private static final long serialVersionUID = 1L;

    @Override
    public void valueChange(ValueChangeEvent event) {
        Country country = (Country) event.getProperty().getValue(); // <--- ERROR
        editCountry(country);
    }
});

event.getProperty().getValue() actually returns the id of the bean.

Do I need to fetch it from db everytime row is changed? Is there a faster way as all data are already in the table? (table.getValue return the id too).

This is the complete class (version with valueChange working using id).

import com.vaadin.addon.jpacontainer.JPAContainer;
import com.vaadin.addon.jpacontainer.JPAContainerFactory;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.util.BeanItem;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;

import evending.model.geographic.Country;

public class CountryView extends VerticalLayout implements View {

    private static final long serialVersionUID = 1L;
    
    Table table;
    FormLayout form;
    JPAContainer<Country> container = JPAContainerFactory.make(Country.class, "shop");
    FieldGroup fieldGroup;
    private HorizontalLayout tableControls;
    private HorizontalLayout formControls;

    public CountryView() {
        setSpacing(true);
        setMargin(true);
        addComponent(buildTableControls());
        addComponent(buildTable());
        addComponent(buildForm());  
        addComponent(buildFormControls());
    }
    
    @Override
    public void enter(ViewChangeEvent event) {
    }
    
    private Table buildTable() {
        table = new Table(null);
        table.setWidth("500px");
        table.setSelectable(true);
        table.setImmediate(true);
        updateTableData();
        table.addValueChangeListener(new ValueChangeListener() {
            private static final long serialVersionUID = 1L;

            @Override
            public void valueChange(ValueChangeEvent event) {
                Long id = (Long) event.getProperty().getValue();
                Country country = (Country) container.getItem(id).getEntity();
                editCountry(country);
            }
        });
        
        return table;
    }
    
    private void editCountry(Country country) {
        if (country == null) {
            country = new Country();
        }
        BeanItem<Country> item = new BeanItem<Country>(country);
        fieldGroup.setItemDataSource(item);
    }
    
    private Component buildForm() {
        form = new FormLayout();
        BeanItem<Country> item = new BeanItem<Country>(new Country());
        fieldGroup = new FieldGroup(item);
        form.addComponent(fieldGroup.buildAndBind("code"));
        form.addComponent(fieldGroup.buildAndBind("name"));
        return form;
    }
    
    private Component buildTableControls() {
        tableControls = new HorizontalLayout();
        Button add = new Button("Add",
                new Button.ClickListener() {
            private static final long serialVersionUID = 1L;

            @Override
            public void buttonClick(ClickEvent event) {
                editCountry(null);
            }
        });
        
        Button delete = new Button("Delete", new Button.ClickListener() {
            private static final long serialVersionUID = 1L;

            public void buttonClick(ClickEvent event) {
                container.removeItem(table.getValue());
                updateTableData();
            }
        });
        
        tableControls.addComponent(add);
        tableControls.addComponent(delete);
        return tableControls;
    }

    private Component buildFormControls() {
        formControls = new HorizontalLayout();
        Button save = new Button("Save", new Button.ClickListener() {
            
            private static final long serialVersionUID = 1L;

            public void buttonClick(ClickEvent event) {
                try {
                    fieldGroup.commit();
                    @SuppressWarnings("unchecked")
                    BeanItem<Country> item = (BeanItem<Country>) fieldGroup.getItemDataSource();
                    container.addEntity(item.getBean());
                    updateTableData();
                    editCountry(null);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        
        Button discard = new Button("Discard", new Button.ClickListener() {
    
            private static final long serialVersionUID = 1L;
    
            public void buttonClick(ClickEvent event) {
                fieldGroup.discard();
            } 
        });
        
        formControls.addComponent(save);
        formControls.addComponent(discard);
        return formControls;
    }
    
    private void updateTableData() {
        table.setContainerDataSource(container);
        table.setVisibleColumns("code", "name");
        table.setColumnHeaders(new String[] { "Code", "Name" });
        table.sort(new Object[] { "code" }, new boolean[]
 { true, true });
    }
}

Hi,

yes, in JPAContainer the value is actually the ID of the entity. So to fetch the actual entity you need either access to the JPAContainer instance (where you can get it with the getItem method), or you can re-fetch the entity from the database.

This is the way it’s done also in the “Address book with JPAContainer” example.

Does this pose an issue to you?

-tepi

Hi! Thank you. Actually I’m using getItem, the code is this:

Long id = (Long) event.getProperty().getValue();
Country country = (Country) container.getItem(id).getEntity();
editCountry(country);

The issue is that this code executes a query, according to the log of tomcat. This could slow down the server.

Ah, ok then.

I don’t know the JPAContainer that well, but I think the default EntityProvider does not do any caching so each fetch will indeed hit the database. Please try using one of the caching providers (e.g. CachingLocalEntityProvider).

-tepi

I thought that caching was only for update purpose. I will try. Thank you.

Not 100% sure but at least by looking at the code of that item it seems to first hit the cache on getItem call, and if the item is not there it will fetch it and add it to the cache.

Oh… my fault. Standard container (that one created with make) is cached. I start with emply table (persistance delete it) and add some new entity. Then the first time I access them they are fetched from db, but next time they are fetched from cache without any query. Sorry. Thank you very much for help.