setValue() of select or combobox in Form using JPAContainer

For days I have been struggling with an issue that, to me, seems quite basic: how to set the value of a select or combobox containing
all
departments to the
current
department of a an employee.

I have an Employee class and a Department class. One of the properties of an employee is his/her Department. Employees are bundled in a JPAContainer as are the Departments. I use Hibernate as the persistence provider. All instances have autogenerated Id’s of the type long.

A form contains the properties of an employee (the employee selected in a table).

Without using a formfieldfactory all properties are shown, including his/her department. For now I even have two ways in which the department is shown:

  • the department itself (for instance ‘Department@7ab2f38f’, so the internal key), and
  • a nested property ‘department.name’ (for instance ‘Logistics’).

However I want a select or combobox containing all departments and showing the current department of the employee (giving the user the possibility to change the department the employee is assigned to). So I try to construct a formfieldfactory to accomplish this result. So far without succes.

I tried many different options (all in the listener connected to the employeetable). Depending on the option I tried, the selected department remains empty or I encounter errors (illegalArgument exceptions because java.lang.Long was expected, but com.vaadin.addon.jpacontainer.JPAContainerItem was given). Stripped to its core, my code now looks like this.


@Entity
public class Department {

	@Id @GeneratedValue
	Long id;

	public String name;	
	
	@OneToMany (mappedBy = "department")
	Set<Employee> employees;

@Entity
public class Employee {

	@Id @GeneratedValue
	Long id;

	public String name;
	
	@ManyToOne
	@JoinColumn
	Department department;
}

employeeTable.addListener(new Property.ValueChangeListener() {
			
			public void valueChange(ValueChangeEvent event) {				
				layout.addComponent(createForm());
			}
  	});

public Form createForm(){
		employeeForm = new Form();
		employeeForm.setFormFieldFactory(this);
		employeeForm.setItemDataSource(employeeTable.getItem(humanResourceTable.getValue()));				
		return employeeForm;
	}

public Field createField(Item item, Object propertyId, Component uiContext) {
		String pid = (String) propertyId;
		if ("name".equals(pid)) {
			final TextField employeeNameField = new TextField();
			return employeeNameField;
		}
		else if ("department".equals(pid)) {

			departments = JPAContainerFactory.make(Department.class, "PersistenceUnit");			
			departmentSelector = new Select();
			departmentSelector.setContainerDataSource(departments);
			Item selectedEmployee = employeeTable.getItem(employeeTable.getValue());
			
			departmentSelector.setValue(resourceGroups.getItem(selectedHumanResource).getEntity().getId());			
			
			return departmentSelector;
		}	
		return null;
	}

Can anyone point me in the right direction?

Hi,

Have you tried the FieldFactory that comes along with the JPAContainer? I have written a couple of
blog posts
about it.

The issue you are facing is rather common for new Vaadin developers. It happens especially easily with ORM solution like JPA and with a container that is using entity identifiers as item identifiers. You are mixing item identifiers with items. Vaadin select is a field who’s value is an identifier of an item in its container datasource. Fields are used to modify properties. Now the value of your property is Department, but the value of your select is a Long (an identifier of a Department).

The JPAContainer has built-in property translators that you can use with common cases. In this simple relation you could use SingleSelectTranslator. It automatically converts identifiers to entities and visa versa. It is also used by the built in FieldFactory that comes with JPAContainer.

cheers,
matti

Thanks Matti!

For others facing the same issue: here’s how I set the value of the select:


Employee selectedEmployee = employees.getEntityProvider().getEntity(employeeTable.getValue());
selectedDepartment = selectedEmployee.getDepartment();

departmentSelector.setValue(selectedDepartment.getId());

‘employees’ is my JPAContainer and ‘getId’ is a getter in my Department class.

Hi,

You can indeed to that as well, but you’ll end up in problems if you are using that select in a Form or directly set a property wrapping “Department” type to the selects property datasource.

This is how you can use a translator manually:

SingleSelectTranslator tr = new SingleSelectTranslator(departmentSelector);
departmentSelector.setPropertyDataSource(tr);
tr.setPropertyDataSource(propertyWrappingDepartment);

Now e.g. MethodProperty (from BeanItem) or JPAContainerItem.ItemProperty gets the correct value from the select (via SST). And for the opposite way, the existing value will be correctly shown on the select.

cheers,
matti

Hi Matti,

I have tried to implement your solution butt this is not working for me yet, what am i doing wrong. I have taken the default maven project made available by Vaadin.
What i have done is:

  • create new maven project in eclipse chosen vaadin-arcetype-jpacontainer version 2.0.0.
  • add FieldFactory to do the following:
    - generate a combobox instead of nativeselect
    - set the default value of the boss for a person.

The FieldFactory class i created.

public class AppFieldFactory extends FieldFactory {
private String persistenceUnit;

public AppFieldFactory(String persistenceUnit) {
	this.persistenceUnit = persistenceUnit;
}

@Override
public Field createField(Item item, Object propertyId, Component uiContext) {
	Field f = null;
	if ("boss".equals(propertyId)) {
		ComboBox boss = new ComboBox("Boss");
		JPAContainer<Person> container = JPAContainerFactory.make(Person.class, persistenceUnit);
		
		boss.setPropertyDataSource(new SingleSelectTranslator(boss));
	    boss.setContainerDataSource(container);
	    boss.setItemCaptionPropertyId("firstName");
	    
        Person defaultBoss = container.getEntityProvider().getEntity(new Long(1));
        Object firstItemId = container.firstItemId();
                
        /* 1)*/ boss.setValue(container.getItem(firstItemId).getEntity().getId());
         
        /* 2)*/ boss.setValue(defaultBoss.getId());
	    return boss;
	}else {
		// Use the super class to create a suitable field base on the
		// property type.
		f = super.createField(item, propertyId, uiContext);
	}
	return f;
}

}

Based on a quick look: the value of the field is set by the form (based on the item data source of the form) after the field factory is called, so whatever values you set there will be overridden before the user sees them.

Hi Henri,

You could be right, something i was totally not aware of, so any idea how i can avoid that this happens ?