Making use of AbstractBackEndHierarchicalDataProvider, issue

Hi there,

I have a weird problem with AbstractBackEndHierarchicalDataProvider<T, F>.
It behaves as it should, except on the very first node, which cannot be expanded (also, traces show that it finds the childrens).
Trying to expand the first node resets the tree (all other expanded nodes get collapsed)
I am quite sure I misunderstood something.
I will explain what I’ve done in details and hopefully one can point my mistake (I am totally new to Vaadin).

I have a hierarchy structure relying on persistent objects (JPA) and I want to make use of lazy loading to display it in a Tree or TreeGrid.
So far it works (tree and treegrid), but the very first node of the hierarchy does not want to expand. The other nodes at the same top level expands as expected.
I am using Vaadin 8.1.6 and Java 8.

The Java inheritance scheme is the following :

@MappedSuperclass
abstract class AbstractEntity
(manages unique id)

@MappedSuperclass
abstract class NoeudTaxonomie extends AbstractEntity
(defines interface)

And here are the actual nodes of the hierarchy:

@Entity
class TypeVeille extends NoeudTaxonomie

@Entity
class Thematique extends NoeudTaxonomie

@Entity
class Domaine extends NoeudTaxonomie

@Entity
class SousDomaine extends NoeudTaxonomie

@Entity
class Paragraphe extends NoeudTaxonomie

Basically NoeudTaxonomie is a node abstract class usefull to manipulate its inherited members as an abstract type.

The hierarchy structure is the following (top to bottom):

TypeVeille
^ (0 to n)
Thematique
^ (0 to n)
Domaine
^ (0 to n)
SousDomaine
^ (0 to n)
Paragraphe

and is implemented with a field class with @OnetoOne relation to its parent.

For instance, this is the source code of Domaine.java

[font=Courier New]
@Entity
@Indexed
@Getter @Setter
public class Domaine extends NoeudTaxonomie {

public final static String NODETYPE = “D”;

@Field(index=Index.YES)
private String label;

// parent node
@OneToOne @JoinColumn(name=“thematique_id”)
private Thematique thematique;

@Override
public String getNodeType() {
    return NODETYPE;
}

@Override
public String getAbsoluteId() {
    return NODETYPE + String.valueOf(getId());
}

@Override
public NoeudTaxonomie getParent() {
    return thematique; 
}

}
[/font]

The other @Entity class follow the same scheme.

I have defined the JpaRepositories in order to fetch the tree data from the database.
TypeVeilleRepository, ThematiqueRepository, etc. One for each node type.

For example
(The other repository are written on the same scheme):

[font=Courier New]
public interface DomaineRepository extends JpaRepository<Domaine, Long> {

@Query("FROM SousDomaine d WHERE d.domaine = ?1")
List<SousDomaine> findChildrenOf(Domaine domaine);

}
[/font]

The this the test code in the init method of the view :

[font=Courier New]
NoeudTaxonomieDataProvider dataProvider = new NoeudTaxonomieDataProvider(typeveilleRepository,thematiqueRepository,domaineRepository,sousdomaineRepository,paragrapheRepository);
Tree tree = new Tree<>();
tree.setDataProvider(dataProvider);
tree.addSelectionListener(new SelectionListener() {
@Override
public void selectionChange(SelectionEvent event) {
if(event.getFirstSelectedItem().isPresent())
System.out.println(“Event: selected node=” + event.getFirstSelectedItem().get().getLabel());
}
});
this.addComponent(tree);

    TreeGrid<NoeudTaxonomie> treegrid = new TreeGrid<>();
    treegrid.setDataProvider(dataProvider);
    treegrid.addColumn(NoeudTaxonomie::getAbsoluteId).setCaption("Id");
    treegrid.addColumn(NoeudTaxonomie::getDateCreation).setCaption("Creation date");
    this.addComponent(treegrid);

[/font]

And now comes the definition of the data provider. I assume the problem comes from here.
For a reason I ignore, getChildCount and fetchChildrenFromBackEnd get called twice.

[size=3]
[font=Courier New]
@SpringComponent
@SuppressWarnings(“serial”)
public class NoeudTaxonomieDataProvider extends AbstractBackEndHierarchicalDataProvider<NoeudTaxonomie, String >{

private final TypeVeilleRepository typeveilleRepository;
private final ThematiqueRepository thematiqueRepository;
private final DomaineRepository domaineRepository;
private final SousDomaineRepository sousdomaineRepository;
private final ParagrapheRepository paragrapheRepository;

@Autowired 
public NoeudTaxonomieDataProvider(
            TypeVeilleRepository typeveilleRepository, 
            ThematiqueRepository thematiqueRepository, 
            DomaineRepository domaineRepository, 
            SousDomaineRepository sousdomaineRepository,
            ParagrapheRepository paragrapheRepository)    {
    
    this.typeveilleRepository = typeveilleRepository;
    this.thematiqueRepository = thematiqueRepository;
    this.domaineRepository = domaineRepository;
    this.sousdomaineRepository = sousdomaineRepository;
    this.paragrapheRepository = paragrapheRepository;
    
}

@Override
public int [u]


getChildCount

[/u](HierarchicalQuery<NoeudTaxonomie, String> query) {
System.out.print("getChildCount : ");
return (int) fetchChildren(query).count();
}

@Override
public boolean [u]


hasChildren

[/u](NoeudTaxonomie item) {
System.out.println("hasChildren? " + item.getAbsoluteId() + " : " + item.getLabel());
// We call the specialized node repository according to the type of the node
switch(item.getNodeType().charAt(0)) {
case ‘V’: return typeveilleRepository.findChildrenOf((TypeVeille)item).size()>0;
case ‘T’: return thematiqueRepository.findChildrenOf((Thematique)item).size()>0;
case ‘D’: return domaineRepository.findChildrenOf((Domaine)item).size()>0;
case ‘S’: return sousdomaineRepository.findChildrenOf((SousDomaine)item).size()>0;
default: return false;

    }
}

@Override
protected Stream<NoeudTaxonomie> [u]


fetchChildrenFromBackEnd

[/u](HierarchicalQuery<NoeudTaxonomie, String> query) {
Optional op = query.getParentOptional();

if(op.isPresent()) // trace
        System.out.print("fetchChildrenFromBackEnd: " + op.get().getAbsoluteId() + " " + op.get().getLabel() );
    else
        System.out.print("fetchChildrenFromBackEnd: parentNode==null");
                
    if(query.getParent()==null) { // fetching all top level nodes (all these nodes have no parent by definition).
        List<TypeVeille> res  = typeveilleRepository.findAll();
        System.out.println(" number of top level nodes=" + res.size() );
        
        return res.stream().map(s -> { return (NoeudTaxonomie) s; }); // casting to super type for the Stream
    }
    
    
    List<? extends NoeudTaxonomie> res = null;
    
    switch(query.getParent().getNodeType().charAt(0)) {
      case 'V': res = typeveilleRepository.findChildrenOf( (TypeVeille) query.getParent()); break;
      case 'T': res = thematiqueRepository.findChildrenOf( (Thematique) query.getParent()); break;
      case 'D': res = domaineRepository.findChildrenOf( (Domaine) query.getParent()); break;
      case 'S': res = sousdomaineRepository.findChildrenOf( (SousDomaine) query.getParent()); break;
      default: res = new ArrayList<NoeudTaxonomie>(); break;
    }
    System.out.println(" res.size=" + res.size());
// Just casting to supertype for the stream (@MappedSuperclass)
    return  res.stream().map(s -> { return (NoeudTaxonomie) s; });
    
}

}
[/font]
[/size]

Did someone already experienced such an issue ? If so, how to solve it ?!

Thank you very much for your help,
Elie

Copy of the trace when displaying the tree the first time:

getChildCount : fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
getChildCount : fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
hasChildren? V1 : Type de Veille 0
hasChildren? V2 : Type de Veille 1
hasChildren? V3 : Type de Veille 2
hasChildren? V4 : Type de Veille 3

Copy of the trace when trying to expand the 2nd top level:
getChildCount : fetchChildrenFromBackEnd: V2 Type de Veille 1 res.size=4
fetchChildrenFromBackEnd: V2 Type de Veille 1 res.size=4
getChildCount : fetchChildrenFromBackEnd: V2 Type de Veille 1 res.size=4
fetchChildrenFromBackEnd: V2 Type de Veille 1 res.size=4
hasChildren? T5 : Thematique 1.0
hasChildren? T6 : Thematique 1.1
hasChildren? T7 : Thematique 1.2
hasChildren? T8 : Thematique 1.3
hasChildren? V2 : Type de Veille 1

Copy of the trace when trying to expand the 1st (problematic) node:
hasChildren? V1 : Type de Veille 0
getChildCount : fetchChildrenFromBackEnd: V1 Type de Veille 0 res.size=4
fetchChildrenFromBackEnd: V1 Type de Veille 0 res.size=4
getChildCount : fetchChildrenFromBackEnd: T1 Thematique 0.0 res.size=4
fetchChildrenFromBackEnd: T1 Thematique 0.0 res.size=4
getChildCount : fetchChildrenFromBackEnd: D1 Domaine 0.0.0 res.size=4
fetchChildrenFromBackEnd: D1 Domaine 0.0.0 res.size=4
getChildCount : fetchChildrenFromBackEnd: S1 SousDomaine 0.0.0.0 res.size=4
fetchChildrenFromBackEnd: S1 SousDomaine 0.0.0.0 res.size=4
getChildCount : fetchChildrenFromBackEnd: P1 Paragraphe 0.0.0.0.0 res.size=0
fetchChildrenFromBackEnd: P1 Paragraphe 0.0.0.0.0 res.size=0
hasChildren? V1 : Type de Veille 0
getChildCount : fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
hasChildren? V3 : Type de Veille 2
hasChildren? V4 : Type de Veille 3
getChildCount : fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
getChildCount : fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
getChildCount : fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
getChildCount : fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
fetchChildrenFromBackEnd: parentNode==null number of top level nodes=4
hasChildren? V1 : Type de Veille 0
hasChildren? V2 : Type de Veille 1
hasChildren? V3 : Type de Veille 2
hasChildren? V4 : Type de Veille 3

If you can reduce your example to a smaller size and it still keeps failing, I’d recommed creating a bug ticket at
github.com/vaadin/framework/issues

-Olli

Hi Elie,
I am facing the exact same issue, did you manage to fix it?

Saif.

Hi Saif,
No because I gave up on Vaadin and switched to Angular 5/devextreme + Spring on server side. I found out a much more reactive community.
Good luck!