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.server.Utils;
36 import org.glom.web.shared.GlomField;
38 import com.mchange.v2.c3p0.ComboPooledDataSource;
41 * @author Ben Konrath <ben@bagu.org>
44 public abstract class ListDBAccess extends DBAccess {
45 protected LayoutFieldVector fieldsToGet;
47 protected ListDBAccess(Document document, String documentID, ComboPooledDataSource cpds, String tableName) {
48 super(document, documentID, cpds, tableName);
51 public abstract int getExpectedResultSize();
53 protected abstract String getSelectQuery(SortClause sortClause);
55 protected abstract String getCountQuery();
57 protected ArrayList<GlomField[]> getListData(int start, int length, boolean useSortClause, int sortColumnIndex,
58 boolean isAscending) {
60 // Add a LayoutItem_Field for the primary key to the end of the LayoutFieldVector if it doesn't already contain
62 if (getPrimaryKeyIndex() < 0) {
63 fieldsToGet.add(getPrimaryKeyLayoutItemField());
66 // create a sort clause for the column we've been asked to sort
67 SortClause sortClause = new SortClause();
69 org.glom.libglom.LayoutItem item = fieldsToGet.get(sortColumnIndex);
70 LayoutItem_Field layoutItemField = LayoutItem_Field.cast_dynamic(item);
71 if (layoutItemField != null)
72 sortClause.addLast(new SortFieldPair(layoutItemField, isAscending));
74 Log.error(documentID, tableName, "Error getting LayoutItem_Field for column index " + sortColumnIndex
75 + ". Cannot create a sort clause for this column.");
78 // create a sort clause for the primary key if we're not asked to sort a specific column
79 int numItems = Utils.safeLongToInt(fieldsToGet.size());
80 for (int i = 0; i < numItems; i++) {
81 LayoutItem_Field layoutItem = fieldsToGet.get(i);
82 Field details = layoutItem.get_full_field_details();
83 if (details != null && details.get_primary_key()) {
84 sortClause.addLast(new SortFieldPair(layoutItem, true)); // ascending
90 ArrayList<GlomField[]> rowsList = new ArrayList<GlomField[]>();
91 Connection conn = null;
95 // Setup the JDBC driver and get the query. Special care needs to be take to ensure that the results will be
96 // based on a cursor so that large amounts of memory are not consumed when the query retrieve a large amount
97 // of data. Here's the relevant PostgreSQL documentation:
98 // http://jdbc.postgresql.org/documentation/83/query.html#query-with-cursor
99 conn = cpds.getConnection();
100 conn.setAutoCommit(false);
101 st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
102 st.setFetchSize(length);
103 String query = getSelectQuery(sortClause) + " OFFSET " + start;
104 // TODO Test memory usage before and after we execute the query that would result in a large ResultSet.
105 // We need to ensure that the JDBC driver is in fact returning a cursor based result set that has a low
106 // memory footprint. Check the difference between this value before and after the query:
107 // Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
108 // Test the execution time at the same time (see the todo item in getLayoutListTable()).
109 rs = st.executeQuery(query);
111 // get the results from the ResultSet
112 rowsList = convertResultSetToDTO(length, fieldsToGet, rs);
113 } catch (SQLException e) {
114 Log.error(documentID, tableName, "Error executing database query.", e);
115 // TODO: somehow notify user of problem
117 // cleanup everything that has been used
125 } catch (Exception e) {
126 Log.error(documentID, tableName,
127 "Error closing database resources. Subsequent database queries may not work.", e);
134 * Get the number of rows a query with the table name and layout fields would return. This is needed for the /* list
137 protected int getResultSizeOfSQLQuery() {
139 // Add a LayoutItem_Field for the primary key to the end of the LayoutFieldVector if it doesn't already contain
141 if (getPrimaryKeyIndex() < 0) {
142 fieldsToGet.add(getPrimaryKeyLayoutItemField());
145 Connection conn = null;
149 // Setup and execute the count query. Special care needs to be take to ensure that the results will be based
150 // on a cursor so that large amounts of memory are not consumed when the query retrieve a large amount of
151 // data. Here's the relevant PostgreSQL documentation:
152 // http://jdbc.postgresql.org/documentation/83/query.html#query-with-cursor
153 conn = cpds.getConnection();
154 conn.setAutoCommit(false);
155 st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
156 String query = getCountQuery();
157 // TODO Test execution time of this query with when the number of rows in the table is large (say >
158 // 1,000,000). Test memory usage at the same time (see the todo item in getTableData()).
159 rs = st.executeQuery(query);
161 // get the number of rows in the query
165 } catch (SQLException e) {
166 Log.error(documentID, tableName, "Error calculating number of rows in the query.", e);
169 // cleanup everything that has been used
177 } catch (Exception e) {
178 Log.error(documentID, tableName,
179 "Error closing database resources. Subsequent database queries may not work.", e);
185 * Gets the primary key index of this list layout.
187 * @return index of primary key or -1 if a primary key was not found
189 public int getPrimaryKeyIndex() {
190 for (int i = 0; i < fieldsToGet.size(); i++) {
191 LayoutItem_Field layoutItemField = fieldsToGet.get(i);
192 Field field = layoutItemField.get_full_field_details();
193 if (field != null && field.get_primary_key())