]> source.dussan.org Git - vaadin-framework.git/commitdiff
Migrate LazyQueryContainer
authorErik Lumme <erik@vaadin.com>
Thu, 14 Sep 2017 07:23:24 +0000 (10:23 +0300)
committerErik Lumme <erik@vaadin.com>
Thu, 14 Sep 2017 07:23:24 +0000 (10:23 +0300)
documentation/articles/LazyQueryContainer.asciidoc [new file with mode: 0644]
documentation/articles/contents.asciidoc [new file with mode: 0644]

diff --git a/documentation/articles/LazyQueryContainer.asciidoc b/documentation/articles/LazyQueryContainer.asciidoc
new file mode 100644 (file)
index 0000000..54e561f
--- /dev/null
@@ -0,0 +1,462 @@
+[[lazy-query-container]]
+Lazy query container
+--------------------
+
+[[when-to-use-lazy-query-container]]
+When to Use Lazy Query Container?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Typical usage scenario is browsing a large persistent data set in Vaadin
+Table. LQC minimizes the complexity of the required custom
+implementation while retaining all table features like sorting and lazy
+loading. LQC delegates sorting of the data set to the backend data store
+instead of sorting in memory. Sorting in memory would require entire
+data set to be loaded to application server.
+
+[[what-is-lazy-loading]]
+What is Lazy Loading?
+~~~~~~~~~~~~~~~~~~~~~
+
+In this context lazy loading refers to loading items to table on demand
+in batches instead of loading the entire data set to memory at once.
+This is useful in most business applications as row counts often range
+from thousands to millions. Loading more than few hundred rows to memory
+often causes considerable delay in page response.
+
+[[getting-started]]
+Getting Started
+~~~~~~~~~~~~~~~
+
+To use LQC you need to get the add-on from add-ons page and drop it to
+your projects WEB-INF/lib directory. After this you can use existing
+query factory (`JpaQueryFactory`), extend `AbstractBeanQuery` or proceed to
+implement custom `Query` and `QueryFactory`. Finally you need to instantiate
+`LazyQueryContainer` and give your query factory as constructor parameter.
+
+[[how-to-use-lazy-query-container-with-table]]
+How to Use Lazy Query Container with Table?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+LQC is implementation of Vaadin Container interface. Please refer to
+Book of Vaadin for usage details of Table and Container implementations
+generally.
+
+[[how-to-use-entitycontainer]]
+How to Use EntityContainer
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`EntityContainer` is specialized version `LazyQueryContainer` allowing easy
+use of JPA as persistence layer and supports defining where criteria and
+corresponding parameter map in addition to normal `LazyQueryContainer`
+features:
+
+[source,java]
+....
+entityContainer = new EntityContainer<Task>(entityManager, true, Task.class, 100,
+    new Object[] { "name" }, new boolean[] { true });
+entityContainer.addContainerProperty("name", String.class, "", true, true);
+entityContainer.addContainerProperty("reporter", String.class, "", true, true);
+entityContainer.addContainerProperty("assignee", String.class, "", true, true);
+whereParameters = new HashMap<String, Object>();
+whereParameters.put("name", nameFilter);
+entityContainer.filter("e.name=:name", whereParameters);
+
+table.setContainerDataSource(entityContainer);
+....
+
+[[how-to-use-beanqueryfactory]]
+How to Use BeanQueryFactory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`BeanQueryFactory` and `AbstractBeanQuery` are used to implement queries
+saving and loading JavaBeans.
+
+The `BeanQueryFactory` is used as follows with the Vaadin table. Usage of
+`queryConfiguration` is optional and enables passing objects to the
+constructed queries:
+
+[source,java]
+....
+Table table = new Table();
+BeanQueryFactory<TaskBeanQuery> queryFactory = new
+    BeanQueryFactory<TaskBeanQuery>(TaskBeanQuery.class);
+
+Map<String,Object> queryConfiguration = new HashMap<String,Object>();
+queryConfiguration.put("taskService",new TaskService());
+queryFactory.setQueryConfiguration(queryConfiguration);
+
+LazyQueryContainer container = new LazyQueryContainer(queryFactory,50);
+table.setContainerDataSource(container);
+....
+
+Here is a simple example of `AbstractBeanQuery` implementation:
+
+[source,java]
+....
+public class TaskBeanQuery extends AbstractBeanQuery<Task> {
+
+  public TaskBeanQuery(QueryDefinition definition,
+        Map<String, Object> queryConfiguration, Object[] sortPropertyIds,
+        boolean[] sortStates) {
+    super(definition, queryConfiguration, sortPropertyIds, sortStates);
+  }
+
+  @Override
+  protected Task constructBean() {
+    return new Task();
+  }
+
+  @Override
+  public int size() {
+    TaskService taskService =
+        (TaskService)queryConfiguration.get("taskService");
+    return taskService.countTasks();
+  }
+
+  @Override
+  protected List<Task> loadBeans(int startIndex, int count) {
+    TaskService taskService =
+        (TaskService)queryConfiguration.get("taskService");
+    return taskService.loadTasks(startIndex, count, sortPropertyIds, sortStates);
+  }
+
+  @Override
+  protected void saveBeans(List<Task> addedTasks, List<Task> modifiedTasks,
+    List<Task> removedTasks) {
+    TaskService taskService =
+        (TaskService)queryConfiguration.get("taskService");
+    taskService.saveTasks(addedTasks, modifiedTasks, removedTasks);
+  }
+}
+....
+
+[[how-to-implement-custom-query-and-queryfactory]]
+How to Implement Custom Query and QueryFactory?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`QueryFactory` instantiates new query whenever sort state changes or
+refresh is requested. Query can construct for example named JPA query in
+constructor. Data loading starts by invocation of `Query.size()` method
+and after this data is loaded in batches by invocations of
+`Query.loadItems()`.
+
+Please remember that the idea is to load data in batches. You do not
+need to load the entire data set to memory. If you do that you are
+better of with some other container implementation like
+`BeanItemContainer`. To be able to load database in batches you need your
+storage to provide you with the result set size and ability to load rows
+in batches as illustrated by the following pseudo code:
+
+[source,java]
+....
+int countObjects(SearchCriteria searchCriteria);
+List<Object> getObjects(SearchCriteria searchCriteria, int startIndex, int batchSize);
+....
+
+Here is simple read only JPA example to illustrate the idea. You can
+find further examples from add-on page.
+
+[source,java]
+....
+package com.logica.portlet.example;
+
+import javax.persistence.EntityManager;
+
+import org.vaadin.addons.lazyquerycontainer.Query;
+import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
+import org.vaadin.addons.lazyquerycontainer.QueryFactory;
+
+public class MovieQueryFactory implements QueryFactory {
+
+  private EntityManager entityManager;
+  private QueryDefinition definition;
+
+  public MovieQueryFactory(EntityManager entityManager) {
+    super();
+    this.entityManager = entityManager;
+  }
+
+  @Override
+  public void setQueryDefinition(QueryDefinition definition) {
+    this.definition = definition;
+  }
+
+  @Override
+  public Query constructQuery(Object[] sortPropertyIds, boolean[] sortStates) {
+    return new MovieQuery(entityManager,definition,sortPropertyIds,sortStates);
+  }
+}
+....
+
+[source,java]
+....
+package com.logica.portlet.example;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+import org.vaadin.addons.lazyquerycontainer.Query;
+import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
+
+import com.logica.example.jpa.Movie;
+import com.vaadin.data.Item;
+import com.vaadin.data.util.BeanItem;
+
+public class MovieQuery implements Query {
+
+  private EntityManager entityManager;
+  private QueryDefinition definition;
+  private String criteria = "";
+
+  public MovieQuery(EntityManager entityManager,
+                    QueryDefinition definition,
+                    Object[] sortPropertyIds,
+                    boolean[] sortStates) {
+    super();
+    this.entityManager = entityManager;
+    this.definition = definition;
+
+    for(int i=0;i<sortPropertyIds.length;i++) {
+      if(i==0) {
+        criteria = " ORDER BY";
+      } else {
+        criteria+ = ",";
+      }
+      criteria += " m." + sortPropertyIds[i];
+      if(sortStates[i]) {
+        criteria += " ASC";
+      }
+      else {
+        criteria += " DESC";
+      }
+    }
+  }
+
+  @Override
+  public Item constructItem() {
+    return new BeanItem<Movie>(new Movie());
+  }
+
+  @Override
+  public int size() {
+    javax.persistence.Query query = entityManager.
+        createQuery("SELECT count(m) from Movie as m");
+    return (int)((Long) query.getSingleResult()).longValue();
+  }
+
+  @Override
+  public List<Item> loadItems(int startIndex, int count) {
+    javax.persistence.Query query = entityManager.
+        createQuery("SELECT m from Movie as m" + criteria);
+    query.setFirstResult(startIndex);
+    query.setMaxResults(count);
+
+    List<Movie> movies=query.getResultList();
+    List<Item> items=new ArrayList<Item>();
+    for(Movie movie : movies) {
+      items.add(new BeanItem<Movie>(movie));
+    }
+
+    return items;
+  }
+
+  @Override
+  public void saveItems(List<Item> addedItems, List<Item> modifiedItems,
+        List<Item> removedItems) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean deleteAllItems() {
+    throw new UnsupportedOperationException();
+  }
+}
+....
+
+[[how-to-implement-editable-table]]
+How to Implement Editable Table?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+First you need to implement the `Query.saveItems()` method. After this you
+need to set some of the properties editable in your items and set table
+in editable mode as well. After user has made changes you need to call
+`container.commit()` or `container.discard()` to commit or rollback
+respectively. Please find complete examples of table handing and
+editable JPA query from add-on page.
+
+[[how-to-use-debug-properties]]
+How to Use Debug Properties?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+LQC provides set of debug properties which give information about
+response times, number of queries constructed and data batches loaded.
+To use these properties the items used need to contain these properties
+with correct ids and types. If you use dynamic items you can defined
+them in the query definition and add them on demand in the query
+implementation.
+
+[source,java]
+....
+container.addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_QUERY_INDEX, Integer.class, 0, true, false);
+container.addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_BATCH_INDEX, Integer.class, 0, true, false);
+container.addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_BATCH_QUERY_TIME, Integer.class, 0, true, false);
+....
+
+[[how-to-use-row-status-indicator-column-in-table]]
+How to Use Row Status Indicator Column in Table?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When creating editable tables LCQ provides
+`QueryItemStatusColumnGenerator` which can be used to generate the status
+column cells to the table. In addition you need to have the status
+property in your items. If your items respect the query definition you
+can implement this as follows:
+
+[source,java]
+....
+container.addContainerProperty(LazyQueryView.PROPERTY_ID_ITEM_STATUS,
+            QueryItemStatus.class, QueryItemStatus.None, true, false);
+....
+
+[[how-to-use-status-column-and-debug-columns-with-beans]]
+How to Use Status Column and Debug Columns with Beans
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is example query implementation which shows how JPA and beans can
+be used together with status and debug properties:
+
+[source,java]
+....
+package org.vaadin.addons.lazyquerycontainer.example;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+import org.vaadin.addons.lazyquerycontainer.CompositeItem;
+import org.vaadin.addons.lazyquerycontainer.Query;
+import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.BeanItem;
+import com.vaadin.data.util.ObjectProperty;
+
+public class TaskQuery implements Query {
+
+  private EntityManager entityManager;
+  private QueryDefinition definition;
+  private String criteria=" ORDER BY t.name ASC";
+
+  public TaskQuery(EntityManager entityManager, QueryDefinition definition,
+      Object[] sortPropertyIds, boolean[] sortStates) {
+    super();
+    this.entityManager = entityManager;
+    this.definition = definition;
+
+    for(int i=0; i<sortPropertyIds.length; i++) {
+      if(i==0) {
+        criteria = " ORDER BY";
+      } else {
+        criteria+ = ",";
+      }
+      criteria += " t." + sortPropertyIds[i];
+      if(sortStates[i]) {
+        criteria += " ASC";
+      }
+      else {
+        criteria += " DESC";
+      }
+    }
+  }
+
+  @Override
+  public Item constructItem() {
+    Task task=new Task();
+    try {
+      BeanInfo info = Introspector.getBeanInfo( Task.class );
+      for ( PropertyDescriptor pd : info.getPropertyDescriptors() ) {
+        for(Object propertyId : definition.getPropertyIds()) {
+          if(pd.getName().equals(propertyId)) {
+            pd.getWriteMethod().invoke(task,
+                definition.getPropertyDefaultValue(propertyId));
+          }
+        }
+      }
+    } catch(Exception e) {
+      throw new RuntimeException("Error in bean property population");
+    }
+    return toItem(task);
+  }
+
+  @Override
+  public int size() {
+    javax.persistence.Query query = entityManager.createQuery(
+        "SELECT count(t) from Task as t");
+    return (int)((Long) query.getSingleResult()).longValue();
+  }
+
+  @Override
+  public List<Item> loadItems(int startIndex, int count) {
+    javax.persistence.Query query = entityManager.createQuery(
+            "SELECT t from Task as t" + criteria);
+    query.setFirstResult(startIndex);
+    query.setMaxResults(count);
+
+    List<Task> tasks=query.getResultList();
+    List<Item> items=new ArrayList<Item>();
+    for(Task task : tasks) {
+      items.add(toItem(task));
+    }
+    return items;
+  }
+
+  @Override
+  public void saveItems(List<Item> addedItems, List<Item> modifiedItems,
+        List<Item> removedItems) {
+    entityManager.getTransaction().begin();
+    for(Item item : addedItems) {
+      entityManager.persist(fromItem(item));
+    }
+    for(Item item : modifiedItems) {
+      entityManager.persist(fromItem(item));
+    }
+    for(Item item : removedItems) {
+      entityManager.remove(fromItem(item));
+    }
+    entityManager.getTransaction().commit();
+  }
+
+  @Override
+  public boolean deleteAllItems() {
+    throw new UnsupportedOperationException();
+  }
+
+  private Item toItem(Task task) {
+    BeanItem<Task> beanItem= new BeanItem<Task>(task);
+
+    CompositeItem compositeItem=new CompositeItem();
+
+    compositeItem.addItem("task", beanItem);
+
+    for(Object propertyId : definition.getPropertyIds()) {
+      if(compositeItem.getItemProperty(propertyId)==null) {
+        compositeItem.addItemProperty(propertyId, new ObjectProperty(
+            definition.getPropertyDefaultValue(propertyId),
+            definition.getPropertyType(propertyId),
+            definition.isPropertyReadOnly(propertyId)));
+      }
+    }
+    return compositeItem;
+  }
+
+  private Task fromItem(Item item) {
+    return (Task)((BeanItem)(((CompositeItem)item).getItem("task"))).getBean();
+  }
+}
+....
diff --git a/documentation/articles/contents.asciidoc b/documentation/articles/contents.asciidoc
new file mode 100644 (file)
index 0000000..02bddf5
--- /dev/null
@@ -0,0 +1,5 @@
+= Community articles for Vaadin 7
+
+[discrete]
+== Articles
+- link:LazyQueryContainer.asciidoc[Lazy query container]