%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.torque.util.BasePeer |
|
|
1 | package org.apache.torque.util; |
|
2 | ||
3 | /* |
|
4 | * Copyright 2001-2004 The Apache Software Foundation. |
|
5 | * |
|
6 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
7 | * you may not use this file except in compliance with the License. |
|
8 | * You may obtain a copy of the License at |
|
9 | * |
|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 | * |
|
12 | * Unless required by applicable law or agreed to in writing, software |
|
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 | * See the License for the specific language governing permissions and |
|
16 | * limitations under the License. |
|
17 | */ |
|
18 | ||
19 | import java.io.BufferedOutputStream; |
|
20 | import java.io.ByteArrayOutputStream; |
|
21 | import java.io.IOException; |
|
22 | import java.io.ObjectOutputStream; |
|
23 | import java.io.Serializable; |
|
24 | import java.math.BigDecimal; |
|
25 | import java.sql.Connection; |
|
26 | import java.sql.PreparedStatement; |
|
27 | import java.sql.SQLException; |
|
28 | import java.sql.Statement; |
|
29 | import java.util.ArrayList; |
|
30 | import java.util.Collections; |
|
31 | import java.util.HashSet; |
|
32 | import java.util.Hashtable; |
|
33 | import java.util.Iterator; |
|
34 | import java.util.List; |
|
35 | ||
36 | import org.apache.commons.lang.StringUtils; |
|
37 | ||
38 | import org.apache.commons.logging.Log; |
|
39 | import org.apache.commons.logging.LogFactory; |
|
40 | ||
41 | import org.apache.torque.Torque; |
|
42 | import org.apache.torque.TorqueException; |
|
43 | import org.apache.torque.adapter.DB; |
|
44 | import org.apache.torque.map.ColumnMap; |
|
45 | import org.apache.torque.map.DatabaseMap; |
|
46 | import org.apache.torque.map.MapBuilder; |
|
47 | import org.apache.torque.map.TableMap; |
|
48 | import org.apache.torque.oid.IdGenerator; |
|
49 | import org.apache.torque.om.NumberKey; |
|
50 | import org.apache.torque.om.ObjectKey; |
|
51 | import org.apache.torque.om.SimpleKey; |
|
52 | import org.apache.torque.om.StringKey; |
|
53 | ||
54 | import com.workingdogs.village.Column; |
|
55 | import com.workingdogs.village.DataSet; |
|
56 | import com.workingdogs.village.KeyDef; |
|
57 | import com.workingdogs.village.QueryDataSet; |
|
58 | import com.workingdogs.village.Record; |
|
59 | import com.workingdogs.village.Schema; |
|
60 | import com.workingdogs.village.TableDataSet; |
|
61 | ||
62 | /** |
|
63 | * This is the base class for all Peer classes in the system. Peer |
|
64 | * classes are responsible for isolating all of the database access |
|
65 | * for a specific business object. They execute all of the SQL |
|
66 | * against the database. Over time this class has grown to include |
|
67 | * utility methods which ease execution of cross-database queries and |
|
68 | * the implementation of concrete Peers. |
|
69 | * |
|
70 | * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a> |
|
71 | * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a> |
|
72 | * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a> |
|
73 | * @author <a href="mailto:stephenh@chase3000.com">Stephen Haberman</a> |
|
74 | * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a> |
|
75 | * @author <a href="mailto:vido@ldh.org">Augustin Vidovic</a> |
|
76 | * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> |
|
77 | * @version $Id: BasePeer.java,v 1.76.2.5 2004/09/11 14:28:59 seade Exp $ |
|
78 | */ |
|
79 | 0 | public abstract class BasePeer implements java.io.Serializable |
80 | { |
|
81 | /** Constant criteria key to reference ORDER BY columns. */ |
|
82 | public static final String ORDER_BY = "ORDER BY"; |
|
83 | ||
84 | /** |
|
85 | * Constant criteria key to remove Case Information from |
|
86 | * search/ordering criteria. |
|
87 | */ |
|
88 | public static final String IGNORE_CASE = "IgNOrE cAsE"; |
|
89 | ||
90 | /** Classes that implement this class should override this value. */ |
|
91 | public static final String TABLE_NAME = "TABLE_NAME"; |
|
92 | ||
93 | /** |
|
94 | * The Torque default MapBuilder. |
|
95 | * |
|
96 | * @deprecated there is no default map builder! |
|
97 | */ |
|
98 | public static final String DEFAULT_MAP_BUILDER = |
|
99 | "org.apache.torque.util.db.map.TurbineMapBuilder"; |
|
100 | ||
101 | /** Hashtable that contains the cached mapBuilders. */ |
|
102 | 9 | private static Hashtable mapBuilders = new Hashtable(5); |
103 | ||
104 | /** the log */ |
|
105 | 18 | protected static Log log = LogFactory.getLog(BasePeer.class); |
106 | ||
107 | /** |
|
108 | * Converts a hashtable to a byte array for storage/serialization. |
|
109 | * |
|
110 | * @param hash The Hashtable to convert. |
|
111 | * @return A byte[] with the converted Hashtable. |
|
112 | * @throws TorqueException Any exceptions caught during processing will be |
|
113 | * rethrown wrapped into a TorqueException. |
|
114 | */ |
|
115 | public static byte[] hashtableToByteArray(Hashtable hash) |
|
116 | throws TorqueException |
|
117 | { |
|
118 | 0 | Hashtable saveData = new Hashtable(hash.size()); |
119 | 0 | String key = null; |
120 | 0 | Object value = null; |
121 | 0 | byte[] byteArray = null; |
122 | ||
123 | 0 | Iterator keys = hash.keySet().iterator(); |
124 | 0 | while (keys.hasNext()) |
125 | { |
|
126 | 0 | key = (String) keys.next(); |
127 | 0 | value = hash.get(key); |
128 | 0 | if (value instanceof Serializable) |
129 | { |
|
130 | 0 | saveData.put(key, value); |
131 | } |
|
132 | } |
|
133 | ||
134 | 0 | ByteArrayOutputStream baos = null; |
135 | 0 | BufferedOutputStream bos = null; |
136 | 0 | ObjectOutputStream out = null; |
137 | try |
|
138 | { |
|
139 | // These objects are closed in the finally. |
|
140 | 0 | baos = new ByteArrayOutputStream(); |
141 | 0 | bos = new BufferedOutputStream(baos); |
142 | 0 | out = new ObjectOutputStream(bos); |
143 | ||
144 | 0 | out.writeObject(saveData); |
145 | 0 | out.flush(); |
146 | 0 | bos.flush(); |
147 | 0 | byteArray = baos.toByteArray(); |
148 | 0 | } |
149 | 0 | catch (Exception e) |
150 | { |
|
151 | 0 | throwTorqueException(e); |
152 | 0 | } |
153 | finally |
|
154 | { |
|
155 | 0 | if (out != null) |
156 | { |
|
157 | try |
|
158 | { |
|
159 | 0 | out.close(); |
160 | } |
|
161 | 0 | catch (IOException ignored) |
162 | { |
|
163 | 0 | } |
164 | } |
|
165 | ||
166 | 0 | if (bos != null) |
167 | { |
|
168 | try |
|
169 | { |
|
170 | 0 | bos.close(); |
171 | } |
|
172 | 0 | catch (IOException ignored) |
173 | { |
|
174 | 0 | } |
175 | } |
|
176 | ||
177 | 0 | if (baos != null) |
178 | { |
|
179 | try |
|
180 | { |
|
181 | 0 | baos.close(); |
182 | } |
|
183 | 0 | catch (IOException ignored) |
184 | { |
|
185 | 0 | } |
186 | } |
|
187 | 0 | } |
188 | 0 | return byteArray; |
189 | } |
|
190 | ||
191 | private static void throwTorqueException(Exception e) |
|
192 | throws TorqueException |
|
193 | { |
|
194 | 0 | if (e instanceof TorqueException) |
195 | { |
|
196 | 0 | throw (TorqueException)e; |
197 | } |
|
198 | else |
|
199 | { |
|
200 | 0 | throw new TorqueException(e); |
201 | } |
|
202 | } |
|
203 | ||
204 | /** |
|
205 | * Sets up a Schema for a table. This schema is then normally |
|
206 | * used as the argument for initTableColumns(). |
|
207 | * |
|
208 | * @param tableName The name of the table. |
|
209 | * @return A Schema. |
|
210 | */ |
|
211 | public static Schema initTableSchema(String tableName) |
|
212 | { |
|
213 | 0 | return initTableSchema(tableName, Torque.getDefaultDB()); |
214 | } |
|
215 | ||
216 | /** |
|
217 | * Sets up a Schema for a table. This schema is then normally |
|
218 | * used as the argument for initTableColumns |
|
219 | * |
|
220 | * @param tableName The propery name for the database in the |
|
221 | * configuration file. |
|
222 | * @param dbName The name of the database. |
|
223 | * @return A Schema. |
|
224 | */ |
|
225 | public static Schema initTableSchema(String tableName, String dbName) |
|
226 | { |
|
227 | 0 | Schema schema = null; |
228 | 0 | Connection con = null; |
229 | ||
230 | try |
|
231 | { |
|
232 | 0 | con = Torque.getConnection(dbName); |
233 | 0 | schema = new Schema().schema(con, tableName); |
234 | } |
|
235 | 0 | catch (Exception e) |
236 | { |
|
237 | 0 | log.error(e); |
238 | 0 | throw new Error("Error in BasePeer.initTableSchema(" |
239 | + tableName |
|
240 | + "): " |
|
241 | + e.getMessage()); |
|
242 | } |
|
243 | finally |
|
244 | { |
|
245 | 0 | Torque.closeConnection(con); |
246 | 0 | } |
247 | 0 | return schema; |
248 | } |
|
249 | ||
250 | /** |
|
251 | * Creates a Column array for a table based on its Schema. |
|
252 | * |
|
253 | * @param schema A Schema object. |
|
254 | * @return A Column[]. |
|
255 | */ |
|
256 | public static Column[] initTableColumns(Schema schema) |
|
257 | { |
|
258 | 0 | Column[] columns = null; |
259 | try |
|
260 | { |
|
261 | 0 | int numberOfColumns = schema.numberOfColumns(); |
262 | 0 | columns = new Column[numberOfColumns]; |
263 | 0 | for (int i = 0; i < numberOfColumns; i++) |
264 | { |
|
265 | 0 | columns[i] = schema.column(i + 1); |
266 | } |
|
267 | } |
|
268 | 0 | catch (Exception e) |
269 | { |
|
270 | 0 | log.error(e); |
271 | 0 | throw new Error( |
272 | "Error in BasePeer.initTableColumns(): " + e.getMessage()); |
|
273 | 0 | } |
274 | 0 | return columns; |
275 | } |
|
276 | ||
277 | /** |
|
278 | * Convenience method to create a String array of column names. |
|
279 | * |
|
280 | * @param columns A Column[]. |
|
281 | * @return A String[]. |
|
282 | */ |
|
283 | public static String[] initColumnNames(Column[] columns) |
|
284 | { |
|
285 | 0 | String[] columnNames = null; |
286 | 0 | columnNames = new String[columns.length]; |
287 | 0 | for (int i = 0; i < columns.length; i++) |
288 | { |
|
289 | 0 | columnNames[i] = columns[i].name().toUpperCase(); |
290 | } |
|
291 | 0 | return columnNames; |
292 | } |
|
293 | ||
294 | /** |
|
295 | * Convenience method to create a String array of criteria keys. |
|
296 | * |
|
297 | * @param tableName Name of table. |
|
298 | * @param columnNames A String[]. |
|
299 | * @return A String[]. |
|
300 | */ |
|
301 | public static String[] initCriteriaKeys( |
|
302 | String tableName, |
|
303 | String[] columnNames) |
|
304 | { |
|
305 | 0 | String[] keys = new String[columnNames.length]; |
306 | 0 | for (int i = 0; i < columnNames.length; i++) |
307 | { |
|
308 | 0 | keys[i] = tableName + "." + columnNames[i].toUpperCase(); |
309 | } |
|
310 | 0 | return keys; |
311 | } |
|
312 | ||
313 | /** |
|
314 | * Convenience method that uses straight JDBC to delete multiple |
|
315 | * rows. Village throws an Exception when multiple rows are |
|
316 | * deleted. |
|
317 | * |
|
318 | * @param con A Connection. |
|
319 | * @param table The table to delete records from. |
|
320 | * @param column The column in the where clause. |
|
321 | * @param value The value of the column. |
|
322 | * @throws TorqueException Any exceptions caught during processing will be |
|
323 | * rethrown wrapped into a TorqueException. |
|
324 | */ |
|
325 | public static void deleteAll( |
|
326 | Connection con, |
|
327 | String table, |
|
328 | String column, |
|
329 | int value) |
|
330 | throws TorqueException |
|
331 | { |
|
332 | 0 | Statement statement = null; |
333 | try |
|
334 | { |
|
335 | 0 | statement = con.createStatement(); |
336 | ||
337 | 0 | StringBuffer query = new StringBuffer(); |
338 | 0 | query.append("DELETE FROM ") |
339 | .append(table) |
|
340 | .append(" WHERE ") |
|
341 | .append(column) |
|
342 | .append(" = ") |
|
343 | .append(value); |
|
344 | ||
345 | 0 | statement.executeUpdate(query.toString()); |
346 | 0 | } |
347 | 0 | catch (SQLException e) |
348 | { |
|
349 | 0 | throw new TorqueException(e); |
350 | } |
|
351 | finally |
|
352 | { |
|
353 | 0 | if (statement != null) |
354 | { |
|
355 | try |
|
356 | { |
|
357 | 0 | statement.close(); |
358 | } |
|
359 | 0 | catch (SQLException ignored) |
360 | { |
|
361 | 0 | } |
362 | } |
|
363 | 0 | } |
364 | 0 | } |
365 | ||
366 | /** |
|
367 | * Convenience method that uses straight JDBC to delete multiple |
|
368 | * rows. Village throws an Exception when multiple rows are |
|
369 | * deleted. This method attempts to get the default database from |
|
370 | * the pool. |
|
371 | * |
|
372 | * @param table The table to delete records from. |
|
373 | * @param column The column in the where clause. |
|
374 | * @param value The value of the column. |
|
375 | * @throws TorqueException Any exceptions caught during processing will be |
|
376 | * rethrown wrapped into a TorqueException. |
|
377 | */ |
|
378 | public static void deleteAll(String table, String column, int value) |
|
379 | throws TorqueException |
|
380 | { |
|
381 | 0 | Connection con = null; |
382 | try |
|
383 | { |
|
384 | // Get a connection to the db. |
|
385 | 0 | con = Torque.getConnection("default"); |
386 | 0 | deleteAll(con, table, column, value); |
387 | } |
|
388 | finally |
|
389 | { |
|
390 | 0 | Torque.closeConnection(con); |
391 | 0 | } |
392 | 0 | } |
393 | ||
394 | /** |
|
395 | * Method to perform deletes based on values and keys in a |
|
396 | * Criteria. |
|
397 | * |
|
398 | * @param criteria The criteria to use. |
|
399 | * @throws TorqueException Any exceptions caught during processing will be |
|
400 | * rethrown wrapped into a TorqueException. |
|
401 | */ |
|
402 | public static void doDelete(Criteria criteria) throws TorqueException |
|
403 | { |
|
404 | 0 | Connection con = null; |
405 | try |
|
406 | { |
|
407 | 0 | con = Transaction.beginOptional( |
408 | criteria.getDbName(), |
|
409 | criteria.isUseTransaction()); |
|
410 | 0 | doDelete(criteria, con); |
411 | 0 | Transaction.commit(con); |
412 | } |
|
413 | 0 | catch (TorqueException e) |
414 | { |
|
415 | 0 | Transaction.safeRollback(con); |
416 | 0 | throw e; |
417 | 0 | } |
418 | 0 | } |
419 | ||
420 | /** |
|
421 | * Method to perform deletes based on values and keys in a Criteria. |
|
422 | * |
|
423 | * @param criteria The criteria to use. |
|
424 | * @param con A Connection. |
|
425 | * @throws TorqueException Any exceptions caught during processing will be |
|
426 | * rethrown wrapped into a TorqueException. |
|
427 | */ |
|
428 | public static void doDelete(Criteria criteria, Connection con) |
|
429 | throws TorqueException |
|
430 | { |
|
431 | 0 | DB db = Torque.getDB(criteria.getDbName()); |
432 | 0 | DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); |
433 | ||
434 | // Set up a list of required tables and add extra entries to |
|
435 | // criteria if directed to delete all related records. |
|
436 | // StringStack.add() only adds element if it is unique. |
|
437 | 0 | HashSet tables = new HashSet(); |
438 | 0 | Iterator it = criteria.keySet().iterator(); |
439 | 0 | while (it.hasNext()) |
440 | { |
|
441 | 0 | String key = (String) it.next(); |
442 | 0 | Criteria.Criterion c = criteria.getCriterion(key); |
443 | 0 | List tableNames = c.getAllTables(); |
444 | 0 | for (int i = 0; i < tableNames.size(); i++) |
445 | { |
|
446 | 0 | String name = (String) tableNames.get(i); |
447 | 0 | String tableName2 = criteria.getTableForAlias(name); |
448 | 0 | if (tableName2 != null) |
449 | { |
|
450 | 0 | tables.add(new StringBuffer( |
451 | name.length() + tableName2.length() + 1) |
|
452 | .append(tableName2) |
|
453 | .append(' ') |
|
454 | .append(name) |
|
455 | .toString()); |
|
456 | } |
|
457 | else |
|
458 | { |
|
459 | 0 | tables.add(name); |
460 | } |
|
461 | } |
|
462 | ||
463 | 0 | if (criteria.isCascade()) |
464 | { |
|
465 | // This steps thru all the columns in the database. |
|
466 | 0 | TableMap[] tableMaps = dbMap.getTables(); |
467 | 0 | for (int i = 0; i < tableMaps.length; i++) |
468 | { |
|
469 | 0 | ColumnMap[] columnMaps = tableMaps[i].getColumns(); |
470 | 0 | for (int j = 0; j < columnMaps.length; j++) |
471 | { |
|
472 | // Only delete rows where the foreign key is |
|
473 | // also a primary key. Other rows need |
|
474 | // updating, but that is not implemented. |
|
475 | 0 | if (columnMaps[j].isForeignKey() |
476 | && columnMaps[j].isPrimaryKey() |
|
477 | && key.equals(columnMaps[j].getRelatedName())) |
|
478 | { |
|
479 | 0 | tables.add(tableMaps[i].getName()); |
480 | 0 | criteria.add(columnMaps[j].getFullyQualifiedName(), |
481 | criteria.getValue(key)); |
|
482 | } |
|
483 | } |
|
484 | } |
|
485 | } |
|
486 | } |
|
487 | 0 | Iterator tabIt = tables.iterator(); |
488 | 0 | while (tabIt.hasNext()) |
489 | { |
|
490 | 0 | String tab = (String) tabIt.next(); |
491 | 0 | KeyDef kd = new KeyDef(); |
492 | 0 | HashSet whereClause = new HashSet(); |
493 | ||
494 | 0 | ColumnMap[] columnMaps = dbMap.getTable(tab).getColumns(); |
495 | 0 | for (int j = 0; j < columnMaps.length; j++) |
496 | { |
|
497 | 0 | ColumnMap colMap = columnMaps[j]; |
498 | 0 | if (colMap.isPrimaryKey()) |
499 | { |
|
500 | 0 | kd.addAttrib(colMap.getColumnName()); |
501 | } |
|
502 | 0 | String key = new StringBuffer(colMap.getTableName()) |
503 | .append('.') |
|
504 | .append(colMap.getColumnName()) |
|
505 | .toString(); |
|
506 | 0 | if (criteria.containsKey(key)) |
507 | { |
|
508 | 0 | if (criteria.getComparison(key).equals(Criteria.CUSTOM)) |
509 | { |
|
510 | 0 | whereClause.add(criteria.getString(key)); |
511 | } |
|
512 | else |
|
513 | { |
|
514 | 0 | whereClause.add(SqlExpression.build( |
515 | colMap.getColumnName(), |
|
516 | criteria.getValue(key), |
|
517 | criteria.getComparison(key), |
|
518 | criteria.isIgnoreCase(), |
|
519 | db)); |
|
520 | } |
|
521 | } |
|
522 | } |
|
523 | ||
524 | // Execute the statement. |
|
525 | 0 | TableDataSet tds = null; |
526 | try |
|
527 | { |
|
528 | 0 | tds = new TableDataSet(con, tab, kd); |
529 | 0 | String sqlSnippet = StringUtils.join(whereClause.iterator(), " AND "); |
530 | ||
531 | 0 | if (log.isDebugEnabled()) |
532 | { |
|
533 | 0 | log.debug("BasePeer.doDelete: whereClause=" + sqlSnippet); |
534 | } |
|
535 | ||
536 | 0 | tds.where(sqlSnippet); |
537 | 0 | tds.fetchRecords(); |
538 | 0 | if (tds.size() > 1 && criteria.isSingleRecord()) |
539 | { |
|
540 | 0 | handleMultipleRecords(tds); |
541 | } |
|
542 | 0 | for (int j = 0; j < tds.size(); j++) |
543 | { |
|
544 | 0 | Record rec = tds.getRecord(j); |
545 | 0 | rec.markToBeDeleted(); |
546 | 0 | rec.save(); |
547 | } |
|
548 | 0 | } |
549 | 0 | catch (Exception e) |
550 | { |
|
551 | 0 | throwTorqueException(e); |
552 | 0 | } |
553 | finally |
|
554 | { |
|
555 | 0 | if (tds != null) |
556 | { |
|
557 | try |
|
558 | { |
|
559 | 0 | tds.close(); |
560 | } |
|
561 | 0 | catch (Exception ignored) |
562 | { |
|
563 | 0 | } |
564 | } |
|
565 | 0 | } |
566 | } |
|
567 | 0 | } |
568 | ||
569 | /** |
|
570 | * Method to perform inserts based on values and keys in a |
|
571 | * Criteria. |
|
572 | * <p> |
|
573 | * If the primary key is auto incremented the data in Criteria |
|
574 | * will be inserted and the auto increment value will be returned. |
|
575 | * <p> |
|
576 | * If the primary key is included in Criteria then that value will |
|
577 | * be used to insert the row. |
|
578 | * <p> |
|
579 | * If no primary key is included in Criteria then we will try to |
|
580 | * figure out the primary key from the database map and insert the |
|
581 | * row with the next available id using util.db.IDBroker. |
|
582 | * <p> |
|
583 | * If no primary key is defined for the table the values will be |
|
584 | * inserted as specified in Criteria and -1 will be returned. |
|
585 | * |
|
586 | * @param criteria Object containing values to insert. |
|
587 | * @return An Object which is the id of the row that was inserted |
|
588 | * (if the table has a primary key) or null (if the table does not |
|
589 | * have a primary key). |
|
590 | * @throws TorqueException Any exceptions caught during processing will be |
|
591 | * rethrown wrapped into a TorqueException. |
|
592 | */ |
|
593 | public static ObjectKey doInsert(Criteria criteria) throws TorqueException |
|
594 | { |
|
595 | 0 | Connection con = null; |
596 | 0 | ObjectKey id = null; |
597 | ||
598 | try |
|
599 | { |
|
600 | 0 | con = Transaction.beginOptional( |
601 | criteria.getDbName(), |
|
602 | criteria.isUseTransaction()); |
|
603 | 0 | id = doInsert(criteria, con); |
604 | 0 | Transaction.commit(con); |
605 | } |
|
606 | 0 | catch (TorqueException e) |
607 | { |
|
608 | 0 | Transaction.safeRollback(con); |
609 | 0 | throw e; |
610 | 0 | } |
611 | ||
612 | 0 | return id; |
613 | } |
|
614 | ||
615 | /** |
|
616 | * Method to perform inserts based on values and keys in a |
|
617 | * Criteria. |
|
618 | * <p> |
|
619 | * If the primary key is auto incremented the data in Criteria |
|
620 | * will be inserted and the auto increment value will be returned. |
|
621 | * <p> |
|
622 | * If the primary key is included in Criteria then that value will |
|
623 | * be used to insert the row. |
|
624 | * <p> |
|
625 | * If no primary key is included in Criteria then we will try to |
|
626 | * figure out the primary key from the database map and insert the |
|
627 | * row with the next available id using util.db.IDBroker. |
|
628 | * <p> |
|
629 | * If no primary key is defined for the table the values will be |
|
630 | * inserted as specified in Criteria and null will be returned. |
|
631 | * |
|
632 | * @param criteria Object containing values to insert. |
|
633 | * @param con A Connection. |
|
634 | * @return An Object which is the id of the row that was inserted |
|
635 | * (if the table has a primary key) or null (if the table does not |
|
636 | * have a primary key). |
|
637 | * @throws TorqueException Any exceptions caught during processing will be |
|
638 | * rethrown wrapped into a TorqueException. |
|
639 | */ |
|
640 | public static ObjectKey doInsert(Criteria criteria, Connection con) |
|
641 | throws TorqueException |
|
642 | { |
|
643 | 0 | SimpleKey id = null; |
644 | ||
645 | // Get the table name and method for determining the primary |
|
646 | // key value. |
|
647 | 0 | String tableName = null; |
648 | 0 | Iterator keys = criteria.keySet().iterator(); |
649 | 0 | if (keys.hasNext()) |
650 | { |
|
651 | 0 | tableName = criteria.getTableName((String) keys.next()); |
652 | } |
|
653 | else |
|
654 | { |
|
655 | 0 | throw new TorqueException("Database insert attempted without " |
656 | + "anything specified to insert"); |
|
657 | } |
|
658 | ||
659 | 0 | DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); |
660 | 0 | TableMap tableMap = dbMap.getTable(tableName); |
661 | 0 | Object keyInfo = tableMap.getPrimaryKeyMethodInfo(); |
662 | 0 | IdGenerator keyGen = tableMap.getIdGenerator(); |
663 | ||
664 | 0 | ColumnMap pk = getPrimaryKey(criteria); |
665 | ||
666 | // pk will be null if there is no primary key defined for the table |
|
667 | // we're inserting into. |
|
668 | 0 | if (pk != null && !criteria.containsKey(pk.getFullyQualclass="keyword">ifiedName())) |
669 | { |
|
670 | 0 | if (keyGen == null) |
671 | { |
|
672 | 0 | throw new TorqueException( |
673 | "IdGenerator for table '" + tableName + "' is null"); |
|
674 | } |
|
675 | // If the keyMethod is SEQUENCE or IDBROKERTABLE, get the id |
|
676 | // before the insert. |
|
677 | ||
678 | 0 | if (keyGen.isPriorToInsert()) |
679 | { |
|
680 | try |
|
681 | { |
|
682 | 0 | if (pk.getType() instanceof Number) |
683 | { |
|
684 | 0 | id = new NumberKey( |
685 | keyGen.getIdAsBigDecimal(con, keyInfo)); |
|
686 | } |
|
687 | else |
|
688 | { |
|
689 | 0 | id = new StringKey(keyGen.getIdAsString(con, keyInfo)); |
690 | } |
|
691 | } |
|
692 | 0 | catch (Exception e) |
693 | { |
|
694 | 0 | throwTorqueException(e); |
695 | 0 | } |
696 | 0 | criteria.add(pk.getFullyQualifiedName(), id); |
697 | } |
|
698 | } |
|
699 | ||
700 | // Use Village to perform the insert. |
|
701 | 0 | TableDataSet tds = null; |
702 | try |
|
703 | { |
|
704 | 0 | tds = new TableDataSet(con, tableName); |
705 | 0 | Record rec = tds.addRecord(); |
706 | 0 | BasePeer.insertOrUpdateRecord(rec, tableName, criteria); |
707 | 0 | } |
708 | 0 | catch (Exception e) |
709 | { |
|
710 | 0 | throwTorqueException(e); |
711 | 0 | } |
712 | finally |
|
713 | { |
|
714 | 0 | if (tds != null) |
715 | { |
|
716 | try |
|
717 | { |
|
718 | 0 | tds.close(); |
719 | } |
|
720 | 0 | catch (Exception e) |
721 | { |
|
722 | 0 | throwTorqueException(e); |
723 | 0 | } |
724 | } |
|
725 | 0 | } |
726 | ||
727 | // If the primary key column is auto-incremented, get the id |
|
728 | // now. |
|
729 | 0 | if (pk != null && keyGen != class="keyword">null && keyGen.isPostInsert()) |
730 | { |
|
731 | try |
|
732 | { |
|
733 | 0 | if (pk.getType() instanceof Number) |
734 | { |
|
735 | 0 | id = new NumberKey(keyGen.getIdAsBigDecimal(con, keyInfo)); |
736 | } |
|
737 | else |
|
738 | { |
|
739 | 0 | id = new StringKey(keyGen.getIdAsString(con, keyInfo)); |
740 | } |
|
741 | } |
|
742 | 0 | catch (Exception e) |
743 | { |
|
744 | 0 | throwTorqueException(e); |
745 | 0 | } |
746 | } |
|
747 | ||
748 | 0 | return id; |
749 | } |
|
750 | ||
751 | /** |
|
752 | * Grouping of code used in both doInsert() and doUpdate() |
|
753 | * methods. Sets up a Record for saving. |
|
754 | * |
|
755 | * @param rec A Record. |
|
756 | * @param tableName Name of table. |
|
757 | * @param criteria A Criteria. |
|
758 | * @throws TorqueException Any exceptions caught during processing will be |
|
759 | * rethrown wrapped into a TorqueException. |
|
760 | */ |
|
761 | private static void insertOrUpdateRecord( |
|
762 | Record rec, |
|
763 | String tableName, |
|
764 | Criteria criteria) |
|
765 | throws TorqueException |
|
766 | { |
|
767 | 0 | DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); |
768 | ||
769 | 0 | ColumnMap[] columnMaps = dbMap.getTable(tableName).getColumns(); |
770 | 0 | boolean shouldSave = false; |
771 | 0 | for (int j = 0; j < columnMaps.length; j++) |
772 | { |
|
773 | 0 | ColumnMap colMap = columnMaps[j]; |
774 | 0 | String key = new StringBuffer(colMap.getTableName()) |
775 | .append('.') |
|
776 | .append(colMap.getColumnName()) |
|
777 | .toString(); |
|
778 | 0 | if (criteria.containsKey(key)) |
779 | { |
|
780 | // A village Record.setValue( String, Object ) would |
|
781 | // be nice here. |
|
782 | 0 | Object obj = criteria.getValue(key); |
783 | 0 | if (obj instanceof SimpleKey) |
784 | { |
|
785 | 0 | obj = ((SimpleKey) obj).getValue(); |
786 | } |
|
787 | try |
|
788 | { |
|
789 | 0 | if (obj == null) |
790 | { |
|
791 | 0 | rec.setValueNull(colMap.getColumnName()); |
792 | } |
|
793 | 0 | else if (obj instanceof String) |
794 | { |
|
795 | 0 | rec.setValue(colMap.getColumnName(), (String) obj); |
796 | } |
|
797 | 0 | else if (obj instanceof Integer) |
798 | { |
|
799 | 0 | rec.setValue(colMap.getColumnName(), |
800 | criteria.getInt(key)); |
|
801 | } |
|
802 | 0 | else if (obj instanceof BigDecimal) |
803 | { |
|
804 | 0 | rec.setValue(colMap.getColumnName(), (BigDecimal) obj); |
805 | } |
|
806 | 0 | else if (obj instanceof Boolean) |
807 | { |
|
808 | 0 | rec.setValue(colMap.getColumnName(), |
809 | criteria.getBoolean(key) ? 1 : 0); |
|
810 | } |
|
811 | 0 | else if (obj instanceof java.util.Date) |
812 | { |
|
813 | 0 | rec.setValue(colMap.getColumnName(), |
814 | (java.util.Date) obj); |
|
815 | } |
|
816 | 0 | else if (obj instanceof Float) |
817 | { |
|
818 | 0 | rec.setValue(colMap.getColumnName(), |
819 | criteria.getFloat(key)); |
|
820 | } |
|
821 | 0 | else if (obj instanceof Double) |
822 | { |
|
823 | 0 | rec.setValue(colMap.getColumnName(), |
824 | criteria.getDouble(key)); |
|
825 | } |
|
826 | 0 | else if (obj instanceof Byte) |
827 | { |
|
828 | 0 | rec.setValue(colMap.getColumnName(), |
829 | ((Byte) obj).byteValue()); |
|
830 | } |
|
831 | 0 | else if (obj instanceof Long) |
832 | { |
|
833 | 0 | rec.setValue(colMap.getColumnName(), |
834 | criteria.getLong(key)); |
|
835 | } |
|
836 | 0 | else if (obj instanceof Short) |
837 | { |
|
838 | 0 | rec.setValue(colMap.getColumnName(), |
839 | ((Short) obj).shortValue()); |
|
840 | } |
|
841 | 0 | else if (obj instanceof Hashtable) |
842 | { |
|
843 | 0 | rec.setValue(colMap.getColumnName(), |
844 | hashtableToByteArray((Hashtable) obj)); |
|
845 | } |
|
846 | 0 | else if (obj instanceof byte[]) |
847 | { |
|
848 | 0 | rec.setValue(colMap.getColumnName(), (byte[]) obj); |
849 | } |
|
850 | } |
|
851 | 0 | catch (Exception e) |
852 | { |
|
853 | 0 | throwTorqueException(e); |
854 | 0 | } |
855 | 0 | shouldSave = true; |
856 | } |
|
857 | } |
|
858 | ||
859 | 0 | if (shouldSave) |
860 | { |
|
861 | try |
|
862 | { |
|
863 | 0 | rec.save(); |
864 | } |
|
865 | 0 | catch (Exception e) |
866 | { |
|
867 | 0 | throwTorqueException(e); |
868 | 0 | } |
869 | } |
|
870 | else |
|
871 | { |
|
872 | 0 | throw new TorqueException("No changes to save"); |
873 | } |
|
874 | 0 | } |
875 | ||
876 | /** |
|
877 | * Method to create an SQL query for display only based on values in a |
|
878 | * Criteria. |
|
879 | * |
|
880 | * @param criteria A Criteria. |
|
881 | * @return the SQL query for display |
|
882 | * @exception TorqueException Trouble creating the query string. |
|
883 | */ |
|
884 | static String createQueryDisplayString(Criteria criteria) |
|
885 | throws TorqueException |
|
886 | { |
|
887 | 36 | return createQuery(criteria).toString(); |
888 | } |
|
889 | ||
890 | /** |
|
891 | * Method to create an SQL query for actual execution based on values in a |
|
892 | * Criteria. |
|
893 | * |
|
894 | * @param criteria A Criteria. |
|
895 | * @return the SQL query for actual execution |
|
896 | * @exception TorqueException Trouble creating the query string. |
|
897 | */ |
|
898 | public static String createQueryString(Criteria criteria) |
|
899 | throws TorqueException |
|
900 | { |
|
901 | 81 | Query query = createQuery(criteria); |
902 | 81 | return query.toString(); |
903 | } |
|
904 | ||
905 | /** |
|
906 | * Method to create an SQL query based on values in a Criteria. Note that |
|
907 | * final manipulation of the limit and offset are performed when the query |
|
908 | * is actually executed. |
|
909 | * |
|
910 | * @param criteria A Criteria. |
|
911 | * @return the sql query |
|
912 | * @exception TorqueException Trouble creating the query string. |
|
913 | */ |
|
914 | static Query createQuery(Criteria criteria) |
|
915 | throws TorqueException |
|
916 | { |
|
917 | 117 | Query query = new Query(); |
918 | 117 | DB db = Torque.getDB(criteria.getDbName()); |
919 | 117 | DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); |
920 | ||
921 | 117 | UniqueList selectModifiers = query.getSelectModifiers(); |
922 | 117 | UniqueList selectClause = query.getSelectClause(); |
923 | 117 | UniqueList fromClause = query.getFromClause(); |
924 | 117 | UniqueList whereClause = query.getWhereClause(); |
925 | 117 | UniqueList orderByClause = query.getOrderByClause(); |
926 | 117 | UniqueList groupByClause = query.getGroupByClause(); |
927 | ||
928 | 117 | UniqueList orderBy = criteria.getOrderByColumns(); |
929 | 117 | UniqueList groupBy = criteria.getGroupByColumns(); |
930 | 117 | UniqueList select = criteria.getSelectColumns(); |
931 | 117 | Hashtable aliases = criteria.getAsColumns(); |
932 | 117 | UniqueList modifiers = criteria.getSelectModifiers(); |
933 | ||
934 | 117 | for (int i = 0; i < modifiers.size(); i++) |
935 | { |
|
936 | 0 | selectModifiers.add(modifiers.get(i)); |
937 | } |
|
938 | ||
939 | 126 | for (int i = 0; i < select.size(); i++) |
940 | { |
|
941 | 9 | String columnName = (String) select.get(i); |
942 | 9 | if (columnName.indexOf('.') == -1 && columnName.indexOf('*') == -1) |
943 | { |
|
944 | 0 | throwMalformedColumnNameException("select", columnName); |
945 | } |
|
946 | 9 | String tableName = null; |
947 | 9 | selectClause.add(columnName); |
948 | 9 | int parenPos = columnName.indexOf('('); |
949 | 9 | if (parenPos == -1) |
950 | { |
|
951 | 0 | tableName = columnName.substring(0, columnName.indexOf('.')); |
952 | } |
|
953 | 9 | else if (columnName.indexOf('.') > -1) |
954 | { |
|
955 | 0 | tableName = |
956 | columnName.substring(parenPos + 1, columnName.indexOf('.')); |
|
957 | // functions may contain qualifiers so only take the last |
|
958 | // word as the table name. |
|
959 | 0 | int lastSpace = tableName.lastIndexOf(' '); |
960 | 0 | if (lastSpace != -1) |
961 | { |
|
962 | 0 | tableName = tableName.substring(lastSpace + 1); |
963 | } |
|
964 | } |
|
965 | 9 | String tableName2 = criteria.getTableForAlias(tableName); |
966 | 9 | if (tableName2 != null) |
967 | { |
|
968 | 0 | fromClause.add(new StringBuffer( |
969 | tableName.length() + tableName2.length() + 1) |
|
970 | .append(tableName2) |
|
971 | .append(' ') |
|
972 | .append(tableName) |
|
973 | .toString()); |
|
974 | } |
|
975 | else |
|
976 | { |
|
977 | 9 | fromClause.add(tableName); |
978 | } |
|
979 | } |
|
980 | ||
981 | 117 | Iterator it = aliases.keySet().iterator(); |
982 | 117 | while (it.hasNext()) |
983 | { |
|
984 | 0 | String key = (String) it.next(); |
985 | 0 | selectClause.add((String) aliases.get(key) + " AS " + key); |
986 | } |
|
987 | ||
988 | 117 | Iterator critKeys = criteria.keySet().iterator(); |
989 | 252 | while (critKeys.hasNext()) |
990 | { |
|
991 | 135 | String key = (String) critKeys.next(); |
992 | 135 | Criteria.Criterion criterion = criteria.getCriterion(key); |
993 | 135 | Criteria.Criterion[] someCriteria = |
994 | criterion.getAttachedCriterion(); |
|
995 | 135 | String table = null; |
996 | 306 | for (int i = 0; i < someCriteria.length; i++) |
997 | { |
|
998 | 171 | String tableName = someCriteria[i].getTable(); |
999 | 171 | table = criteria.getTableForAlias(tableName); |
1000 | 171 | if (table != null) |
1001 | { |
|
1002 | 0 | fromClause.add(new StringBuffer( |
1003 | tableName.length() + table.length() + 1) |
|
1004 | .append(table) |
|
1005 | .append(' ') |
|
1006 | .append(tableName) |
|
1007 | .toString()); |
|
1008 | } |
|
1009 | else |
|
1010 | { |
|
1011 | 171 | fromClause.add(tableName); |
1012 | 171 | table = tableName; |
1013 | } |
|
1014 | ||
1015 | 171 | boolean ignorCase = ((criteria.isIgnoreCase() |
1016 | || someCriteria[i].isIgnoreCase()) |
|
1017 | && (dbMap |
|
1018 | .getTable(table) |
|
1019 | .getColumn(someCriteria[i].getColumn()) |
|
1020 | .getType() |
|
1021 | instanceof String)); |
|
1022 | ||
1023 | 171 | someCriteria[i].setIgnoreCase(ignorCase); |
1024 | } |
|
1025 | ||
1026 | 135 | criterion.setDB(db); |
1027 | 135 | whereClause.add(criterion.toString()); |
1028 | } |
|
1029 | ||
1030 | 117 | List join = criteria.getJoinL(); |
1031 | 117 | if (join != null) |
1032 | { |
|
1033 | 0 | for (int i = 0; i < join.size(); i++) |
1034 | { |
|
1035 | 0 | String join1 = (String) join.get(i); |
1036 | 0 | String join2 = (String) criteria.getJoinR().get(i); |
1037 | 0 | if (join1.indexOf('.') == -1) |
1038 | { |
|
1039 | 0 | throwMalformedColumnNameException("join", join1); |
1040 | } |
|
1041 | 0 | if (join2.indexOf('.') == -1) |
1042 | { |
|
1043 | 0 | throwMalformedColumnNameException("join", join2); |
1044 | } |
|
1045 | ||
1046 | 0 | String tableName = join1.substring(0, join1.indexOf('.')); |
1047 | 0 | String table = criteria.getTableForAlias(tableName); |
1048 | 0 | if (table != null) |
1049 | { |
|
1050 | 0 | fromClause.add(new StringBuffer( |
1051 | tableName.length() + table.length() + 1) |
|
1052 | .append(table) |
|
1053 | .append(' ') |
|
1054 | .append(tableName) |
|
1055 | .toString()); |
|
1056 | } |
|
1057 | else |
|
1058 | { |
|
1059 | 0 | fromClause.add(tableName); |
1060 | } |
|
1061 | ||
1062 | 0 | int dot = join2.indexOf('.'); |
1063 | 0 | tableName = join2.substring(0, dot); |
1064 | 0 | table = criteria.getTableForAlias(tableName); |
1065 | 0 | if (table != null) |
1066 | { |
|
1067 | 0 | fromClause.add(new StringBuffer( |
1068 | tableName.length() + table.length() + 1) |
|
1069 | .append(table) |
|
1070 | .append(' ') |
|
1071 | .append(tableName) |
|
1072 | .toString()); |
|
1073 | } |
|
1074 | else |
|
1075 | { |
|
1076 | 0 | fromClause.add(tableName); |
1077 | 0 | table = tableName; |
1078 | } |
|
1079 | ||
1080 | 0 | boolean ignorCase = (criteria.isIgnoreCase() |
1081 | && (dbMap |
|
1082 | .getTable(table) |
|
1083 | .getColumn(join2.substring(dot + 1, join2.length())) |
|
1084 | .getType() |
|
1085 | instanceof String)); |
|
1086 | ||
1087 | 0 | whereClause.add( |
1088 | SqlExpression.buildInnerJoin(join1, join2, ignorCase, db)); |
|
1089 | } |
|
1090 | } |
|
1091 | ||
1092 | // need to allow for multiple group bys |
|
1093 | 117 | if (groupBy != null && groupBy.size() > 0) |
1094 | { |
|
1095 | 0 | for (int i = 0; i < groupBy.size(); i++) |
1096 | { |
|
1097 | 0 | String groupByColumn = (String) groupBy.get(i); |
1098 | 0 | if (groupByColumn.indexOf('.') == -1) |
1099 | { |
|
1100 | 0 | throwMalformedColumnNameException("group by", |
1101 | groupByColumn); |
|
1102 | } |
|
1103 | 0 | groupByClause.add(groupByColumn); |
1104 | } |
|
1105 | } |
|
1106 | ||
1107 | 117 | Criteria.Criterion having = criteria.getHaving(); |
1108 | 117 | if (having != null) |
1109 | { |
|
1110 | //String groupByString = null; |
|
1111 | 0 | query.setHaving(having.toString()); |
1112 | } |
|
1113 | ||
1114 | 117 | if (orderBy != null && orderBy.size() > 0) |
1115 | { |
|
1116 | // Check for each String/Character column and apply |
|
1117 | // toUpperCase(). |
|
1118 | 0 | for (int i = 0; i < orderBy.size(); i++) |
1119 | { |
|
1120 | 0 | String orderByColumn = (String) orderBy.get(i); |
1121 | 0 | if (orderByColumn.indexOf('.') == -1) |
1122 | { |
|
1123 | 0 | throwMalformedColumnNameException("order by", |
1124 | orderByColumn); |
|
1125 | } |
|
1126 | 0 | String tableName = |
1127 | orderByColumn.substring(0, orderByColumn.indexOf('.')); |
|
1128 | 0 | String table = criteria.getTableForAlias(tableName); |
1129 | 0 | if (table == null) |
1130 | { |
|
1131 | 0 | table = tableName; |
1132 | } |
|
1133 | ||
1134 | // See if there's a space (between the column list and sort |
|
1135 | // order in ORDER BY table.column DESC). |
|
1136 | 0 | int spacePos = orderByColumn.indexOf(' '); |
1137 | String columnName; |
|
1138 | 0 | if (spacePos == -1) |
1139 | { |
|
1140 | 0 | columnName = |
1141 | orderByColumn.substring(orderByColumn.indexOf('.') + 1); |
|
1142 | } |
|
1143 | else |
|
1144 | { |
|
1145 | 0 | columnName = orderByColumn.substring( |
1146 | orderByColumn.indexOf('.') + 1, spacePos); |
|
1147 | } |
|
1148 | 0 | ColumnMap column = dbMap.getTable(table).getColumn(columnName); |
1149 | 0 | if (column.getType() instanceof String) |
1150 | { |
|
1151 | 0 | if (spacePos == -1) |
1152 | { |
|
1153 | 0 | orderByClause.add( |
1154 | db.ignoreCaseInOrderBy(orderByColumn)); |
|
1155 | } |
|
1156 | else |
|
1157 | { |
|
1158 | 0 | orderByClause.add(db.ignoreCaseInOrderBy( |
1159 | orderByColumn.substring(0, spacePos)) |
|
1160 | + orderByColumn.substring(spacePos)); |
|
1161 | } |
|
1162 | 0 | selectClause.add( |
1163 | db.ignoreCaseInOrderBy(table + '.' + columnName)); |
|
1164 | } |
|
1165 | else |
|
1166 | { |
|
1167 | 0 | orderByClause.add(orderByColumn); |
1168 | } |
|
1169 | } |
|
1170 | } |
|
1171 | ||
1172 | 117 | LimitHelper.buildLimit(criteria, query); |
1173 | 117 | if (log.isDebugEnabled()) |
1174 | { |
|
1175 | 0 | log.debug(query.toString()); |
1176 | } |
|
1177 | 117 | return query; |
1178 | } |
|
1179 | ||
1180 | /** |
|
1181 | * Returns all results. |
|
1182 | * |
|
1183 | * @param criteria A Criteria. |
|
1184 | * @return List of Record objects. |
|
1185 | * @throws TorqueException Any exceptions caught during processing will be |
|
1186 | * rethrown wrapped into a TorqueException. |
|
1187 | */ |
|
1188 | public static List doSelect(Criteria criteria) throws TorqueException |
|
1189 | { |
|
1190 | 0 | Connection con = null; |
1191 | 0 | List results = null; |
1192 | ||
1193 | try |
|
1194 | { |
|
1195 | 0 | con = Transaction.beginOptional( |
1196 | criteria.getDbName(), |
|
1197 | criteria.isUseTransaction()); |
|
1198 | 0 | results = doSelect(criteria, con); |
1199 | 0 | Transaction.commit(con); |
1200 | } |
|
1201 | 0 | catch (Exception e) |
1202 | { |
|
1203 | 0 | Transaction.safeRollback(con); |
1204 | 0 | throwTorqueException(e); |
1205 | 0 | } |
1206 | 0 | return results; |
1207 | } |
|
1208 | ||
1209 | /** |
|
1210 | * Returns all results. |
|
1211 | * |
|
1212 | * @param criteria A Criteria. |
|
1213 | * @param con A Connection. |
|
1214 | * @return List of Record objects. |
|
1215 | * @throws TorqueException Any exceptions caught during processing will be |
|
1216 | * rethrown wrapped into a TorqueException. |
|
1217 | */ |
|
1218 | public static List doSelect(Criteria criteria, Connection con) |
|
1219 | throws TorqueException |
|
1220 | { |
|
1221 | 0 | Query query = createQuery(criteria); |
1222 | ||
1223 | 0 | if (query.hasLimit()) |
1224 | { |
|
1225 | // We don't need Village to limit the Query |
|
1226 | 0 | return executeQuery(query.toString(), |
1227 | 0, |
|
1228 | -1, |
|
1229 | criteria.isSingleRecord(), |
|
1230 | con); |
|
1231 | } |
|
1232 | else |
|
1233 | { |
|
1234 | // There is not limit string registered |
|
1235 | // with the query. Let Village decide. |
|
1236 | 0 | return executeQuery(query.toString(), |
1237 | criteria.getOffset(), |
|
1238 | criteria.getLimit(), |
|
1239 | criteria.isSingleRecord(), |
|
1240 | con); |
|
1241 | } |
|
1242 | } |
|
1243 | ||
1244 | /** |
|
1245 | * Utility method which executes a given sql statement. This |
|
1246 | * method should be used for select statements only. Use |
|
1247 | * executeStatement for update, insert, and delete operations. |
|
1248 | * |
|
1249 | * @param queryString A String with the sql statement to execute. |
|
1250 | * @return List of Record objects. |
|
1251 | * @throws TorqueException Any exceptions caught during processing will be |
|
1252 | * rethrown wrapped into a TorqueException. |
|
1253 | */ |
|
1254 | public static List executeQuery(String queryString) throws TorqueException |
|
1255 | { |
|
1256 | 0 | return executeQuery(queryString, Torque.getDefaultDB(), false); |
1257 | } |
|
1258 | ||
1259 | /** |
|
1260 | * Utility method which executes a given sql statement. This |
|
1261 | * method should be used for select statements only. Use |
|
1262 | * executeStatement for update, insert, and delete operations. |
|
1263 | * |
|
1264 | * @param queryString A String with the sql statement to execute. |
|
1265 | * @param dbName The database to connect to. |
|
1266 | * @return List of Record objects. |
|
1267 | * @throws TorqueException Any exceptions caught during processing will be |
|
1268 | * rethrown wrapped into a TorqueException. |
|
1269 | */ |
|
1270 | public static List executeQuery(String queryString, String dbName) |
|
1271 | throws TorqueException |
|
1272 | { |
|
1273 | 0 | return executeQuery(queryString, dbName, false); |
1274 | } |
|
1275 | ||
1276 | /** |
|
1277 | * Method for performing a SELECT. Returns all results. |
|
1278 | * |
|
1279 | * @param queryString A String with the sql statement to execute. |
|
1280 | * @param dbName The database to connect to. |
|
1281 | * @param singleRecord Whether or not we want to select only a |
|
1282 | * single record. |
|
1283 | * @return List of Record objects. |
|
1284 | * @throws TorqueException Any exceptions caught during processing will be |
|
1285 | * rethrown wrapped into a TorqueException. |
|
1286 | */ |
|
1287 | public static List executeQuery( |
|
1288 | String queryString, |
|
1289 | String dbName, |
|
1290 | boolean singleRecord) |
|
1291 | throws TorqueException |
|
1292 | { |
|
1293 | 0 | return executeQuery(queryString, 0, -1, dbName, singleRecord); |
1294 | } |
|
1295 | ||
1296 | /** |
|
1297 | * Method for performing a SELECT. Returns all results. |
|
1298 | * |
|
1299 | * @param queryString A String with the sql statement to execute. |
|
1300 | * @param singleRecord Whether or not we want to select only a |
|
1301 | * single record. |
|
1302 | * @param con A Connection. |
|
1303 | * @return List of Record objects. |
|
1304 | * @throws TorqueException Any exceptions caught during processing will be |
|
1305 | * rethrown wrapped into a TorqueException. |
|
1306 | */ |
|
1307 | public static List executeQuery( |
|
1308 | String queryString, |
|
1309 | boolean singleRecord, |
|
1310 | Connection con) |
|
1311 | throws TorqueException |
|
1312 | { |
|
1313 | 0 | return executeQuery(queryString, 0, -1, singleRecord, con); |
1314 | } |
|
1315 | ||
1316 | /** |
|
1317 | * Method for performing a SELECT. |
|
1318 | * |
|
1319 | * @param queryString A String with the sql statement to execute. |
|
1320 | * @param start The first row to return. |
|
1321 | * @param numberOfResults The number of rows to return. |
|
1322 | * @param dbName The database to connect to. |
|
1323 | * @param singleRecord Whether or not we want to select only a |
|
1324 | * single record. |
|
1325 | * @return List of Record objects. |
|
1326 | * @throws TorqueException Any exceptions caught during processing will be |
|
1327 | * rethrown wrapped into a TorqueException. |
|
1328 | */ |
|
1329 | public static List executeQuery( |
|
1330 | String queryString, |
|
1331 | int start, |
|
1332 | int numberOfResults, |
|
1333 | String dbName, |
|
1334 | boolean singleRecord) |
|
1335 | throws TorqueException |
|
1336 | { |
|
1337 | 0 | Connection db = null; |
1338 | 0 | List results = null; |
1339 | try |
|
1340 | { |
|
1341 | 0 | db = Torque.getConnection(dbName); |
1342 | // execute the query |
|
1343 | 0 | results = executeQuery( |
1344 | queryString, |
|
1345 | start, |
|
1346 | numberOfResults, |
|
1347 | singleRecord, |
|
1348 | db); |
|
1349 | } |
|
1350 | finally |
|
1351 | { |
|
1352 | 0 | Torque.closeConnection(db); |
1353 | 0 | } |
1354 | 0 | return results; |
1355 | } |
|
1356 | ||
1357 | /** |
|
1358 | * Method for performing a SELECT. Returns all results. |
|
1359 | * |
|
1360 | * @param queryString A String with the sql statement to execute. |
|
1361 | * @param start The first row to return. |
|
1362 | * @param numberOfResults The number of rows to return. |
|
1363 | * @param singleRecord Whether or not we want to select only a |
|
1364 | * single record. |
|
1365 | * @param con A Connection. |
|
1366 | * @return List of Record objects. |
|
1367 | * @throws TorqueException Any exceptions caught during processing will be |
|
1368 | * rethrown wrapped into a TorqueException. |
|
1369 | */ |
|
1370 | public static List executeQuery( |
|
1371 | String queryString, |
|
1372 | int start, |
|
1373 | int numberOfResults, |
|
1374 | boolean singleRecord, |
|
1375 | Connection con) |
|
1376 | throws TorqueException |
|
1377 | { |
|
1378 | 0 | QueryDataSet qds = null; |
1379 | 0 | List results = Collections.EMPTY_LIST; |
1380 | try |
|
1381 | { |
|
1382 | // execute the query |
|
1383 | 0 | long startTime = System.currentTimeMillis(); |
1384 | 0 | qds = new QueryDataSet(con, queryString); |
1385 | 0 | if (log.isDebugEnabled()) |
1386 | { |
|
1387 | 0 | log.debug("Elapsed time=" |
1388 | + (System.currentTimeMillis() - startTime) + " ms"); |
|
1389 | } |
|
1390 | 0 | results = getSelectResults( |
1391 | qds, start, numberOfResults, singleRecord); |
|
1392 | 0 | } |
1393 | 0 | catch (Exception e) |
1394 | { |
|
1395 | 0 | throwTorqueException(e); |
1396 | 0 | } |
1397 | finally |
|
1398 | { |
|
1399 | 0 | if (qds != null) |
1400 | { |
|
1401 | try |
|
1402 | { |
|
1403 | 0 | qds.close(); |
1404 | } |
|
1405 | 0 | catch (Exception ignored) |
1406 | { |
|
1407 | 0 | } |
1408 | } |
|
1409 | 0 | } |
1410 | 0 | return results; |
1411 | } |
|
1412 | ||
1413 | /** |
|
1414 | * Returns all records in a QueryDataSet as a List of Record |
|
1415 | * objects. Used for functionality like util.LargeSelect. |
|
1416 | * |
|
1417 | * @see #getSelectResults(QueryDataSet, int, int, boolean) |
|
1418 | * @param qds the QueryDataSet |
|
1419 | * @return a List of Record objects |
|
1420 | * @throws TorqueException Any exceptions caught during processing will be |
|
1421 | * rethrown wrapped into a TorqueException. |
|
1422 | */ |
|
1423 | public static List getSelectResults(QueryDataSet qds) |
|
1424 | throws TorqueException |
|
1425 | { |
|
1426 | 0 | return getSelectResults(qds, 0, -1, false); |
1427 | } |
|
1428 | ||
1429 | /** |
|
1430 | * Returns all records in a QueryDataSet as a List of Record |
|
1431 | * objects. Used for functionality like util.LargeSelect. |
|
1432 | * |
|
1433 | * @see #getSelectResults(QueryDataSet, int, int, boolean) |
|
1434 | * @param qds the QueryDataSet |
|
1435 | * @param singleRecord |
|
1436 | * @return a List of Record objects |
|
1437 | * @throws TorqueException Any exceptions caught during processing will be |
|
1438 | * rethrown wrapped into a TorqueException. |
|
1439 | */ |
|
1440 | public static List getSelectResults(QueryDataSet qds, boolean singleRecord) |
|
1441 | throws TorqueException |
|
1442 | { |
|
1443 | 0 | return getSelectResults(qds, 0, -1, singleRecord); |
1444 | } |
|
1445 | ||
1446 | /** |
|
1447 | * Returns numberOfResults records in a QueryDataSet as a List |
|
1448 | * of Record objects. Starting at record 0. Used for |
|
1449 | * functionality like util.LargeSelect. |
|
1450 | * |
|
1451 | * @see #getSelectResults(QueryDataSet, int, int, boolean) |
|
1452 | * @param qds the QueryDataSet |
|
1453 | * @param numberOfResults |
|
1454 | * @param singleRecord |
|
1455 | * @return a List of Record objects |
|
1456 | * @throws TorqueException Any exceptions caught during processing will be |
|
1457 | * rethrown wrapped into a TorqueException. |
|
1458 | */ |
|
1459 | public static List getSelectResults( |
|
1460 | QueryDataSet qds, |
|
1461 | int numberOfResults, |
|
1462 | boolean singleRecord) |
|
1463 | throws TorqueException |
|
1464 | { |
|
1465 | 0 | List results = null; |
1466 | 0 | if (numberOfResults != 0) |
1467 | { |
|
1468 | 0 | results = getSelectResults(qds, 0, numberOfResults, singleRecord); |
1469 | } |
|
1470 | 0 | return results; |
1471 | } |
|
1472 | ||
1473 | /** |
|
1474 | * Returns numberOfResults records in a QueryDataSet as a List |
|
1475 | * of Record objects. Starting at record start. Used for |
|
1476 | * functionality like util.LargeSelect. |
|
1477 | * |
|
1478 | * @param qds The <code>QueryDataSet</code> to extract results |
|
1479 | * from. |
|
1480 | * @param start The index from which to start retrieving |
|
1481 | * <code>Record</code> objects from the data set. |
|
1482 | * @param numberOfResults The number of results to return (or |
|
1483 | * <code> -1</code> for all results). |
|
1484 | * @param singleRecord Whether or not we want to select only a |
|
1485 | * single record. |
|
1486 | * @return A <code>List</code> of <code>Record</code> objects. |
|
1487 | * @exception TorqueException If any <code>Exception</code> occurs. |
|
1488 | */ |
|
1489 | public static List getSelectResults( |
|
1490 | QueryDataSet qds, |
|
1491 | int start, |
|
1492 | int numberOfResults, |
|
1493 | boolean singleRecord) |
|
1494 | throws TorqueException |
|
1495 | { |
|
1496 | 0 | List results = null; |
1497 | try |
|
1498 | { |
|
1499 | 0 | if (numberOfResults <= 0) |
1500 | { |
|
1501 | 0 | results = new ArrayList(); |
1502 | 0 | qds.fetchRecords(); |
1503 | } |
|
1504 | else |
|
1505 | { |
|
1506 | 0 | results = new ArrayList(numberOfResults); |
1507 | 0 | qds.fetchRecords(start, numberOfResults); |
1508 | } |
|
1509 | 0 | if (qds.size() > 1 && singleRecord) |
1510 | { |
|
1511 | 0 | handleMultipleRecords(qds); |
1512 | } |
|
1513 | ||
1514 | 0 | int startRecord = 0; |
1515 | ||
1516 | //Offset the correct number of people |
|
1517 | 0 | if (start > 0 && numberOfResults <= 0) |
1518 | { |
|
1519 | 0 | startRecord = start; |
1520 | } |
|
1521 | ||
1522 | // Return a List of Record objects. |
|
1523 | 0 | for (int i = startRecord; i < qds.size(); i++) |
1524 | { |
|
1525 | 0 | Record rec = qds.getRecord(i); |
1526 | 0 | results.add(rec); |
1527 | } |
|
1528 | } |
|
1529 | 0 | catch (Exception e) |
1530 | { |
|
1531 | 0 | throwTorqueException(e); |
1532 | 0 | } |
1533 | 0 | return results; |
1534 | } |
|
1535 | ||
1536 | /** |
|
1537 | * Helper method which returns the primary key contained |
|
1538 | * in the given Criteria object. |
|
1539 | * |
|
1540 | * @param criteria A Criteria. |
|
1541 | * @return ColumnMap if the Criteria object contains a primary |
|
1542 | * key, or null if it doesn't. |
|
1543 | * @throws TorqueException Any exceptions caught during processing will be |
|
1544 | * rethrown wrapped into a TorqueException. |
|
1545 | */ |
|
1546 | private static ColumnMap getPrimaryKey(Criteria criteria) |
|
1547 | throws TorqueException |
|
1548 | { |
|
1549 | // Assume all the keys are for the same table. |
|
1550 | 0 | String key = (String) criteria.keys().nextElement(); |
1551 | ||
1552 | 0 | String table = criteria.getTableName(key); |
1553 | 0 | ColumnMap pk = null; |
1554 | ||
1555 | 0 | if (!table.equals("")) |
1556 | { |
|
1557 | 0 | DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); |
1558 | 0 | if (dbMap == null) |
1559 | { |
|
1560 | 0 | throw new TorqueException("dbMap is null"); |
1561 | } |
|
1562 | 0 | if (dbMap.getTable(table) == null) |
1563 | { |
|
1564 | 0 | throw new TorqueException("dbMap.getTable() is null"); |
1565 | } |
|
1566 | ||
1567 | 0 | ColumnMap[] columns = dbMap.getTable(table).getColumns(); |
1568 | ||
1569 | 0 | for (int i = 0; i < columns.length; i++) |
1570 | { |
|
1571 | 0 | if (columns[i].isPrimaryKey()) |
1572 | { |
|
1573 | 0 | pk = columns[i]; |
1574 | 0 | break; |
1575 | } |
|
1576 | } |
|
1577 | } |
|
1578 | 0 | return pk; |
1579 | } |
|
1580 | ||
1581 | /** |
|
1582 | * Convenience method used to update rows in the DB. Checks if a |
|
1583 | * <i>single</i> int primary key is specified in the Criteria |
|
1584 | * object and uses it to perform the udpate. If no primary key is |
|
1585 | * specified an Exception will be thrown. |
|
1586 | * <p> |
|
1587 | * Use this method for performing an update of the kind: |
|
1588 | * <p> |
|
1589 | * "WHERE primary_key_id = an int" |
|
1590 | * <p> |
|
1591 | * To perform an update with non-primary key fields in the WHERE |
|
1592 | * clause use doUpdate(criteria, criteria). |
|
1593 | * |
|
1594 | * @param updateValues A Criteria object containing values used in |
|
1595 | * set clause. |
|
1596 | * @throws TorqueException Any exceptions caught during processing will be |
|
1597 | * rethrown wrapped into a TorqueException. |
|
1598 | */ |
|
1599 | public static void doUpdate(Criteria updateValues) throws TorqueException |
|
1600 | { |
|
1601 | 0 | Connection con = null; |
1602 | try |
|
1603 | { |
|
1604 | 0 | con = Transaction.beginOptional( |
1605 | updateValues.getDbName(), |
|
1606 | updateValues.isUseTransaction()); |
|
1607 | 0 | doUpdate(updateValues, con); |
1608 | 0 | Transaction.commit(con); |
1609 | } |
|
1610 | 0 | catch (TorqueException e) |
1611 | { |
|
1612 | 0 | Transaction.safeRollback(con); |
1613 | 0 | throw e; |
1614 | 0 | } |
1615 | 0 | } |
1616 | ||
1617 | /** |
|
1618 | * Convenience method used to update rows in the DB. Checks if a |
|
1619 | * <i>single</i> int primary key is specified in the Criteria |
|
1620 | * object and uses it to perform the udpate. If no primary key is |
|
1621 | * specified an Exception will be thrown. |
|
1622 | * <p> |
|
1623 | * Use this method for performing an update of the kind: |
|
1624 | * <p> |
|
1625 | * "WHERE primary_key_id = an int" |
|
1626 | * <p> |
|
1627 | * To perform an update with non-primary key fields in the WHERE |
|
1628 | * clause use doUpdate(criteria, criteria). |
|
1629 | * |
|
1630 | * @param updateValues A Criteria object containing values used in |
|
1631 | * set clause. |
|
1632 | * @param con A Connection. |
|
1633 | * @throws TorqueException Any exceptions caught during processing will be |
|
1634 | * rethrown wrapped into a TorqueException. |
|
1635 | */ |
|
1636 | public static void doUpdate(Criteria updateValues, Connection con) |
|
1637 | throws TorqueException |
|
1638 | { |
|
1639 | 0 | ColumnMap pk = getPrimaryKey(updateValues); |
1640 | 0 | Criteria selectCriteria = null; |
1641 | ||
1642 | 0 | if (pk != null && updateValues.containsKey(pk.getFullyQualclass="keyword">ifiedName())) |
1643 | { |
|
1644 | 0 | selectCriteria = new Criteria(2); |
1645 | 0 | selectCriteria.put(pk.getFullyQualifiedName(), |
1646 | updateValues.remove(pk.getFullyQualifiedName())); |
|
1647 | } |
|
1648 | else |
|
1649 | { |
|
1650 | 0 | throw new TorqueException("No PK specified for database update"); |
1651 | } |
|
1652 | ||
1653 | 0 | doUpdate(selectCriteria, updateValues, con); |
1654 | 0 | } |
1655 | ||
1656 | /** |
|
1657 | * Method used to update rows in the DB. Rows are selected based |
|
1658 | * on selectCriteria and updated using values in updateValues. |
|
1659 | * <p> |
|
1660 | * Use this method for performing an update of the kind: |
|
1661 | * <p> |
|
1662 | * WHERE some_column = some value AND could_have_another_column = |
|
1663 | * another value AND so on... |
|
1664 | * |
|
1665 | * @param selectCriteria A Criteria object containing values used in where |
|
1666 | * clause. |
|
1667 | * @param updateValues A Criteria object containing values used in set |
|
1668 | * clause. |
|
1669 | * @throws TorqueException Any exceptions caught during processing will be |
|
1670 | * rethrown wrapped into a TorqueException. |
|
1671 | */ |
|
1672 | public static void doUpdate(Criteria selectCriteria, Criteria updateValues) |
|
1673 | throws TorqueException |
|
1674 | { |
|
1675 | 0 | Connection con = null; |
1676 | try |
|
1677 | { |
|
1678 | 0 | con = Transaction.beginOptional(selectCriteria.getDbName(), |
1679 | updateValues.isUseTransaction()); |
|
1680 | 0 | doUpdate(selectCriteria, updateValues, con); |
1681 | 0 | Transaction.commit(con); |
1682 | } |
|
1683 | 0 | catch (TorqueException e) |
1684 | { |
|
1685 | 0 | Transaction.safeRollback(con); |
1686 | 0 | throw e; |
1687 | 0 | } |
1688 | 0 | } |
1689 | ||
1690 | /** |
|
1691 | * Method used to update rows in the DB. Rows are selected based |
|
1692 | * on selectCriteria and updated using values in updateValues. |
|
1693 | * <p> |
|
1694 | * Use this method for performing an update of the kind: |
|
1695 | * <p> |
|
1696 | * WHERE some_column = some value AND could_have_another_column = |
|
1697 | * another value AND so on. |
|
1698 | * |
|
1699 | * @param selectCriteria A Criteria object containing values used in where |
|
1700 | * clause. |
|
1701 | * @param updateValues A Criteria object containing values used in set |
|
1702 | * clause. |
|
1703 | * @param con A Connection. |
|
1704 | * @throws TorqueException Any exceptions caught during processing will be |
|
1705 | * rethrown wrapped into a TorqueException. |
|
1706 | */ |
|
1707 | public static void doUpdate( |
|
1708 | Criteria selectCriteria, |
|
1709 | Criteria updateValues, |
|
1710 | Connection con) |
|
1711 | throws TorqueException |
|
1712 | { |
|
1713 | 0 | DB db = Torque.getDB(selectCriteria.getDbName()); |
1714 | 0 | DatabaseMap dbMap = Torque.getDatabaseMap(selectCriteria.getDbName()); |
1715 | ||
1716 | // Set up a list of required tables. StringStack.add() |
|
1717 | // only adds element if it is unique. |
|
1718 | 0 | HashSet tables = new HashSet(); |
1719 | 0 | Iterator it = selectCriteria.keySet().iterator(); |
1720 | 0 | while (it.hasNext()) |
1721 | { |
|
1722 | 0 | tables.add(selectCriteria.getTableName((String) it.next())); |
1723 | } |
|
1724 | ||
1725 | 0 | Iterator tabIt = tables.iterator(); |
1726 | 0 | while (tabIt.hasNext()) |
1727 | { |
|
1728 | 0 | String tab = (String) tabIt.next(); |
1729 | 0 | KeyDef kd = new KeyDef(); |
1730 | 0 | HashSet whereClause = new HashSet(); |
1731 | 0 | DatabaseMap tempDbMap = dbMap; |
1732 | ||
1733 | 0 | ColumnMap[] columnMaps = tempDbMap.getTable(tab).getColumns(); |
1734 | 0 | for (int j = 0; j < columnMaps.length; j++) |
1735 | { |
|
1736 | 0 | ColumnMap colMap = columnMaps[j]; |
1737 | 0 | if (colMap.isPrimaryKey()) |
1738 | { |
|
1739 | 0 | kd.addAttrib(colMap.getColumnName()); |
1740 | } |
|
1741 | 0 | String key = new StringBuffer(colMap.getTableName()) |
1742 | .append('.') |
|
1743 | .append(colMap.getColumnName()) |
|
1744 | .toString(); |
|
1745 | 0 | if (selectCriteria.containsKey(key)) |
1746 | { |
|
1747 | 0 | if (selectCriteria |
1748 | .getComparison(key) |
|
1749 | .equals(Criteria.CUSTOM)) |
|
1750 | { |
|
1751 | 0 | whereClause.add(selectCriteria.getString(key)); |
1752 | } |
|
1753 | else |
|
1754 | { |
|
1755 | 0 | whereClause.add( |
1756 | SqlExpression.build( |
|
1757 | colMap.getColumnName(), |
|
1758 | selectCriteria.getValue(key), |
|
1759 | selectCriteria.getComparison(key), |
|
1760 | selectCriteria.isIgnoreCase(), |
|
1761 | db)); |
|
1762 | } |
|
1763 | } |
|
1764 | } |
|
1765 | 0 | TableDataSet tds = null; |
1766 | try |
|
1767 | { |
|
1768 | // Get affected records. |
|
1769 | 0 | tds = new TableDataSet(con, tab, kd); |
1770 | 0 | String sqlSnippet = StringUtils.join(whereClause.iterator(), " AND "); |
1771 | 0 | if (log.isDebugEnabled()) |
1772 | { |
|
1773 | 0 | log.debug("BasePeer.doUpdate: whereClause=" + sqlSnippet); |
1774 | } |
|
1775 | 0 | tds.where(sqlSnippet); |
1776 | 0 | tds.fetchRecords(); |
1777 | ||
1778 | 0 | if (tds.size() > 1 && selectCriteria.isSingleRecord()) |
1779 | { |
|
1780 | 0 | handleMultipleRecords(tds); |
1781 | } |
|
1782 | 0 | for (int j = 0; j < tds.size(); j++) |
1783 | { |
|
1784 | 0 | Record rec = tds.getRecord(j); |
1785 | 0 | BasePeer.insertOrUpdateRecord(rec, tab, updateValues); |
1786 | } |
|
1787 | 0 | } |
1788 | 0 | catch (Exception e) |
1789 | { |
|
1790 | 0 | throwTorqueException(e); |
1791 | 0 | } |
1792 | finally |
|
1793 | { |
|
1794 | 0 | if (tds != null) |
1795 | { |
|
1796 | try |
|
1797 | { |
|
1798 | 0 | tds.close(); |
1799 | } |
|
1800 | 0 | catch (Exception e) |
1801 | { |
|
1802 | 0 | throwTorqueException(e); |
1803 | 0 | } |
1804 | } |
|
1805 | 0 | } |
1806 | } |
|
1807 | 0 | } |
1808 | ||
1809 | /** |
|
1810 | * Utility method which executes a given sql statement. This |
|
1811 | * method should be used for update, insert, and delete |
|
1812 | * statements. Use executeQuery() for selects. |
|
1813 | * |
|
1814 | * @param stmt A String with the sql statement to execute. |
|
1815 | * @return The number of rows affected. |
|
1816 | * @throws TorqueException Any exceptions caught during processing will be |
|
1817 | * rethrown wrapped into a TorqueException. |
|
1818 | */ |
|
1819 | public static int executeStatement(String stmt) throws TorqueException |
|
1820 | { |
|
1821 | 0 | return executeStatement(stmt, Torque.getDefaultDB()); |
1822 | } |
|
1823 | ||
1824 | /** |
|
1825 | * Utility method which executes a given sql statement. This |
|
1826 | * method should be used for update, insert, and delete |
|
1827 | * statements. Use executeQuery() for selects. |
|
1828 | * |
|
1829 | * @param stmt A String with the sql statement to execute. |
|
1830 | * @param dbName Name of database to connect to. |
|
1831 | * @return The number of rows affected. |
|
1832 | * @throws TorqueException Any exceptions caught during processing will be |
|
1833 | * rethrown wrapped into a TorqueException. |
|
1834 | */ |
|
1835 | public static int executeStatement(String stmt, String dbName) |
|
1836 | throws TorqueException |
|
1837 | { |
|
1838 | 0 | Connection db = null; |
1839 | 0 | int rowCount = -1; |
1840 | try |
|
1841 | { |
|
1842 | 0 | db = Torque.getConnection(dbName); |
1843 | 0 | rowCount = executeStatement(stmt, db); |
1844 | } |
|
1845 | finally |
|
1846 | { |
|
1847 | 0 | Torque.closeConnection(db); |
1848 | 0 | } |
1849 | 0 | return rowCount; |
1850 | } |
|
1851 | ||
1852 | /** |
|
1853 | * Utility method which executes a given sql statement. This |
|
1854 | * method should be used for update, insert, and delete |
|
1855 | * statements. Use executeQuery() for selects. |
|
1856 | * |
|
1857 | * @param stmt A String with the sql statement to execute. |
|
1858 | * @param con A Connection. |
|
1859 | * @return The number of rows affected. |
|
1860 | * @throws TorqueException Any exceptions caught during processing will be |
|
1861 | * rethrown wrapped into a TorqueException. |
|
1862 | */ |
|
1863 | public static int executeStatement(String stmt, Connection con) |
|
1864 | throws TorqueException |
|
1865 | { |
|
1866 | 0 | int rowCount = -1; |
1867 | 0 | Statement statement = null; |
1868 | try |
|
1869 | { |
|
1870 | 0 | statement = con.createStatement(); |
1871 | 0 | rowCount = statement.executeUpdate(stmt); |
1872 | 0 | } |
1873 | 0 | catch (SQLException e) |
1874 | { |
|
1875 | 0 | throw new TorqueException(e); |
1876 | } |
|
1877 | finally |
|
1878 | { |
|
1879 | 0 | if (statement != null) |
1880 | { |
|
1881 | try |
|
1882 | { |
|
1883 | 0 | statement.close(); |
1884 | } |
|
1885 | 0 | catch (SQLException e) |
1886 | { |
|
1887 | 0 | throw new TorqueException(e); |
1888 | 0 | } |
1889 | } |
|
1890 | 0 | } |
1891 | 0 | return rowCount; |
1892 | } |
|
1893 | ||
1894 | /** |
|
1895 | * If the user specified that (s)he only wants to retrieve a |
|
1896 | * single record and multiple records are retrieved, this method |
|
1897 | * is called to handle the situation. The default behavior is to |
|
1898 | * throw an exception, but subclasses can override this method as |
|
1899 | * needed. |
|
1900 | * |
|
1901 | * @param ds The DataSet which contains multiple records. |
|
1902 | * @exception TorqueException Couldn't handle multiple records. |
|
1903 | */ |
|
1904 | protected static void handleMultipleRecords(DataSet ds) |
|
1905 | throws TorqueException |
|
1906 | { |
|
1907 | 0 | throw new TorqueException("Criteria expected single Record and " |
1908 | + "Multiple Records were selected"); |
|
1909 | } |
|
1910 | ||
1911 | /** |
|
1912 | * This method returns the MapBuilder specified in the |
|
1913 | * configuration file. By default, this is |
|
1914 | * org.apache.torque.util.db.map.TurbineMapBuilder. |
|
1915 | * FIXME! With the decoupled Torque there seem to be no |
|
1916 | * default map builder anymore. |
|
1917 | * |
|
1918 | * @return A MapBuilder. |
|
1919 | * @throws TorqueException Any exceptions caught during processing will be |
|
1920 | * rethrown wrapped into a TorqueException. |
|
1921 | * @deprecated you have to specify the name of the map builder! |
|
1922 | */ |
|
1923 | public static MapBuilder getMapBuilder() throws TorqueException |
|
1924 | { |
|
1925 | 0 | return getMapBuilder(DEFAULT_MAP_BUILDER.trim()); |
1926 | } |
|
1927 | ||
1928 | /** |
|
1929 | * This method returns the MapBuilder specified in the name |
|
1930 | * parameter. You should pass in the full path to the class, ie: |
|
1931 | * org.apache.torque.util.db.map.TurbineMapBuilder. The |
|
1932 | * MapBuilder instances are cached in this class for speed. |
|
1933 | * |
|
1934 | * @param name name of the MapBuilder |
|
1935 | * @return A MapBuilder, or null (and logs the error) if the |
|
1936 | * MapBuilder was not found. |
|
1937 | */ |
|
1938 | public static MapBuilder getMapBuilder(String name) |
|
1939 | { |
|
1940 | try |
|
1941 | { |
|
1942 | 0 | MapBuilder mb = (MapBuilder) mapBuilders.get(name); |
1943 | // Use the 'double-check pattern' for syncing |
|
1944 | // caching of the MapBuilder. |
|
1945 | 0 | if (mb == null) |
1946 | { |
|
1947 | 0 | synchronized (mapBuilders) |
1948 | { |
|
1949 | 0 | mb = (MapBuilder) mapBuilders.get(name); |
1950 | 0 | if (mb == null) |
1951 | { |
|
1952 | 0 | mb = (MapBuilder) Class.forName(name).newInstance(); |
1953 | // Cache the MapBuilder before it is built. |
|
1954 | 0 | mapBuilders.put(name, mb); |
1955 | } |
|
1956 | 0 | } |
1957 | } |
|
1958 | ||
1959 | // Build the MapBuilder in its own synchronized block to |
|
1960 | // avoid locking up the whole Hashtable while doing so. |
|
1961 | // Note that *all* threads need to do a sync check on isBuilt() |
|
1962 | // to avoid grabing an uninitialized MapBuilder. This, however, |
|
1963 | // is a relatively fast operation. |
|
1964 | 0 | synchronized (mb) |
1965 | { |
|
1966 | 0 | if (!mb.isBuilt()) |
1967 | { |
|
1968 | try |
|
1969 | { |
|
1970 | 0 | mb.doBuild(); |
1971 | } |
|
1972 | 0 | catch (Exception e) |
1973 | { |
|
1974 | // need to think about whether we'd want to remove |
|
1975 | // the MapBuilder from the cache if it can't be |
|
1976 | // built correctly...? pgo |
|
1977 | 0 | throw e; |
1978 | 0 | } |
1979 | } |
|
1980 | 0 | } |
1981 | 0 | return mb; |
1982 | } |
|
1983 | 0 | catch (Exception e) |
1984 | { |
|
1985 | // Have to catch possible exceptions because method is |
|
1986 | // used in initialization of Peers. Log the exception and |
|
1987 | // return null. |
|
1988 | 0 | String message = |
1989 | "BasePeer.MapBuilder failed trying to instantiate: " + name; |
|
1990 | 0 | log.error(message, e); |
1991 | } |
|
1992 | 0 | return null; |
1993 | } |
|
1994 | ||
1995 | /** |
|
1996 | * Performs a SQL <code>select</code> using a PreparedStatement. |
|
1997 | * Note: this method does not handle null criteria values. |
|
1998 | * |
|
1999 | * @param criteria |
|
2000 | * @param con |
|
2001 | * @return |
|
2002 | * @throws TorqueException Error performing database query. |
|
2003 | */ |
|
2004 | public static List doPSSelect(Criteria criteria, Connection con) |
|
2005 | throws TorqueException |
|
2006 | { |
|
2007 | 0 | List v = null; |
2008 | ||
2009 | 0 | StringBuffer qry = new StringBuffer(); |
2010 | 0 | List params = new ArrayList(criteria.size()); |
2011 | ||
2012 | 0 | createPreparedStatement(criteria, qry, params); |
2013 | ||
2014 | 0 | PreparedStatement stmt = null; |
2015 | try |
|
2016 | { |
|
2017 | 0 | stmt = con.prepareStatement(qry.toString()); |
2018 | ||
2019 | 0 | for (int i = 0; i < params.size(); i++) |
2020 | { |
|
2021 | 0 | Object param = params.get(i); |
2022 | 0 | if (param instanceof java.sql.Date) |
2023 | { |
|
2024 | 0 | stmt.setDate(i + 1, (java.sql.Date) param); |
2025 | } |
|
2026 | 0 | else if (param instanceof NumberKey) |
2027 | { |
|
2028 | 0 | stmt.setBigDecimal(i + 1, |
2029 | ((NumberKey) param).getBigDecimal()); |
|
2030 | } |
|
2031 | else |
|
2032 | { |
|
2033 | 0 | stmt.setString(i + 1, param.toString()); |
2034 | } |
|
2035 | } |
|
2036 | ||
2037 | 0 | QueryDataSet qds = null; |
2038 | try |
|
2039 | { |
|
2040 | 0 | qds = new QueryDataSet(stmt.executeQuery()); |
2041 | 0 | v = getSelectResults(qds); |
2042 | } |
|
2043 | finally |
|
2044 | { |
|
2045 | 0 | if (qds != null) |
2046 | { |
|
2047 | 0 | qds.close(); |
2048 | } |
|
2049 | } |
|
2050 | 0 | } |
2051 | 0 | catch (Exception e) |
2052 | { |
|
2053 | 0 | throwTorqueException(e); |
2054 | 0 | } |
2055 | finally |
|
2056 | { |
|
2057 | 0 | if (stmt != null) |
2058 | { |
|
2059 | try |
|
2060 | { |
|
2061 | 0 | stmt.close(); |
2062 | } |
|
2063 | 0 | catch (SQLException e) |
2064 | { |
|
2065 | 0 | throw new TorqueException(e); |
2066 | 0 | } |
2067 | } |
|
2068 | 0 | } |
2069 | 0 | return v; |
2070 | } |
|
2071 | ||
2072 | /** |
|
2073 | * Do a Prepared Statement select according to the given criteria |
|
2074 | * |
|
2075 | * @param criteria |
|
2076 | * @return |
|
2077 | * @throws TorqueException Any exceptions caught during processing will be |
|
2078 | * rethrown wrapped into a TorqueException. |
|
2079 | */ |
|
2080 | public static List doPSSelect(Criteria criteria) throws TorqueException |
|
2081 | { |
|
2082 | 0 | Connection con = Torque.getConnection(criteria.getDbName()); |
2083 | 0 | List v = null; |
2084 | ||
2085 | try |
|
2086 | { |
|
2087 | 0 | v = doPSSelect(criteria, con); |
2088 | } |
|
2089 | finally |
|
2090 | { |
|
2091 | 0 | Torque.closeConnection(con); |
2092 | 0 | } |
2093 | 0 | return v; |
2094 | } |
|
2095 | ||
2096 | /** |
|
2097 | * Create a new PreparedStatement. It builds a string representation |
|
2098 | * of a query and a list of PreparedStatement parameters. |
|
2099 | * |
|
2100 | * @param criteria |
|
2101 | * @param queryString |
|
2102 | * @param params |
|
2103 | * @throws TorqueException Any exceptions caught during processing will be |
|
2104 | * rethrown wrapped into a TorqueException. |
|
2105 | */ |
|
2106 | public static void createPreparedStatement( |
|
2107 | Criteria criteria, |
|
2108 | StringBuffer queryString, |
|
2109 | List params) |
|
2110 | throws TorqueException |
|
2111 | { |
|
2112 | 0 | DB db = Torque.getDB(criteria.getDbName()); |
2113 | 0 | DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); |
2114 | ||
2115 | 0 | Query query = new Query(); |
2116 | ||
2117 | 0 | UniqueList selectModifiers = query.getSelectModifiers(); |
2118 | 0 | UniqueList selectClause = query.getSelectClause(); |
2119 | 0 | UniqueList fromClause = query.getFromClause(); |
2120 | 0 | UniqueList whereClause = query.getWhereClause(); |
2121 | 0 | UniqueList orderByClause = query.getOrderByClause(); |
2122 | ||
2123 | 0 | UniqueList orderBy = criteria.getOrderByColumns(); |
2124 | 0 | UniqueList select = criteria.getSelectColumns(); |
2125 | 0 | Hashtable aliases = criteria.getAsColumns(); |
2126 | 0 | UniqueList modifiers = criteria.getSelectModifiers(); |
2127 | ||
2128 | 0 | for (int i = 0; i < modifiers.size(); i++) |
2129 | { |
|
2130 | 0 | selectModifiers.add(modifiers.get(i)); |
2131 | } |
|
2132 | ||
2133 | 0 | for (int i = 0; i < select.size(); i++) |
2134 | { |
|
2135 | 0 | String columnName = (String) select.get(i); |
2136 | 0 | if (columnName.indexOf('.') == -1) |
2137 | { |
|
2138 | 0 | throwMalformedColumnNameException("select", columnName); |
2139 | } |
|
2140 | 0 | String tableName = null; |
2141 | 0 | selectClause.add(columnName); |
2142 | 0 | int parenPos = columnName.indexOf('('); |
2143 | 0 | if (parenPos == -1) |
2144 | { |
|
2145 | 0 | tableName = columnName.substring(0, columnName.indexOf('.')); |
2146 | } |
|
2147 | else |
|
2148 | { |
|
2149 | 0 | tableName = |
2150 | columnName.substring(parenPos + 1, columnName.indexOf('.')); |
|
2151 | // functions may contain qualifiers so only take the last |
|
2152 | // word as the table name. |
|
2153 | 0 | int lastSpace = tableName.lastIndexOf(' '); |
2154 | 0 | if (lastSpace != -1) |
2155 | { |
|
2156 | 0 | tableName = tableName.substring(lastSpace + 1); |
2157 | } |
|
2158 | } |
|
2159 | 0 | String tableName2 = criteria.getTableForAlias(tableName); |
2160 | 0 | if (tableName2 != null) |
2161 | { |
|
2162 | 0 | fromClause.add(new StringBuffer(tableName.length() |
2163 | + tableName2.length() + 1) |
|
2164 | .append(tableName2) |
|
2165 | .append(' ') |
|
2166 | .append(tableName) |
|
2167 | .toString()); |
|
2168 | } |
|
2169 | else |
|
2170 | { |
|
2171 | 0 | fromClause.add(tableName); |
2172 | } |
|
2173 | } |
|
2174 | ||
2175 | 0 | Iterator it = aliases.keySet().iterator(); |
2176 | 0 | while (it.hasNext()) |
2177 | { |
|
2178 | 0 | String key = (String) it.next(); |
2179 | 0 | selectClause.add((String) aliases.get(key) + " AS " + key); |
2180 | } |
|
2181 | ||
2182 | 0 | Iterator critKeys = criteria.keySet().iterator(); |
2183 | 0 | while (critKeys.hasNext()) |
2184 | { |
|
2185 | 0 | String key = (String) critKeys.next(); |
2186 | 0 | Criteria.Criterion criterion = criteria.getCriterion(key); |
2187 | 0 | Criteria.Criterion[] someCriteria = |
2188 | criterion.getAttachedCriterion(); |
|
2189 | ||
2190 | 0 | String table = null; |
2191 | 0 | for (int i = 0; i < someCriteria.length; i++) |
2192 | { |
|
2193 | 0 | String tableName = someCriteria[i].getTable(); |
2194 | 0 | table = criteria.getTableForAlias(tableName); |
2195 | 0 | if (table != null) |
2196 | { |
|
2197 | 0 | fromClause.add(new StringBuffer(tableName.length() |
2198 | + table.length() + 1) |
|
2199 | .append(table) |
|
2200 | .append(' ') |
|
2201 | .append(tableName) |
|
2202 | .toString()); |
|
2203 | } |
|
2204 | else |
|
2205 | { |
|
2206 | 0 | fromClause.add(tableName); |
2207 | 0 | table = tableName; |
2208 | } |
|
2209 | ||
2210 | 0 | boolean ignorCase = ((criteria.isIgnoreCase() |
2211 | || someCriteria[i].isIgnoreCase()) |
|
2212 | && (dbMap |
|
2213 | .getTable(table) |
|
2214 | .getColumn(someCriteria[i].getColumn()) |
|
2215 | .getType() |
|
2216 | instanceof String)); |
|
2217 | ||
2218 | 0 | someCriteria[i].setIgnoreCase(ignorCase); |
2219 | } |
|
2220 | ||
2221 | 0 | criterion.setDB(db); |
2222 | 0 | StringBuffer sb = new StringBuffer(); |
2223 | 0 | criterion.appendPsTo(sb, params); |
2224 | 0 | whereClause.add(sb.toString()); |
2225 | } |
|
2226 | ||
2227 | 0 | List join = criteria.getJoinL(); |
2228 | 0 | if (join != null) |
2229 | { |
|
2230 | 0 | for (int i = 0; i < join.size(); i++) |
2231 | { |
|
2232 | 0 | String join1 = (String) join.get(i); |
2233 | 0 | String join2 = (String) criteria.getJoinR().get(i); |
2234 | 0 | if (join1.indexOf('.') == -1) |
2235 | { |
|
2236 | 0 | throwMalformedColumnNameException("join", join1); |
2237 | } |
|
2238 | 0 | if (join2.indexOf('.') == -1) |
2239 | { |
|
2240 | 0 | throwMalformedColumnNameException("join", join2); |
2241 | } |
|
2242 | ||
2243 | 0 | String tableName = join1.substring(0, join1.indexOf('.')); |
2244 | 0 | String table = criteria.getTableForAlias(tableName); |
2245 | 0 | if (table != null) |
2246 | { |
|
2247 | 0 | fromClause.add(new StringBuffer(tableName.length() |
2248 | + table.length() + 1) |
|
2249 | .append(table) |
|
2250 | .append(' ') |
|
2251 | .append(tableName) |
|
2252 | .toString()); |
|
2253 | } |
|
2254 | else |
|
2255 | { |
|
2256 | 0 | fromClause.add(tableName); |
2257 | } |
|
2258 | ||
2259 | 0 | int dot = join2.indexOf('.'); |
2260 | 0 | tableName = join2.substring(0, dot); |
2261 | 0 | table = criteria.getTableForAlias(tableName); |
2262 | 0 | if (table != null) |
2263 | { |
|
2264 | 0 | fromClause.add(new StringBuffer(tableName.length() |
2265 | + table.length() + 1) |
|
2266 | .append(table) |
|
2267 | .append(' ') |
|
2268 | .append(tableName) |
|
2269 | .toString()); |
|
2270 | } |
|
2271 | else |
|
2272 | { |
|
2273 | 0 | fromClause.add(tableName); |
2274 | 0 | table = tableName; |
2275 | } |
|
2276 | ||
2277 | 0 | boolean ignorCase = (criteria.isIgnoreCase() |
2278 | && (dbMap |
|
2279 | .getTable(table) |
|
2280 | .getColumn(join2.substring(dot + 1, join2.length())) |
|
2281 | .getType() |
|
2282 | instanceof String)); |
|
2283 | ||
2284 | 0 | whereClause.add( |
2285 | SqlExpression.buildInnerJoin(join1, join2, ignorCase, db)); |
|
2286 | } |
|
2287 | } |
|
2288 | ||
2289 | 0 | if (orderBy != null && orderBy.size() > 0) |
2290 | { |
|
2291 | // Check for each String/Character column and apply |
|
2292 | // toUpperCase(). |
|
2293 | 0 | for (int i = 0; i < orderBy.size(); i++) |
2294 | { |
|
2295 | 0 | String orderByColumn = (String) orderBy.get(i); |
2296 | 0 | if (orderByColumn.indexOf('.') == -1) |
2297 | { |
|
2298 | 0 | throwMalformedColumnNameException("order by", |
2299 | orderByColumn); |
|
2300 | } |
|
2301 | 0 | String table = |
2302 | orderByColumn.substring(0, orderByColumn.indexOf('.')); |
|
2303 | // See if there's a space (between the column list and sort |
|
2304 | // order in ORDER BY table.column DESC). |
|
2305 | 0 | int spacePos = orderByColumn.indexOf(' '); |
2306 | String columnName; |
|
2307 | 0 | if (spacePos == -1) |
2308 | { |
|
2309 | 0 | columnName = |
2310 | orderByColumn.substring(orderByColumn.indexOf('.') + 1); |
|
2311 | } |
|
2312 | else |
|
2313 | { |
|
2314 | 0 | columnName = orderByColumn.substring( |
2315 | orderByColumn.indexOf('.') + 1, |
|
2316 | spacePos); |
|
2317 | } |
|
2318 | 0 | ColumnMap column = dbMap.getTable(table).getColumn(columnName); |
2319 | 0 | if (column.getType() instanceof String) |
2320 | { |
|
2321 | 0 | if (spacePos == -1) |
2322 | { |
|
2323 | 0 | orderByClause.add( |
2324 | db.ignoreCaseInOrderBy(orderByColumn)); |
|
2325 | } |
|
2326 | else |
|
2327 | { |
|
2328 | 0 | orderByClause.add(db.ignoreCaseInOrderBy( |
2329 | orderByColumn.substring(0, spacePos)) |
|
2330 | + orderByColumn.substring(spacePos)); |
|
2331 | } |
|
2332 | 0 | selectClause.add( |
2333 | db.ignoreCaseInOrderBy(table + '.' + columnName)); |
|
2334 | } |
|
2335 | else |
|
2336 | { |
|
2337 | 0 | orderByClause.add(orderByColumn); |
2338 | } |
|
2339 | } |
|
2340 | } |
|
2341 | ||
2342 | 0 | LimitHelper.buildLimit(criteria, query); |
2343 | 0 | String sql = query.toString(); |
2344 | 0 | log.debug(sql); |
2345 | 0 | queryString.append(sql); |
2346 | 0 | } |
2347 | ||
2348 | /** |
|
2349 | * Throws a TorqueException with the malformed column name error |
|
2350 | * message. The error message looks like this:<p> |
|
2351 | * |
|
2352 | * <code> |
|
2353 | * Malformed column name in Criteria [criteriaPhrase]: |
|
2354 | * '[columnName]' is not of the form 'table.column' |
|
2355 | * </code> |
|
2356 | * |
|
2357 | * @param criteriaPhrase a String, one of "select", "join", or "order by" |
|
2358 | * @param columnName a String containing the offending column name |
|
2359 | * @throws TorqueException Any exceptions caught during processing will be |
|
2360 | * rethrown wrapped into a TorqueException. |
|
2361 | */ |
|
2362 | private static void throwMalformedColumnNameException( |
|
2363 | String criteriaPhrase, |
|
2364 | String columnName) |
|
2365 | throws TorqueException |
|
2366 | { |
|
2367 | 0 | throw new TorqueException("Malformed column name in Criteria " |
2368 | + criteriaPhrase |
|
2369 | + ": '" |
|
2370 | + columnName |
|
2371 | + "' is not of the form 'table.column'"); |
|
2372 | } |
|
2373 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |