001: /*
002: * $Header: /export/home/cvsroot/MyPersonalizerRepository/MyPersonalizer/Subsystems/Kernel/Sources/es/udc/mypersonalizer/kernel/model/query/executor/sql/SQLQueryExecutorDelegate.java,v 1.1.1.1 2004/03/25 12:08:37 fbellas Exp $
003: * $Revision: 1.1.1.1 $
004: * $Date: 2004/03/25 12:08:37 $
005: *
006: * =============================================================================
007: *
008: * Copyright (c) 2003, The MyPersonalizer Development Group
009: * (http://www.tic.udc.es/~fbellas/mypersonalizer/index.html) at
010: * University Of A Coruņa
011: * All rights reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions are met:
015: *
016: * - Redistributions of source code must retain the above copyright notice,
017: * this list of conditions and the following disclaimer.
018: *
019: * - Redistributions in binary form must reproduce the above copyright notice,
020: * this list of conditions and the following disclaimer in the documentation
021: * and/or other materials provided with the distribution.
022: *
023: * - Neither the name of the University Of A Coruņa nor the names of its
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
028: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
029: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
030: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
031: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
032: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
033: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
034: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
035: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
036: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
037: * POSSIBILITY OF SUCH DAMAGE.
038: *
039: */
040:
041: package es.udc.mypersonalizer.kernel.model.query.executor.sql;
042:
043: import java.sql.Connection;
044: import java.sql.PreparedStatement;
045: import java.sql.ResultSet;
046: import java.util.ArrayList;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051:
052: import es.udc.mypersonalizer.kernel.model.annotators.sql.SQLPersistenceTypeAnnotationHelper;
053: import es.udc.mypersonalizer.kernel.model.metainfo.MetaCompoundProperty;
054: import es.udc.mypersonalizer.kernel.model.metainfo.MetaProperty;
055: import es.udc.mypersonalizer.kernel.model.metainfo.MetaSimpleProperty;
056: import es.udc.mypersonalizer.kernel.model.properties.Property;
057: import es.udc.mypersonalizer.kernel.model.properties.SimpleProperty;
058: import es.udc.mypersonalizer.kernel.model.query.ast.Query;
059: import es.udc.mypersonalizer.kernel.model.query.ast.Step;
060: import es.udc.mypersonalizer.kernel.model.query.ast.expr.PropertyListExpression;
061: import es.udc.mypersonalizer.kernel.model.query.executor.QueryExecutorDelegate;
062: import es.udc.mypersonalizer.kernel.model.repository.sql.config.DatabaseConventionsConfigManager;
063: import es.udc.mypersonalizer.kernel.model.repository.sql.plain.Transaction;
064: import es.udc.mypersonalizer.kernel.model.repository.sql.plain.TransactionManager;
065: import es.udc.mypersonalizer.kernel.model.repository.sql.storers.FindMetaPropertyVisitor;
066: import es.udc.mypersonalizer.kernel.util.exceptions.InternalErrorException;
067:
068: /**
069: * Query executor for services using the SQL persistence type.
070: * <p>
071: * This class defines de <i>document order</i> as the order defined by the
072: * property identifier (if the property is univalued) or the order defined
073: * by the parent's property identifier (for sorting properties) and the
074: * generated identifier (for sorting values).
075: *
076: * @author Abel Muinho
077: * @since 1.0
078: */
079: public class SQLQueryExecutorDelegate implements QueryExecutorDelegate {
080:
081: /** The name of the generated identifier column, read through the
082: * configuration manager. */
083: private static String GENERATED_IDENTIFIER_COLUMN_NAME = null;
084: /** The name of the property identifier column, read through the
085: * configuration manager. */
086: private static String PROPERTY_IDENTIFIER_COLUMN_NAME = null;
087: /** The name of the column storing the users login name, read through the
088: * configuration manager. */
089: private String LOGIN_COLUMN = null;
090:
091: /** The connection to the database. */
092: private Connection connection;
093:
094: /**
095: * Creates an instance of the query executor.
096: * @throws InternalErrorException if the configuration can't be read.
097: */
098: public SQLQueryExecutorDelegate() throws InternalErrorException {
099: if (GENERATED_IDENTIFIER_COLUMN_NAME == null) {
100: GENERATED_IDENTIFIER_COLUMN_NAME = DatabaseConventionsConfigManager
101: .getConfig().getGeneratedIdentifierColumn();
102: }
103: if (PROPERTY_IDENTIFIER_COLUMN_NAME == null) {
104: PROPERTY_IDENTIFIER_COLUMN_NAME = DatabaseConventionsConfigManager
105: .getConfig().getPropertyIdentifierColumn();
106: }
107: if (LOGIN_COLUMN == null) {
108: LOGIN_COLUMN = DatabaseConventionsConfigManager.getConfig()
109: .getLoginColumn();
110: }
111: }
112:
113: public PropertyListExpression evaluate(Query query)
114: throws InternalErrorException {
115:
116: return evaluate(query, 0, Integer.MAX_VALUE);
117:
118: }
119:
120: public PropertyListExpression evaluate(Query query, int start,
121: int count) throws InternalErrorException {
122:
123: Transaction transaction = null;
124:
125: if (start < 0 || count < 1) {
126: throw new IllegalArgumentException(
127: "Invalid Page-by-Page limits.");
128: }
129:
130: try {
131: /* Get the transaction and the associated connection. */
132: transaction = TransactionManager.getInstance()
133: .getTransaction(Transaction.TRANSACTION_REQUIRED);
134: connection = transaction.getConnection();
135:
136: SelectIdsMetaPropertyVisitor visitor = new SelectIdsMetaPropertyVisitor();
137: visitor.buildQuery(query);
138: PreparedStatement statement = connection.prepareStatement(
139: visitor.getSqlForPropertyIds(),
140: ResultSet.TYPE_SCROLL_INSENSITIVE,
141: ResultSet.CONCUR_READ_ONLY);
142: Iterator it = visitor.getParameters().iterator();
143: int param = 1;
144: while (it.hasNext()) {
145: statement.setObject(param++, it.next());
146: }
147:
148: PropertyListExpression result = new PropertyListExpression();
149: Step lastStep = (Step) query.getReturnClause().get(
150: query.getReturnClause().size() - 1);
151: MetaProperty target = lastStep.getMetaProperty();
152: ResultSet rs = statement.executeQuery();
153:
154: /* Our index is zero-based, but the result set is one-based.
155: * However, as we will be advancing one position before reading
156: * data (ResultSet.next()), the indexes match... but it is not
157: * legal to call ResultSet.absolute(0).
158: */
159: if (start > 0) {
160: rs.absolute(start);
161: }
162:
163: if (target instanceof MetaSimpleProperty) {
164: result.setProperties(getSimpleProperties(
165: (MetaSimpleProperty) target, rs, count));
166: } else {
167: result.setProperties(getCompoundProperties(
168: (MetaCompoundProperty) target, rs, count));
169: }
170:
171: /* Release transaction. */
172: transaction.release();
173:
174: return result;
175: } catch (InternalErrorException e) {
176: rollbackTransaction(transaction);
177: throw e;
178: } catch (Exception e) {
179: rollbackTransaction(transaction);
180: throw new InternalErrorException(e);
181: }
182: }
183:
184: /**
185: * Obtains the result properties, assuming they are compound.
186: * @param target the target metaproperty describing the result.
187: * @param rs the resultset with the property identifiers.
188: * @param count the maximum number of properties to return.
189: * @return the list of properties retrieved.
190: * @throws InternalErrorException if the properties can not be retrieved.
191: */
192: private List getCompoundProperties(MetaCompoundProperty target,
193: ResultSet rs, int count) throws InternalErrorException {
194:
195: List properties = new ArrayList();
196: try {
197: boolean hasNext = rs.next();
198: while (hasNext && count > 0) {
199: Map key = new HashMap(2);
200: /* Take care of the UserRegistrationInformation service. */
201: if (SQLQueryExecutorHelper
202: .isUserRegistrationInformationRootProperty(target)) {
203: key.put(LOGIN_COLUMN, rs.getString(LOGIN_COLUMN));
204: } else {
205: key
206: .put(
207: PROPERTY_IDENTIFIER_COLUMN_NAME,
208: new Long(
209: rs
210: .getLong(PROPERTY_IDENTIFIER_COLUMN_NAME)));
211: }
212: if (target.isMultiValued()) {
213: key
214: .put(
215: GENERATED_IDENTIFIER_COLUMN_NAME,
216: new Long(
217: rs
218: .getLong(GENERATED_IDENTIFIER_COLUMN_NAME)));
219: }
220: FindMetaPropertyVisitor visitor = new FindMetaPropertyVisitor(
221: connection, null, key);
222: Property property = (Property) target.accept(visitor);
223: properties.add(property);
224: hasNext = rs.next();
225: count--;
226: }
227: } catch (Exception e) {
228: throw new InternalErrorException(e);
229: }
230: return properties;
231: }
232:
233: /**
234: * Obtains the result properties, assuming they are simple.
235: * @param target the target metaproperty describing the result.
236: * @param rs the resultset with the property identifiers.
237: * @param count the maximum number of properties to return.
238: * @return the list of properties retrieved.
239: * @throws InternalErrorException if the properties can not be retrieved.
240: */
241: private List getSimpleProperties(MetaSimpleProperty target,
242: ResultSet rs, int count) throws InternalErrorException {
243: /* Return a list of values. */
244: String columnName = SQLPersistenceTypeAnnotationHelper
245: .getColumnNameAnnotation(target);
246: String simpleName = target.getSimpleName();
247: List result;
248: if (target.isMultiValued()) {
249: result = buildMultivaluedSimpleProperties(target,
250: columnName, simpleName, rs, count);
251: } else {
252: result = buildUnivaluedSimpleProperties(target, columnName,
253: simpleName, rs, count);
254: }
255: return result;
256: }
257:
258: /**
259: * Creates a list of univalued simple-properties from the data in a
260: * <code>ResultSet</code>.
261: * @param target the MetaSimpleProperty describing the properties to build.
262: * @param columnName the name of the column containing the values.
263: * @param simpleName the name of the univalued simple property.
264: * @param rs the resultset with the data.
265: * @param count the maximum number of properties to build.
266: * @return a list of simple properties.
267: * @throws InternalErrorException if errors are found while accessing the
268: * result set.
269: */
270: private List buildUnivaluedSimpleProperties(
271: MetaSimpleProperty target, String columnName,
272: String simpleName, ResultSet rs, int count)
273: throws InternalErrorException {
274: List properties = new ArrayList();
275: SimpleProperty property;
276: try {
277: while (rs.next() && count > 0) {
278: property = new SimpleProperty(simpleName,
279: new String[] { rs.getString(columnName) },
280: target.getJavaType());
281: properties.add(property);
282: count--;
283: }
284: } catch (Exception e) {
285: throw new InternalErrorException(e);
286: }
287: return properties;
288: }
289:
290: /**
291: * Creates a list of multivalued simple-properties from the data in a
292: * <code>ResultSet</code>, ordered by property identifier.
293: * @param target the MetaSimpleProperty describing the properties to build.
294: * @param columnName the name of the column containing the values.
295: * @param simpleName the name of the univalued simple property.
296: * @param rs the resultset with the data.
297: * @param count the maximum number of properties to build.
298: * @return a list of simple properties.
299: * @throws InternalErrorException if errors are found while accessing the
300: * result set.
301: */
302: private List buildMultivaluedSimpleProperties(
303: MetaSimpleProperty target, String columnName,
304: String simpleName, ResultSet rs, int count)
305: throws InternalErrorException {
306:
307: List properties = new ArrayList();
308: SimpleProperty property;
309: try {
310: boolean moreResults = rs.next();
311: long propId;
312: while (moreResults && count > 0) {
313: List values = new ArrayList();
314: do {
315: propId = rs
316: .getLong(PROPERTY_IDENTIFIER_COLUMN_NAME);
317: values.add(rs.getString(columnName));
318: moreResults = rs.next();
319: } while (moreResults
320: && rs.getLong(PROPERTY_IDENTIFIER_COLUMN_NAME) == propId);
321:
322: String[] valuesAsString = (String[]) values
323: .toArray(new String[] {});
324: property = new SimpleProperty(simpleName,
325: valuesAsString, target.getJavaType());
326: properties.add(property);
327: count--;
328: }
329: } catch (Exception e) {
330: throw new InternalErrorException(e);
331: }
332: return properties;
333: }
334:
335: /**
336: * Safely executes a rollback on the given transaction.
337: * @param transaction the transaction to be rolled back.
338: * @throws InternalErrorException if the rollback operation can't be
339: * completed because of errors.
340: */
341: private void rollbackTransaction(Transaction transaction)
342: throws InternalErrorException {
343:
344: if (transaction != null) {
345: transaction.rollback();
346: }
347: }
348: }
|