You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UsingJDBCwithLazyQueryContainerAndFilteringTable.asciidoc 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. ---
  2. title: Using JDBC with Lazy Query Container and FilteringTable
  3. order: 3
  4. layout: page
  5. ---
  6. [[using-jdbc-with-lazy-query-container-and-filteringtable]]
  7. = Using JDBC with Lazy Query Container and FilteringTable
  8. Introduction
  9. Populating display tables from a database is a deceptively complicated
  10. operation, especially when mixing multiple techniques together. This
  11. page provides an example of one way to efficiently load data from a SQL
  12. database table into a filterable UI, using the _Lazy Query Container_ and
  13. _FilteringTable_ add-ons.
  14. Note: Do not use the SQLContainer package. This is buggy and will have
  15. your database and garbage collector crunching in loops.
  16. `Query` and `QueryFactory` implementation
  17. The place to start is the Lazy Query Container's (LQC) Query interface.
  18. This is where the interface with your database happens. This example
  19. access a database table with computer statistics. It's read-only. How to
  20. log and access your JDBC connection differs in each environment; they
  21. are treated generically here. Only select imports are included.
  22. [source,java]
  23. ....
  24. import org.vaadin.addons.lazyquerycontainer.Query;
  25. import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
  26. import org.vaadin.addons.lazyquerycontainer.QueryFactory;
  27. import com.vaadin.data.Container.Filter;
  28. import com.vaadin.data.Item;
  29. import com.vaadin.data.util.ObjectProperty;
  30. import com.vaadin.data.util.PropertysetItem;
  31. import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
  32. import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
  33. /**
  34. * Query for using the database's device-status table as a data source
  35. * for a Vaadin container (table).
  36. */
  37. public class DeviceStatusQuery implements Query {
  38. private static final Logger log = LoggerFactory.getLogger(DeviceStatusQuery.class);
  39. /**
  40. * The table column names. Use these instead of typo-prone magic strings.
  41. */
  42. public static enum Column {
  43. hostname, loc_id, update_when, net_ip, lan_ip, lan_mac, hardware,
  44. opsys, image, sw_ver, cpu_load, proc_count, mem_usage, disk_usage;
  45. public boolean is(Object other) {
  46. if (other instanceof String)
  47. return this.toString().equals(other);
  48. else
  49. return (this == other);
  50. }
  51. };
  52. public static class Factory implements QueryFactory {
  53. private int locId;
  54. /**
  55. * Constructor
  56. * @param locId - location ID
  57. */
  58. public Factory(int locId) {
  59. this.locId = locId;
  60. }
  61. @Override
  62. public Query constructQuery(QueryDefinition def) {
  63. return new DeviceStatusQuery(def, locId);
  64. }
  65. }//class Factory
  66. /////// INSTANCE ///////
  67. private String countQuery;
  68. private String fetchQuery;
  69. /** Borrow from SQLContainer to build filter queries */
  70. private StatementHelper stmtHelper = new StatementHelper();
  71. /**
  72. * Constructor
  73. * @param locId - location ID
  74. * @param userId - ID of user viewing the data
  75. */
  76. private DeviceStatusQuery(QueryDefinition def, int locId) {
  77. Build filters block List<Filter> filters = def.getFilters();
  78. String filterStr = null;
  79. if (filters != null && !filters.isEmpty())
  80. filterStr = QueryBuilder.getJoinedFilterString(filters, "AND", stmtHelper);
  81. // Count query
  82. StringBuilder query = new StringBuilder( "SELECT COUNT(*) FROM device_status");
  83. query.append(" WHERE loc_id=").append(locId);
  84. if (filterStr != null)
  85. query.append(" AND ").append(filterStr);
  86. this.countQuery = query.toString();
  87. // Fetch query
  88. query = new StringBuilder(
  89. "SELECT hostname, loc_id, update_when, net_ip, lan_ip, " +
  90. "lan_mac, hardware, opsys, image, sw_ver, cpu_load, " +
  91. "proc_count, mem_usage, disk_usage FROM device_status");
  92. query.append(" WHERE loc_id=").append(locId);
  93. if (filterStr != null)
  94. query.append(" AND ").append(filterStr);
  95. // Build Order by
  96. Object[] sortIds = def.getSortPropertyIds();
  97. if (sortIds != null && sortIds.length > 0) {
  98. query.append(" ORDER BY ");
  99. boolean[] sortAsc = def.getSortPropertyAscendingStates();
  100. assert sortIds.length == sortAsc.length;
  101. for (int si = 0; si < sortIds.length; ++si) {
  102. if (si > 0) query.append(',');
  103. query.append(sortIds[si]);
  104. if (sortAsc[si]) query.append(" ASC");
  105. else query.append(" DESC");
  106. }
  107. }
  108. else query.append(" ORDER BY hostname");
  109. this.fetchQuery = query.toString();
  110. log.trace("DeviceStatusQuery count: {}", this.countQuery);
  111. log.trace("DeviceStatusQuery fetch: {}", this.fetchQuery);
  112. }//constructor
  113. @Override
  114. public int size() {
  115. int result = 0;
  116. try (Connection conn = Database.getConnection()) {
  117. PreparedStatement stmt = conn.prepareStatement(this.countQuery);
  118. stmtHelper.setParameterValuesToStatement(stmt);
  119. ResultSet rs = stmt.executeQuery();
  120. if (rs.next()) result = rs.getInt(1);
  121. stmt.close();
  122. }
  123. catch (SQLException ex) {
  124. log.error("DB access failure", ex);
  125. }
  126. log.trace("DeviceStatusQuery size=\{}", result);
  127. return result;
  128. }
  129. @Override
  130. public List<Item> loadItems(int startIndex, int count) {
  131. List<Item> items = new ArrayList<Item>();
  132. try (Connection conn = Database.getConnection()) {
  133. String q = this.fetchQuery + " LIMIT " + count + " OFFSET " + startIndex;
  134. PreparedStatement stmt = conn.prepareStatement(q);
  135. stmtHelper.setParameterValuesToStatement(stmt);
  136. ResultSet rs = stmt.executeQuery();
  137. while (rs.next()) {
  138. PropertysetItem item = new PropertysetItem();
  139. // Include the data type parameter on ObjectProperty any time the value could be null
  140. item.addItemProperty(Column.hostname,
  141. new ObjectProperty<String>(rs.getString(1), String.class));
  142. item.addItemProperty(Column.loc_id,
  143. new ObjectProperty<Integer>(rs.getInt(2), Integer.class));
  144. item.addItemProperty(Column.update_when,
  145. new ObjectProperty<Timestamp>(rs.getTimestamp(3), Timestamp.class));
  146. item.addItemProperty(Column.net_ip,
  147. new ObjectProperty<String>(rs.getString(4)));
  148. item.addItemProperty(Column.lan_ip,
  149. new ObjectProperty<String>(rs.getString(5)));
  150. item.addItemProperty(Column.lan_mac,
  151. new ObjectProperty<String>(rs.getString(6)));
  152. item.addItemProperty(Column.hardware,
  153. new ObjectProperty<String>(rs.getString(7)));
  154. item.addItemProperty(Column.opsys,
  155. new ObjectProperty<String>(rs.getString(8)));
  156. item.addItemProperty(Column.image,
  157. new ObjectProperty<String>(rs.getString(9)));
  158. item.addItemProperty(Column.sw_ver,
  159. new ObjectProperty<String>(rs.getString(10)));
  160. item.addItemProperty(Column.cpu_load,
  161. new ObjectProperty<String>(rs.getString(11)));
  162. item.addItemProperty(Column.proc_count,
  163. new ObjectProperty<Integer>(rs.getInt(12)));
  164. item.addItemProperty(Column.mem_usage,
  165. new ObjectProperty<Integer>(rs.getInt(13)));
  166. item.addItemProperty(Column.disk_usage,
  167. new ObjectProperty<Integer>(rs.getInt(14)));
  168. items.add(item);
  169. }
  170. rs.close();
  171. stmt.close();
  172. }
  173. catch (SQLException ex) {
  174. log.error("DB access failure", ex);
  175. }
  176. log.trace("DeviceStatusQuery load {} items from {}={} found", count,
  177. startIndex, items.size());
  178. return items;
  179. } //loadItems()
  180. /**
  181. * Only gets here if loadItems() fails, so return an empty state.
  182. * Throwing from here causes an infinite loop.
  183. */
  184. @Override
  185. public Item constructItem() {
  186. PropertysetItem item = new PropertysetItem();
  187. item.addItemProperty(Column.hostname, new ObjectProperty<String>(""));
  188. item.addItemProperty(Column.loc_id, new ObjectProperty<Integer>(-1));
  189. item.addItemProperty(Column.update_when,
  190. new ObjectProperty<Timestamp>(new Timestamp(System.currentTimeMillis())));
  191. item.addItemProperty(Column.net_ip, new ObjectProperty<String>(""));
  192. item.addItemProperty(Column.lan_ip, new ObjectProperty<String>(""));
  193. item.addItemProperty(Column.lan_mac, new ObjectProperty<String>(""));
  194. item.addItemProperty(Column.hardware, new ObjectProperty<String>(""));
  195. item.addItemProperty(Column.opsys, new ObjectProperty<String>(""));
  196. item.addItemProperty(Column.image, new ObjectProperty<String>(""));
  197. item.addItemProperty(Column.sw_ver, new ObjectProperty<String>(""));
  198. item.addItemProperty(Column.cpu_load, new ObjectProperty<String>(""));
  199. item.addItemProperty(Column.proc_count, new ObjectProperty<Integer>(0));
  200. item.addItemProperty(Column.mem_usage, new ObjectProperty<Integer>(0));
  201. item.addItemProperty(Column.disk_usage, new ObjectProperty<Integer>(0));
  202. log.warn("Shouldn't be calling DeviceStatusQuery.constructItem()");
  203. return item;
  204. }
  205. @Override
  206. public boolean deleteAllItems() {
  207. throw new UnsupportedOperationException();
  208. }
  209. @Override
  210. public void saveItems(List<Item> arg0, List<Item> arg1, List<Item> arg2) {
  211. throw new UnsupportedOperationException();
  212. }
  213. }
  214. ....
  215. Using the Query with FilteringTable
  216. Now that we have our Query, we need to create a table to hold it. Here's
  217. one of many ways to do it with FilteringTable.
  218. [source,java]
  219. ....
  220. import org.tepi.filtertable.FilterDecorator;
  221. import org.tepi.filtertable.numberfilter.NumberFilterPopupConfig;
  222. import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
  223. import com.vaadin.data.Property;
  224. import com.vaadin.server.Resource;
  225. import com.vaadin.shared.ui.datefield.Resolution;
  226. import com.vaadin.ui.DateField;
  227. import com.vaadin.ui.AbstractTextField.TextChangeEventMode;
  228. /**
  229. * Filterable table of device statuses.
  230. */
  231. public class DeviceStatusTable extends FilterTable {
  232. private final
  233. String[] columnHeaders = {"Device", "Site", "Last Report", "Report IP",
  234. "LAN IP", "MAC Adrs", "Hardware", "O/S", "Image", "Software", "CPU"
  235. "Load", "Processes", "Memory Use", "Disk Use"};
  236. /**
  237. * Configuration this table for displaying of DeviceStatusQuery data.
  238. */
  239. public void configure(LazyQueryContainer dataSource) {
  240. super.setFilterGenerator(new LQCFilterGenerator(dataSource));
  241. super.setFilterBarVisible(true);
  242. super.setSelectable(true);
  243. super.setImmediate(true);
  244. super.setColumnReorderingAllowed(true);
  245. super.setColumnCollapsingAllowed(true);
  246. super.setSortEnabled(true);
  247. dataSource.addContainerProperty(Column.hostname, String.class, null, true, true);
  248. dataSource.addContainerProperty(Column.loc_id, Integer.class, null, true, false);
  249. dataSource.addContainerProperty(Column.update_when, Timestamp.class, null, true, true);
  250. dataSource.addContainerProperty(Column.net_ip, String.class, null, true, true);
  251. dataSource.addContainerProperty(Column.lan_ip, String.class, null, true, true);
  252. dataSource.addContainerProperty(Column.lan_mac, String.class, null, true, true);
  253. dataSource.addContainerProperty(Column.hardware, String.class, null, true, true);
  254. dataSource.addContainerProperty(Column.opsys, String.class, null, true, true);
  255. dataSource.addContainerProperty(Column.image, String.class, null, true, true);
  256. dataSource.addContainerProperty(Column.sw_ver, String.class, null, true, true);
  257. dataSource.addContainerProperty(Column.cpu_load, String.class, null, true, true);
  258. dataSource.addContainerProperty(Column.proc_count, Integer.class, null, true, true);
  259. dataSource.addContainerProperty(Column.mem_usage, Integer.class, null, true, true);
  260. dataSource.addContainerProperty(Column.disk_usage, Integer.class, null, true, true);
  261. super.setContainerDataSource(dataSource);
  262. super.setColumnHeaders(columnHeaders);
  263. super.setColumnCollapsed(Column.lan_mac, true);
  264. super.setColumnCollapsed(Column.opsys, true);
  265. super.setColumnCollapsed(Column.image, true);
  266. super.setFilterFieldVisible(Column.loc_id, false);
  267. }
  268. @Override
  269. protected String formatPropertyValue(Object rowId, Object colId, Property<?> property) {
  270. if (Column.loc_id.is(colId)) {
  271. // Example of how to translate a column value
  272. return Hierarchy.getLocation(((Integer) property.getValue())).getShortName();
  273. } else if (Column.update_when.is(colId)) {
  274. // Example of how to format a value.
  275. return ((java.sql.Timestamp) property.getValue()).toString().substring(0, 19);
  276. }
  277. return super.formatPropertyValue(rowId, colId, property);
  278. }
  279. /**
  280. * Filter generator that triggers a refresh of a LazyQueryContainer
  281. * whenever the filters change.
  282. */
  283. public class LQCFilterGenerator implements FilterGenerator {
  284. private final LazyQueryContainer lqc;
  285. public LQCFilterGenerator(LazyQueryContainer lqc) {
  286. this.lqc = lqc;
  287. }
  288. @Override
  289. public Filter generateFilter(Object propertyId, Object value) {
  290. return null;
  291. }
  292. @Override
  293. public Filter generateFilter(Object propertyId, Field<?> originatingField) {
  294. return null;
  295. }
  296. @Override
  297. public AbstractField<?> getCustomFilterComponent(Object propertyId) {
  298. return null;
  299. }
  300. @Override
  301. public void filterRemoved(Object propertyId) {
  302. this.lqc.refresh();
  303. }
  304. @Override
  305. public void filterAdded(Object propertyId, Class<? extends Filter> filterType, Object value) {
  306. this.lqc.refresh();
  307. }
  308. @Override
  309. public Filter filterGeneratorFailed(Exception reason, Object propertyId, Object value) {
  310. return null;
  311. }
  312. }
  313. }
  314. ....
  315. Put them together on the UI
  316. Now we have our Container that reads from the database, and a Table for
  317. displaying them, lets put the final pieces together somewhere in some UI
  318. code:
  319. [source,java]
  320. ....
  321. final DeviceStatusTable table = new DeviceStatusTable();
  322. table.setSizeFull();
  323. DeviceStatusQuery.Factory factory = new DeviceStatusQuery.Factory(locationID);
  324. final LazyQueryContainer statusDataContainer = new LazyQueryContainer(factory,
  325. /*index*/ null, /*batchSize*/ 50, false);
  326. statusDataContainer.getQueryView().setMaxCacheSize(300);
  327. table.configure(statusDataContainer);
  328. layout.addComponent(table);
  329. layout.setHeight(100f, Unit.PERCENTAGE); // no scrollbar
  330. // Respond to row click
  331. table.addValueChangeListener(new Property.ValueChangeListener() {
  332. @Override
  333. public void valueChange(ValueChangeEvent event) {
  334. Object index = event.getProperty().getValue();
  335. if (index != nulll) {
  336. int locId = (Integer) statusDataContainer.getItem(index)
  337. .getItemProperty(DeviceStatusQuery.Column.loc_id).getValue();
  338. doSomething(locId);
  339. table.setValue(null); //visually deselect
  340. }
  341. }
  342. });
  343. ....
  344. And finally, since we're using `SQLContainer`{empty}'s `QueryBuilder`, depending on
  345. your database you may need to include something like this once during
  346. your application startup:
  347. [source,java]
  348. ....
  349. import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
  350. import com.vaadin.data.util.sqlcontainer.query.generator.filter.StringDecorator;
  351. // Configure Vaadin SQLContainer to work with MySQL
  352. QueryBuilder.setStringDecorator(new StringDecorator("`","`"));
  353. ....