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