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.DriverManager;
24 import java.sql.ResultSet;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.util.ArrayList;
28 import java.util.List;
30 import org.glom.web.server.Log;
31 import org.glom.web.server.Utils;
32 import org.glom.web.server.libglom.Document;
33 import org.glom.web.shared.DataItem;
34 import org.glom.web.shared.libglom.Field;
35 import org.glom.web.shared.libglom.layout.LayoutItem;
36 import org.glom.web.shared.libglom.layout.LayoutItemField;
37 import org.glom.web.shared.libglom.layout.SortClause;
38 import org.glom.web.shared.libglom.layout.UsesRelationship;
40 import com.mchange.v2.c3p0.ComboPooledDataSource;
45 public abstract class ListDBAccess extends DBAccess {
46 protected List<LayoutItemField> fieldsToGet;
48 protected ListDBAccess(final Document document, final String documentID, final ComboPooledDataSource cpds,
49 final String tableName) {
50 super(document, documentID, cpds, tableName);
53 protected abstract String getSelectQuery(String quickFind, SortClause sortClause);
55 protected abstract String getCountQuery();
62 * @param sortColumnIndex
63 * The index of the column to sort by, or -1 for none.
67 protected ArrayList<DataItem[]> getListData(final String quickFind, final int start, final int length,
68 final int sortColumnIndex, final boolean isAscending) {
70 // create a sort clause for the column we've been asked to sort
71 final SortClause sortClause = new SortClause();
72 if (sortColumnIndex != -1) {
73 final LayoutItem item = fieldsToGet.get(sortColumnIndex);
74 if (item instanceof LayoutItemField) {
75 final UsesRelationship layoutItemField = (UsesRelationship) item;
76 sortClause.add(new SortClause.SortField(layoutItemField, isAscending));
78 Log.error(documentID, tableName, "Error getting LayoutItemField for column index " + sortColumnIndex
79 + ". Cannot create a sort clause for this column.");
82 // create a sort clause for the primary key if we're not asked to sort a specific column
84 if (fieldsToGet != null) {
85 numItems = Utils.safeLongToInt(fieldsToGet.size());
88 for (int i = 0; i < numItems; i++) {
89 final LayoutItemField layoutItem = fieldsToGet.get(i);
90 final Field details = layoutItem.getFullFieldDetails();
91 if (details != null && details.getPrimaryKey()) {
92 sortClause.add(new SortClause.SortField(layoutItem, true)); // ascending
98 ArrayList<DataItem[]> rowsList = new ArrayList<DataItem[]>();
99 Connection conn = null;
104 //Change the timeout, because it otherwise takes ages to fail sometimes when the details are not setup.
105 //This is more than enough.
106 DriverManager.setLoginTimeout(5);
108 // Setup the JDBC driver and get the query. Special care needs to be take to ensure that the results will be
109 // based on a cursor so that large amounts of memory are not consumed when the query retrieve a large amount
110 // of data. Here's the relevant PostgreSQL documentation:
111 // http://jdbc.postgresql.org/documentation/83/query.html#query-with-cursor
112 conn = cpds.getConnection();
114 Log.error(documentID, tableName, "The connection is null.");
118 conn.setAutoCommit(false);
119 st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
120 st.setFetchSize(length);
121 final String query = getSelectQuery(quickFind, sortClause) + " OFFSET " + start;
122 // TODO Test memory usage before and after we execute the query that would result in a large ResultSet.
123 // We need to ensure that the JDBC driver is in fact returning a cursor based result set that has a low
124 // memory footprint. Check the difference between this value before and after the query:
125 // Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
126 // Test the execution time at the same time (see the todo item in getLayoutListTable()).
127 rs = st.executeQuery(query);
129 // get the results from the ResultSet
130 rowsList = convertResultSetToDTO(length, fieldsToGet, rs);
131 } catch (final SQLException e) {
132 Log.error(documentID, tableName, "Error executing database query.", e);
133 // TODO: somehow notify user of problem
135 // cleanup everything that has been used
146 } catch (final Exception e) {
147 Log.error(documentID, tableName,
148 "Error closing database resources. Subsequent database queries may not work.", e);
155 * Get the number of rows a query with the table name and layout fields would return. This is needed for the /* list
158 protected int getResultSizeOfSQLQuery() {
160 Connection conn = null;
164 //Change the timeout, because it otherwise takes ages to fail sometimes when the details are not setup.
165 //This is more than enough.
166 DriverManager.setLoginTimeout(5);
168 // Setup and execute the count query. Special care needs to be take to ensure that the results will be based
169 // on a cursor so that large amounts of memory are not consumed when the query retrieve a large amount of
170 // data. Here's the relevant PostgreSQL documentation:
171 // http://jdbc.postgresql.org/documentation/83/query.html#query-with-cursor
172 conn = cpds.getConnection();
173 conn.setAutoCommit(false);
174 st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
175 final String query = getCountQuery();
177 // TODO Test execution time of this query with when the number of rows in the table is large (say >
178 // 1,000,000). Test memory usage at the same time (see the todo item in getTableData()).
179 rs = st.executeQuery(query);
181 // get the number of rows in the query
185 } catch (final SQLException e) {
186 Log.error(documentID, tableName, "Error calculating number of rows in the query.", e);
189 // cleanup everything that has been used
200 } catch (final Exception e) {
201 Log.error(documentID, tableName,
202 "Error closing database resources. Subsequent database queries may not work.", e);