2 * Copyright (C) 2011 Openismus GmbH
4 * This file is part of GWT-Glom.
6 * GWT-Glom is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
11 * GWT-Glom is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with GWT-Glom. If not, see <http://www.gnu.org/licenses/>.
20 package org.glom.web.server.database;
22 import java.sql.Connection;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.sql.Statement;
26 import java.util.ArrayList;
28 import org.glom.libglom.Document;
29 import org.glom.libglom.Field;
30 import org.glom.libglom.LayoutFieldVector;
31 import org.glom.libglom.LayoutItem_Field;
32 import org.glom.libglom.SortClause;
33 import org.glom.libglom.SortFieldPair;
34 import org.glom.web.server.Log;
35 import org.glom.web.shared.GlomField;
37 import com.mchange.v2.c3p0.ComboPooledDataSource;
40 * @author Ben Konrath <ben@bagu.org>
43 public abstract class ListDBAccess extends DBAccess {
44 protected LayoutFieldVector fieldsToGet;
46 protected ListDBAccess(Document document, String documentID, ComboPooledDataSource cpds, String tableName) {
47 super(document, documentID, cpds, tableName);
50 public abstract int getExpectedResultSize();
52 protected abstract String getSelectQuery(SortClause sortClause);
54 protected abstract String getCountQuery();
56 protected ArrayList<GlomField[]> getListData(int start, int length, boolean useSortClause, int sortColumnIndex,
57 boolean isAscending) {
59 // create a sort clause for the column we've been asked to sort
60 SortClause sortClause = new SortClause();
62 org.glom.libglom.LayoutItem item = fieldsToGet.get(sortColumnIndex);
63 LayoutItem_Field layoutItemField = LayoutItem_Field.cast_dynamic(item);
64 if (layoutItemField != null)
65 sortClause.addLast(new SortFieldPair(layoutItemField, isAscending));
67 Log.error(documentID, tableName, "Error getting LayoutItem_Field for column index " + sortColumnIndex
68 + ". Cannot create a sort clause for this column.");
71 // create a sort clause for the primary key and if we're not asked to sort a specific column
72 LayoutItem_Field primaryKeyLayout = getPrimaryKeyLayoutItemField();
73 Field details = primaryKeyLayout.get_full_field_details();
74 // double check that it's actually a primary_key
75 if (details != null && details.get_primary_key()) {
76 sortClause.addLast(new SortFieldPair(primaryKeyLayout, true)); // ascending
80 // Add a LayoutItem_Field for the primary key to the end of the LayoutFieldVector if it doesn't already contain
82 if (getPrimaryKeyIndex() < 0) {
83 fieldsToGet.add(getPrimaryKeyLayoutItemField());
86 ArrayList<GlomField[]> rowsList = new ArrayList<GlomField[]>();
87 Connection conn = null;
91 // Setup the JDBC driver and get the query. Special care needs to be take to ensure that the results will be
92 // based on a cursor so that large amounts of memory are not consumed when the query retrieve a large amount
93 // of data. Here's the relevant PostgreSQL documentation:
94 // http://jdbc.postgresql.org/documentation/83/query.html#query-with-cursor
95 conn = cpds.getConnection();
96 conn.setAutoCommit(false);
97 st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
98 st.setFetchSize(length);
99 String query = getSelectQuery(sortClause) + " OFFSET " + start;
100 // TODO Test memory usage before and after we execute the query that would result in a large ResultSet.
101 // We need to ensure that the JDBC driver is in fact returning a cursor based result set that has a low
102 // memory footprint. Check the difference between this value before and after the query:
103 // Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
104 // Test the execution time at the same time (see the todo item in getLayoutListTable()).
105 rs = st.executeQuery(query);
107 // get the results from the ResultSet
108 rowsList = convertResultSetToDTO(length, fieldsToGet, rs);
109 } catch (SQLException e) {
110 Log.error(documentID, tableName, "Error executing database query.", e);
111 // TODO: somehow notify user of problem
113 // cleanup everything that has been used
121 } catch (Exception e) {
122 Log.error(documentID, tableName,
123 "Error closing database resources. Subsequent database queries may not work.", e);
130 * Get the number of rows a query with the table name and layout fields would return. This is needed for the /* list
133 protected int getResultSizeOfSQLQuery() {
135 // Add a LayoutItem_Field for the primary key to the end of the LayoutFieldVector if it doesn't already contain
137 if (getPrimaryKeyIndex() < 0) {
138 fieldsToGet.add(getPrimaryKeyLayoutItemField());
141 Connection conn = null;
145 // Setup and execute the count query. Special care needs to be take to ensure that the results will be based
146 // on a cursor so that large amounts of memory are not consumed when the query retrieve a large amount of
147 // data. Here's the relevant PostgreSQL documentation:
148 // http://jdbc.postgresql.org/documentation/83/query.html#query-with-cursor
149 conn = cpds.getConnection();
150 conn.setAutoCommit(false);
151 st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
152 String query = getCountQuery();
153 // TODO Test execution time of this query with when the number of rows in the table is large (say >
154 // 1,000,000). Test memory usage at the same time (see the todo item in getTableData()).
155 rs = st.executeQuery(query);
157 // get the number of rows in the query
161 } catch (SQLException e) {
162 Log.error(documentID, tableName, "Error calculating number of rows in the query.", e);
165 // cleanup everything that has been used
173 } catch (Exception e) {
174 Log.error(documentID, tableName,
175 "Error closing database resources. Subsequent database queries may not work.", e);
181 * Gets the primary key index of this list layout.
183 * @return index of primary key or -1 if a primary key was not found
185 public int getPrimaryKeyIndex() {
186 for (int i = 0; i < fieldsToGet.size(); i++) {
187 LayoutItem_Field layoutItemField = fieldsToGet.get(i);
188 Field field = layoutItemField.get_full_field_details();
189 if (field != null && field.get_primary_key())