1 package com.workingdogs.village;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.ByteArrayOutputStream;
23 import java.io.PrintWriter;
24
25 import java.sql.Connection;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28 import java.sql.Statement;
29
30 import java.util.Vector;
31
32 /***
33 * The DataSet represents a table in the database. It is extended by <a href="QueryDataSet.html">QueryDataSet</a> and <a
34 * href="TableDataSet.html">TableDataSet</a> and should not be used directly. A DataSet contains a <a
35 * href="Schema.html">Schema</a> and potentially a collection of <a href="Record.html">Records</a>.
36 *
37 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
38 * @version $Revision: 568 $
39 */
40 public abstract class DataSet
41 {
42 /*** indicates that all records should be retrieved during a fetch */
43 protected static final int ALL_RECORDS = -1;
44
45 /*** this DataSet's schema object */
46 protected Schema schema;
47
48 /*** this DataSet's collection of Record objects */
49 protected Vector records = null;
50
51 /*** this DataSet's connection object */
52 protected Connection conn;
53
54 /*** have all records been retrieved with the fetchRecords? */
55 private boolean allRecordsRetrieved = false;
56
57 /*** number of records retrieved */
58 private int recordRetrievedCount = 0;
59
60 /*** number of records that were last fetched */
61 private int lastFetchSize = 0;
62
63 /*** the columns in the SELECT statement for this DataSet */
64 private String columns;
65
66 /*** the select string that was used to build this DataSet */
67 protected StringBuffer selectString;
68
69 /*** the KeyDef for this DataSet */
70 private KeyDef keyDefValue;
71
72 /*** the result set for this DataSet */
73 protected ResultSet resultSet;
74
75 /*** the Statement for this DataSet */
76 protected Statement stmt;
77
78 /***
79 * Private, not used
80 *
81 * @exception DataSetException
82 * @exception SQLException
83 */
84 public DataSet()
85 throws DataSetException, SQLException
86 {
87 }
88
89 /***
90 * Create a new DataSet with a connection and a Table name
91 *
92 * @param conn
93 * @param tableName
94 *
95 * @exception DataSetException
96 * @exception SQLException
97 */
98 DataSet(Connection conn, String tableName)
99 throws DataSetException, SQLException
100 {
101 this.conn = conn;
102 this.columns = "*";
103 this.schema = new Schema().schema(conn, tableName);
104 }
105
106 /***
107 * Create a new DataSet with a connection, schema and KeyDef
108 *
109 * @param conn
110 * @param schema
111 * @param keydef
112 *
113 * @exception DataSetException
114 * @exception SQLException
115 */
116 DataSet(Connection conn, Schema schema, KeyDef keydef)
117 throws DataSetException, SQLException
118 {
119 if (conn == null)
120 {
121 throw new SQLException("Database connection could not be established!");
122 }
123 else if (schema == null)
124 {
125 throw new DataSetException("You need to specify a valid schema!");
126 }
127 else if (keydef == null)
128 {
129 throw new DataSetException("You need to specify a valid KeyDef!");
130 }
131
132 this.conn = conn;
133 this.schema = schema;
134 this.columns = "*";
135
136 this.keyDefValue = keydef;
137 }
138
139 /***
140 * Create a new DataSet with a connection, tablename and KeyDef
141 *
142 * @param conn
143 * @param tableName
144 * @param keydef
145 *
146 * @exception SQLException
147 * @exception DataSetException
148 */
149 DataSet(Connection conn, String tableName, KeyDef keydef)
150 throws SQLException, DataSetException
151 {
152 this.conn = conn;
153 this.keyDefValue = keydef;
154 this.columns = "*";
155 this.schema = new Schema().schema(conn, tableName);
156 }
157
158 /***
159 * Create a new DataSet with a connection, tablename and list of columns
160 *
161 * @param conn
162 * @param tableName
163 * @param columns
164 *
165 * @exception SQLException
166 * @exception DataSetException
167 */
168 DataSet(Connection conn, String tableName, String columns)
169 throws SQLException, DataSetException
170 {
171 this.conn = conn;
172 this.columns = columns;
173 this.schema = new Schema().schema(conn, tableName, columns);
174 }
175
176 /***
177 * Create a new DataSet with a connection, tableName, columns and a KeyDef
178 *
179 * @param conn
180 * @param tableName
181 * @param columns
182 * @param keyDef
183 *
184 * @exception SQLException
185 * @exception DataSetException
186 */
187 DataSet(Connection conn, String tableName, String columns, KeyDef keyDef)
188 throws SQLException, DataSetException
189 {
190 this.conn = conn;
191 this.columns = columns;
192 this.keyDefValue = keyDef;
193 this.schema = new Schema().schema(conn, tableName, columns);
194 }
195
196 /***
197 * Gets the ResultSet for this DataSet
198 *
199 * @return the result set for this DataSet
200 *
201 * @exception SQLException
202 * @exception DataSetException
203 */
204 public ResultSet resultSet()
205 throws SQLException, DataSetException
206 {
207 if (this.resultSet == null)
208 {
209 throw new DataSetException("ResultSet is null.");
210 }
211
212 return this.resultSet;
213 }
214
215 /***
216 * Calls addRecord(DataSet)
217 *
218 * @return the added record
219 *
220 * @exception DataSetException
221 * @exception SQLException
222 */
223 public Record addRecord()
224 throws DataSetException, SQLException
225 {
226 return addRecord(this);
227 }
228
229 /***
230 * Creates a new Record within this DataSet
231 *
232 * @param ds
233 *
234 * @return the added record
235 *
236 * @exception DataSetException
237 * @exception SQLException
238 */
239 public Record addRecord(DataSet ds)
240 throws DataSetException, SQLException
241 {
242 if (ds instanceof QueryDataSet)
243 {
244 throw new DataSetException("You cannot add records to a QueryDataSet.");
245 }
246
247 if (records == null)
248 {
249 records = new Vector(10);
250 }
251
252 Record rec = new Record(ds, true);
253 rec.markForInsert();
254 records.addElement(rec);
255
256 return rec;
257 }
258
259 /***
260 * Check if all the records have been retrieve
261 *
262 * @return true if all records have been retrieved
263 */
264 public boolean allRecordsRetrieved()
265 {
266 return this.allRecordsRetrieved;
267 }
268
269 /***
270 * Set all records retrieved
271 *
272 * @param set TODO: DOCUMENT ME!
273 */
274 void setAllRecordsRetrieved(boolean set)
275 {
276 this.allRecordsRetrieved = set;
277 }
278
279 /***
280 * Remove a record from the DataSet's internal storage
281 *
282 * @param rec
283 *
284 * @return the record removed
285 *
286 * @exception DataSetException
287 */
288 public Record removeRecord(Record rec)
289 throws DataSetException
290 {
291 Record removeRec = null;
292
293 try
294 {
295 int loc = this.records.indexOf(rec);
296 removeRec = (Record) this.records.elementAt(loc);
297 this.records.removeElementAt(loc);
298 }
299 catch (Exception e)
300 {
301 throw new DataSetException("Record could not be removed!");
302 }
303
304 return removeRec;
305 }
306
307 /***
308 * Remove all records from the DataSet and nulls those records out and close() the DataSet.
309 *
310 * @return an instance of myself
311 */
312 public DataSet clearRecords()
313 {
314 this.records.removeAllElements();
315 this.records = null;
316
317 return this;
318 }
319
320 /***
321 * Removes the records from the DataSet, but does not null the records out
322 *
323 * @return an instance of myself
324 */
325 public DataSet releaseRecords()
326 {
327 this.records = null;
328 this.recordRetrievedCount = 0;
329 this.lastFetchSize = 0;
330 setAllRecordsRetrieved(false);
331
332 return this;
333 }
334
335 /***
336 * Releases the records, closes the ResultSet and the Statement, and nulls the Schema and Connection references.
337 *
338 * @exception SQLException
339 * @exception DataSetException
340 */
341 public void close()
342 throws SQLException, DataSetException
343 {
344 releaseRecords();
345 this.schema = null;
346
347 if ((this.resultSet != null) && !(this instanceof QueryDataSet))
348 {
349 resultSet().close();
350 }
351
352 this.resultSet = null;
353
354 if (this.stmt != null)
355 {
356 this.stmt.close();
357 }
358
359 this.conn = null;
360 }
361
362 /***
363 * Essentially the same as releaseRecords, but it won't work on a QueryDataSet that has been created with a ResultSet
364 *
365 * @return an instance of myself
366 *
367 * @exception DataSetException
368 * @exception SQLException
369 */
370 public DataSet reset()
371 throws DataSetException, SQLException
372 {
373 if (!((resultSet() != null) && (this instanceof QueryDataSet)))
374 {
375 return releaseRecords();
376 }
377 else
378 {
379 throw new DataSetException("You cannot call reset() on a QueryDataSet.");
380 }
381 }
382
383 /***
384 * Gets the current database connection
385 *
386 * @return a database connection
387 *
388 * @exception SQLException
389 */
390 public Connection connection()
391 throws SQLException
392 {
393 return this.conn;
394 }
395
396 /***
397 * Gets the Schema for this DataSet
398 *
399 * @return the Schema for this DataSet
400 */
401 public Schema schema()
402 {
403 return this.schema;
404 }
405
406 /***
407 * Get Record at 0 based index position
408 *
409 * @param pos
410 *
411 * @return an instance of the found Record
412 *
413 * @exception DataSetException
414 */
415 public Record getRecord(int pos)
416 throws DataSetException
417 {
418 if (containsRecord(pos))
419 {
420 Record rec = (Record) this.records.elementAt(pos);
421
422 if (this instanceof TableDataSet)
423 {
424 rec.markForUpdate();
425 }
426
427 recordRetrievedCount++;
428
429 return rec;
430 }
431
432 throw new DataSetException("Record not found at index: " + pos);
433 }
434
435 /***
436 * Find Record at 0 based index position. This is an internal alternative to getRecord which tries to be smart about the type
437 * of record it is.
438 *
439 * @param pos
440 *
441 * @return an instance of the found Record
442 *
443 * @exception DataSetException
444 */
445 Record findRecord(int pos)
446 throws DataSetException
447 {
448 if (containsRecord(pos))
449 {
450 return (Record) this.records.elementAt(pos);
451 }
452
453 throw new DataSetException("Record not found at index: " + pos);
454 }
455
456 /***
457 * Check to see if the DataSet contains a Record at 0 based position
458 *
459 * @param pos
460 *
461 * @return true if record exists
462 */
463 public boolean containsRecord(int pos)
464 {
465 try
466 {
467 if (this.records.elementAt(pos) != null)
468 {
469 return true;
470 }
471 }
472 catch (Exception e)
473 {
474 return false;
475 }
476
477 return false;
478 }
479
480 /***
481 * Causes the DataSet to hit the database and fetch all the records.
482 *
483 * @return an instance of myself
484 *
485 * @exception SQLException
486 * @exception DataSetException
487 */
488 public DataSet fetchRecords()
489 throws SQLException, DataSetException
490 {
491 return fetchRecords(ALL_RECORDS);
492 }
493
494 /***
495 * Causes the DataSet to hit the database and fetch max records.
496 *
497 * @param max
498 *
499 * @return an instance of myself
500 *
501 * @exception SQLException
502 * @exception DataSetException
503 */
504 public DataSet fetchRecords(int max)
505 throws SQLException, DataSetException
506 {
507 return fetchRecords(0, max);
508 }
509
510 /***
511 * Causes the DataSet to hit the database and fetch max records, starting at start. Record count begins at 0.
512 *
513 * @param start
514 * @param max
515 *
516 * @return an instance of myself
517 *
518 * @exception SQLException
519 * @exception DataSetException
520 */
521 public DataSet fetchRecords(int start, int max)
522 throws SQLException, DataSetException
523 {
524 if (max == 0)
525 {
526 throw new DataSetException("Max is 1 based and must be greater than 0!");
527 }
528 else if ((lastFetchSize() > 0) && (this.records != null))
529 {
530 throw new DataSetException("You must call DataSet.clearRecords() before executing DataSet.fetchRecords() again!");
531 }
532
533 if (selectString == null)
534 {
535 selectString = new StringBuffer(256);
536 selectString.append("SELECT ");
537 selectString.append(schema().attributes());
538 selectString.append(" FROM ");
539 selectString.append(schema().tableName());
540 }
541
542 try
543 {
544 if ((stmt == null) && (this.resultSet == null))
545 {
546 stmt = connection().createStatement();
547 this.resultSet = stmt.executeQuery(selectString.toString());
548 }
549
550 if (this.resultSet != null)
551 {
552 if ((this.records == null) && (max > 0))
553 {
554 this.records = new Vector(max);
555 }
556 else
557 {
558 this.records = new Vector();
559 }
560
561 int startCounter = 0;
562 int fetchCount = 0;
563
564 while (!allRecordsRetrieved())
565 {
566 if (fetchCount == max)
567 {
568 break;
569 }
570
571 if (this.resultSet.next())
572 {
573 if (startCounter >= start)
574 {
575 Record rec = new Record(this);
576 records.addElement(rec);
577 fetchCount++;
578 }
579 else
580 {
581 startCounter++;
582 }
583 }
584 else
585 {
586 setAllRecordsRetrieved(true);
587
588 break;
589 }
590 }
591
592 lastFetchSize = fetchCount;
593 }
594 }
595 catch (SQLException e)
596 {
597 if (stmt != null)
598 {
599 stmt.close();
600 }
601
602 throw new SQLException(e.getMessage());
603 }
604
605 return this;
606 }
607
608 /***
609 * The number of records that were fetched with the last fetchRecords.
610 *
611 * @return int
612 */
613 public int lastFetchSize()
614 {
615 return lastFetchSize;
616 }
617
618 /***
619 * gets the KeyDef object for this DataSet
620 *
621 * @return the keydef for this DataSet, this value can be null
622 */
623 public KeyDef keydef()
624 {
625 return this.keyDefValue;
626 }
627
628 /***
629 * This returns a represention of this DataSet
630 *
631 * @return TODO: DOCUMENT ME!
632 */
633 public String toString()
634 {
635 try
636 {
637 ByteArrayOutputStream bout = new ByteArrayOutputStream();
638 PrintWriter out = new PrintWriter(bout);
639
640 if (schema != null)
641 {
642 out.println(schema.toString());
643 }
644
645 for (int i = 0; i < size(); i++)
646 {
647 out.println(findRecord(i));
648 }
649
650 out.flush();
651
652 return bout.toString();
653 }
654 catch (DataSetException e)
655 {
656 return "{}";
657 }
658 }
659
660 /***
661 * Gets the tableName defined in the schema
662 *
663 * @return string
664 *
665 * @throws DataSetException TODO: DOCUMENT ME!
666 */
667 public String tableName()
668 throws DataSetException
669 {
670 return schema().tableName();
671 }
672
673 /***
674 * Calculates the maxColumnWidths for the column in a DataSet I really don't know what this is used for so it isn't
675 * implemented.
676 *
677 * @param with_heading
678 *
679 * @return int
680 *
681 * @exception DataSetException
682 * @exception SQLException
683 */
684 public int [] maxColumnWidths(boolean with_heading)
685 throws DataSetException, SQLException
686 {
687 if (schema() == null)
688 {
689 throw new DataSetException("Schema is null!");
690 }
691
692 throw new DataSetException("Not yet implemented!");
693 }
694
695 /***
696 * Classes extending this class must implement this method.
697 *
698 * @return the select string
699 *
700 * @throws DataSetException TODO: DOCUMENT ME!
701 */
702 public abstract String getSelectString()
703 throws DataSetException;
704
705 /***
706 * Returns the columns attribute for the DataSet
707 *
708 * @return the columns attribute for the DataSet
709 */
710 String getColumns()
711 {
712 return this.columns;
713 }
714
715 /***
716 * Gets the number of Records in this DataSet. It is 0 based.
717 *
718 * @return number of Records in this DataSet
719 */
720 public int size()
721 {
722 if (this.records == null)
723 {
724 return 0;
725 }
726
727 return this.records.size();
728 }
729 }