Error in MultiSelectListBox out of bounds

Hi,

I have two tables: department (id, name) and product (id, name, id_department) (hibernate - manytoone)

In the department I call all the items and as in a MultiSelectListBox with setItems

When I retrieve the product table and place it in the MultiSelectListBox using binder it gives the following error:

java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 3

I try with setValue, but same problem.

What is happening?

Could you show the relevant parts of your code (what you have tried exactly)? Otherwise it’s hard to imagine what exactly is going on and what could be going wrong.

Hi,

I did a little test. My data access class is here, but you can understand how it works

Master:


@NamedQuery(name = "product", query = "select t from productTest t order by t.name")

@Entity(name="productTest")
@Table(name = "productTest")
@Getter @Setter
public class ProductTest extends Model {

	private static final long serialVersionUID = 7412735202355089750L;
	@Column(name="name",length = 50, nullable = false)
	private String name = "";
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	private Set<DepartmentTest> departament = new HashSet<>();
}

Detail:


@NamedQuery(name = "department", query = "select t from departmentTest t order by t.name")

@Entity(name="departmentTest")
@Table(name = "departmentTest")
@Getter @Setter
public class DepartmentTest extends Model {

	private static final long serialVersionUID = 2798505907519151322L;
	@Column(name="name",length = 50, nullable = false)
	private String name = "";
	
}

Data:

insert  into `departmenttest`(`id`,`name`,`tz1user`) values (1,'Test1',0),(2,'Test2',0),(3,'Test3',0);
insert  into `producttest`(`id`,`name`,`tz1user`) values (1,'Product 01',0);
//Hibernate table join
insert  into `producttest_departmenttest`(`productTest_id`,`departament_id`) values (1,2);

Class:

public class DepartmentMultiSelectListBox extends MultiSelectListBox<DepartmentTest> {

	private static final long serialVersionUID = -5569052352237294032L;
	private Tz1Controller<DepartmentTest> departmenTz1Controller = new Tz1Controller<>(DepartmentTest.class);
	
	public DepartmentMultiSelectListBox() {
		setItems(departmenTz1Controller.setQueryNamed("department").getResult());
		setRenderer(new ComponentRenderer<>(column -> new Tz1Label(column.getName())));
	}

Main:

@Route("test")
public class MultiSelectTest extends VerticalLayout {

	private static final long serialVersionUID = 1320561879546625026L;

	private DepartmentMultiSelectListBox departmentMultiSelectListBox = new DepartmentMultiSelectListBox(); 
	private Tz1Controller<ProductTest> productTz1Controller = new Tz1Controller<>(ProductTest.class);
	private Button testButton = new Button("test me");
	public MultiSelectTest() {
		testButton.addClickListener(e -> testButtonClick());
		add(departmentMultiSelectListBox,testButton);
	}
	private void testButtonClick() {
		departmentMultiSelectListBox.setValue(productTz1Controller.setQueryNamed("product").getResult().get(0).getDepartament());
	}
}

If click in testButton, error:

08:20:56,486 ERROR [com.vaadin.flow.server.DefaultErrorHandler]
 (default task-2) : java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
	at java.base/java.util.Objects.checkIndex(Objects.java:372)
	at java.base/java.util.ArrayList.get(ArrayList.java:458)
	at deployment.proativo-1.0.war//com.vaadin.flow.component.listbox.MultiSelectListBox.lambda$presentationToModel$1(MultiSelectListBox.java:61)

It only works if SetValue contains the same objects as SetItems, but this is not how combox, listbox, etc … Try binder, it doesn’t work either.

I’ve tried it in several ways and this component still doesn’t work with Binder and, strangely, it’s not part of the documentation and examples on the vaadin website ([https://vaadin.com/components]
(https://vaadin.com/components)).

Well … the component goes to waste and I’m going to use another approach.

Sounds a bit like you’re running into this https://github.com/vaadin/vaadin-list-box-flow/issues/73

But then I guess the cause would be that the value you’re trying to set is not part of the items. Are you sure the items are set correctly so that it includes the value that you’re trying to set with setValue?

I also noticed in some places you have a typos with word “departament” instead of “department”. I’m not sure if this can be the problem (in case the relations don’t get solved correctly maybe?). In class ProductTest there is “departament”, in one table there is “departament_id” and in class MultiSelectTest you have call to “getDepartament()”. In most places it seems to be spelled correctly though.

Afaik it should work if you give setValue() the same instance (of your DepartmentTest object in this case) which is part of the set that you have set with setItems().

It should also work with equal objects that are not the same instance as long as your DepartmentTest.equals() method returns true for two objects referring to the same department, but apparently there’s a bug in MultiSelectListBox so this way is broken atm. Here’s a new issue that was just opened about it https://github.com/vaadin/vaadin-list-box-flow/issues/74

Hi,

I did this test quickly because it asked me for details, but it’s not the writing problem, I ran it here and it’s functional.

The problem is precisely that they are different objects, although the data is the same, the objects are different because they are retrieved from different tables and that is the problem.

If I have a table that has the data (complete registration data), and another that has a part of the data (user selection), then, it is normal to use the complete table and then take the table with the partial to show the user what he has already selected or not.

Even more normal is to use the binder for this task. But as I have shown, because data recovery is another object than the one initially placed on the object, soon Binder will present the problem I am reporting. What makes Binder this component completely unused.

To work the process, I would have to go through the objects in the component’s setItens, and check if they are the same as the id (primary key) of the objects retrieved in another data table, if so, make an array with these objects and then give the SetValue .

But if so, why Binder? And the job of doing this just for that component is out of the question.

And yes, this link is about what I’m saying https://github.com/vaadin/vaadin-list-box-flow/issues/74

Thanks