This is a thread for discussing the LazyQueryContainer and reporting bugs/suggestions. Short description of the container:
[i]
Lazy Query Container is a Vaadin framework addon which provides lazy loading container for Vaadin tables. Lazy Query Container supports buffered reads and writes of items. Data reads and writes are delegated through Query interface to an application specific business delegate.
The QueryFactory and Query interface implementations are designed to be application specific and should invoke a data source like WebService client, JDBC connection, JPA context or Java service class to read and write data items. The data source has to support querying data in batches.
Lazy Query Container supports the following features: lazy loading of items, loading Items in batches, item caching with limited cache size, sorting, query statistics, changing item set and changing property set. Since version 1.1.0 Lazy Query Container also supports adding items, modifying items and removing items. The changes are buffered and can be either commited or discarded. Row status icons and status column generator are also included to allow for visualization of row states. See the demo for hands on experience on the features.
[/i]
Hi there,
nice container. Works fine for me ! (without sorting)
But when I try to sort I get a bit confused.
In your MockQuery you build up your (Mock)Database and store it in the instance variable
items . This is like getting all Objects from the service layer and store them in the Query Object. All this is done in the constructor.
Then sorting is done afterwards in the constructor.
The
getItem method only gets chucks of the sorted item array.
This means that sorting onyl works if the
items array is filled in the constructor.
But where is the lazy loading then ?
My Implementation of Query is making a call to the service layer each time when
getItem is called and gets the requested Objects only. There was no need for an instance variable
items .
But how to implement sorting then ? Do I have to pre-fetch my Objects in the constructor ?
Hello again,
writing the previous post helped me to abstract a bit.
Your MockQuery confused me a bit, but I have a solution now.
The Query implementation is responsible of storing the items and knowing
when to get more (aka. lazy loading).
Here is pseudo Code of what I have done.
public class PseudoQuery implements Query {
private List items;
private final Object[] sortPropertyIds;
private final boolean[] ascendingStates;
public PseudoQuery(Object[] sortPropertyIds, boolean[]
ascendingStates) {
this.sortPropertyIds = sortPropertyIds;
this.ascendingStates = ascendingStates;
//Initialy load 20 objects
fillItems(0, 20);
}
private fillItems(int startIndex, int count) {
//Ask service to load objects from startIndex to startIndex + count and store it in items
if (this.sortPropertyIds.length != 0) {
//sort items
}
}
public List<Item> getItems(int startIndex,int count) {
if ((startIndex + count) > this.items.size()) {
//load some more items (hello lazy loading)
fillItems(this.items.size(), 20);
}
//get the requested chunk form items
}
public int size() {
//Ask service for total number of objects
}
}
You got it right :). You could leave it the initial load of 20 items in the constructor. If you have any improvement ideas please let me know or fork the repository in github if you feel like tweaking the code directly.
Nice job with the transparency in your picture btw.
Next thing I’m trying to do is make the items in the container editable.
Another thing is, that the initial size to load in the query should be parameterized. Maybe the batchSize of the view should be passed to the query or the queryDefinition. So the Query implementor knows how many Objects he initially should load.
I think this is getting a very usefull Container espacially for bigger datasets. Do you think it is possible to extend this container for use with a HierachicalContainer suitable for tree widgets?
I got a tree widget containing lots of data. With every tree level i like to execute different database queries to load child elements. For example Customers on root level, shoporders as children and, duno, order positions at third level.
For an application programmer following interface could be nice:
Nice update with the batch size and I just pulled the commit to the main branch.
I would not add possibility to change the batch size though (setter) as it can result in odd situations if the batch size changes. Best to keep it immutable and maybe let the query definition be the only storage location for the value. Would you refactor it so that the other objects delegate the batch size value getter and setter to the query definition?
Good luck with the editable part by the way. That would be useful improvement for me too :).
I have not used Hierarchical Container myself so I don’t know how directly Lazy Query Container is applicable but anyone in the know is welcome to the sources. At least principles and design patterns can be reused. Lazy HC implementation would be a treat.
I seem to have added setter for batch size myself to the LazyQueryView but in hindsight it is probably better to just have the batch size defined solely in the constructor of a QueryDefinition implementation and add only a getter to the QueryDefinition interface.
Yes, making it immutable is much better.
I changed it in the Interface and DefaultQueryDefinition.
I also needed the constructor
public LazyQueryContainer(QueryDefinition queryDefinition, QueryFactory queryFactory)
Volker
I have been starting on this one.
I’ve created a new Branch “addItem” in my fork of your project.
I was playing arround a bit and at the moment the Container tells the QueryView that an item should be added.
The QueryView tells the Query and the Query implementation needs to knows how to create an new Item.
Maybe Query and QueryView is the wrong place for this. A new ItemFactory or something would probably be better.
The MockQuery also needs something like a static data store, because at the moment adding has no effect, because a new MockQuery is created each time.
Changed the MockQuery and MockQueryFactory according to your idea so they should be able to track the changes made through the UI now if you manage implement the middle layer.
The design of table component depends strongly on its container size() method to decide when to request a new batch of data (delegates to Query) when scrolling. That is, if there is no size provided in the container which is the total number of items in the data source, the table will show an empty list.
Here’s coming my problem…
I have no way to find out the total number of items in my data source (no sql used, but web services). I only have a flag “hasMore” which tells there’s more items to request.
Does anybody have any idea how to overcome such a problem. Is it possible to make a table to load the next page(batch) of data only by flag “hasMore”.
I’ve got the same problem, I only have an estimated size of how many items there could be.
I made the size of my Lazy Container variable. I set it to something like tablePageLength * 3. And if the container reaches the end of the size, it is increased if there are more items. You have to take care about outOfBounce exceptions with the items.
The rendering of the table is a bit odd sometimes. Especially the scroll bar in the table container.
But altogether it works fine.
how do you handle the situation when the increased size of the container bigger than the number of items returned with the query? I have empty rows in the table.
Hmmm, I can’t remember having empty rows. I only had this solution in my sandbox, meanwhile I make an extra query to get the size (I know it’s very bad, but in my use-case it is not very costly).
But as far as I can remember, I increased the size when the getItem(int) Method wanted to get an item that was bigger than the current size. But the increasing was stopped when hasNext was false.
You would need the following pattern to be implemented in the web service API for proper paging:
int countObjects(SearchCriteria searchCriteria);
List<Object> getObjects(SearchCriteria searchCriteria, int startIndex, int batchSize);
If you can not affect the web service API then you can do as you discussed earlier. In addition when you hit the end of result set you can refresh the container which reconstructs the query and apply then the observed result set size as the query size. In this approach the scroll bar seems to behave oddly from the users perspective though.
You could also try to get reasonable amount of objects with the first fetch in the query construction face to get a fixed size in case there are only a few rows in the result set.