View Javadoc

1   /*
2    * The Working-Dogs.com License, Version 1.1
3    *
4    * Copyright (c) 1999 Working-Dogs.com.  All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met:
9    *
10   * 1. Redistributions of source code must retain the above copyright
11   *    notice, this list of conditions and the following disclaimer.
12   *
13   * 2. Redistributions in binary form must reproduce the above copyright
14   *    notice, this list of conditions and the following disclaimer in
15   *    the documentation and/or other materials provided with the
16   *    distribution.
17   *
18   * 3. The end-user documentation included with the redistribution, if
19   *    any, must include the following acknowlegement:
20   *       "This product includes software developed by the
21   *        Working-Dogs.com <http://www.Working-Dogs.com/>."
22   *    Alternately, this acknowlegement may appear in the software itself,
23   *    if and wherever such third-party acknowlegements normally appear.
24   *
25   * 4. The names "Working-Dogs.com" and "Village" must not be used to
26   *    endorse or promote products derived from this software without
27   *    prior written permission. For written permission, please contact
28   *    jon@working-dogs.com.
29   *
30   * 5. Products derived from this software may not be called
31   *    "Working-Dogs.com" nor may "Village" appear in their names
32   *    without prior written permission of Working-Dogs.com.
33   *
34   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
38   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
39   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
40   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
41   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
42   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
43   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
44   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45   * SUCH DAMAGE.
46   * ====================================================================
47   *
48   * This software consists of voluntary contributions made by many
49   * individuals on behalf of the Working-Dogs.com.  For more
50   * information on the Working-Dogs.com, please see
51   * <http://www.Working-Dogs.com/>.
52   */
53  package com.workingdogs.village;
54  
55  import java.sql.Connection;
56  import java.sql.ResultSet;
57  import java.sql.SQLException;
58  
59  import java.util.Enumeration;
60  
61  /***
62   * This class is used for doing select/insert/delete/update on the database. A TableDataSet cannot be used to join multiple tables
63   * for an update, if you need join functionality on a select, you should use a <a href="QueryDataSet.html">QueryDataSet</a>.
64   *
65   * <P>
66   * Here is an example usage for this code that gets the first 10 records where column "a" = 1:
67   * <PRE>
68   *  KeyDef kd = new KeyDef().setAttrib("column");
69   *  TableDataSet tds = new TableDataSet(connection, "table_name", kd );
70   *  tds.where ("a=1" ); // WHERE a = 1
71   *  tds.fetchRecords(10); // fetch first 10 records where column a=1
72   *  for ( int i=0;i< tds.size(); i++ )
73   *  {
74   *  Record rec = tds.getRecord(i); // zero based
75   *  String columnA = rec.getValue("a");
76   *  if ( columnA.equals ("1") )
77   *  System.out.print ("We got a column!");
78   *  }
79   *  tds.close();
80   *  </PRE>
81   * </p>
82   *
83   * <P>
84   * It is important to remember to always close() the TableDataSet when you are finished with it.
85   * </p>
86   *
87   * <P>
88   * As you can see, using a TableDataSet makes doing selects from the database trivial. You do not need to write any SQL and it
89   * makes it easy to cache a TableDataSet for future use within your application.
90   * </p>
91   *
92   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
93   * @version $Revision: 568 $
94   */
95  public class TableDataSet
96          extends DataSet
97  {
98      /*** the optimistic locking column value */
99      private String optimisticLockingCol;
100 
101     /*** the value for the sql where clause */
102     private String where = null;
103 
104     /*** the value for the sql order by clause */
105     private String order = null;
106 
107     /*** the value for the sql other clause */
108     private String other = null;
109 
110     // by default this is false;
111 
112     /*** TODO: DOCUMENT ME! */
113     private boolean refreshOnSave = false;
114 
115     /***
116      * Default constructor.
117      *
118      * @exception SQLException
119      * @exception DataSetException
120      */
121     public TableDataSet()
122             throws SQLException, DataSetException
123     {
124         super();
125     }
126 
127     /***
128      * Creates a new TableDataSet object.
129      *
130      * @param conn TODO: DOCUMENT ME!
131      * @param tableName TODO: DOCUMENT ME!
132      *
133      * @throws SQLException TODO: DOCUMENT ME!
134      * @throws DataSetException TODO: DOCUMENT ME!
135      */
136     public TableDataSet(Connection conn, String tableName)
137             throws SQLException, DataSetException
138     {
139         super(conn, tableName);
140     }
141 
142     /***
143      * Creates a new TableDataSet object.
144      *
145      * @param conn TODO: DOCUMENT ME!
146      * @param schema TODO: DOCUMENT ME!
147      * @param keydef TODO: DOCUMENT ME!
148      *
149      * @throws SQLException TODO: DOCUMENT ME!
150      * @throws DataSetException TODO: DOCUMENT ME!
151      */
152     public TableDataSet(Connection conn, Schema schema, KeyDef keydef)
153             throws SQLException, DataSetException
154     {
155         super(conn, schema, keydef);
156     }
157 
158     /***
159      * Creates a new TableDataSet object.
160      *
161      * @param conn TODO: DOCUMENT ME!
162      * @param tableName TODO: DOCUMENT ME!
163      * @param keydef TODO: DOCUMENT ME!
164      *
165      * @throws SQLException TODO: DOCUMENT ME!
166      * @throws DataSetException TODO: DOCUMENT ME!
167      */
168     public TableDataSet(Connection conn, String tableName, KeyDef keydef)
169             throws SQLException, DataSetException
170     {
171         super(conn, tableName, keydef);
172     }
173 
174     /***
175      * Creates a new TableDataSet object.
176      *
177      * @param conn TODO: DOCUMENT ME!
178      * @param tableName TODO: DOCUMENT ME!
179      * @param columns TODO: DOCUMENT ME!
180      *
181      * @throws SQLException TODO: DOCUMENT ME!
182      * @throws DataSetException TODO: DOCUMENT ME!
183      */
184     public TableDataSet(Connection conn, String tableName, String columns)
185             throws SQLException, DataSetException
186     {
187         super(conn, tableName, columns);
188     }
189 
190     /***
191      * Creates a new TableDataSet object.
192      *
193      * @param conn TODO: DOCUMENT ME!
194      * @param tableName TODO: DOCUMENT ME!
195      * @param columns TODO: DOCUMENT ME!
196      * @param keydef TODO: DOCUMENT ME!
197      *
198      * @throws SQLException TODO: DOCUMENT ME!
199      * @throws DataSetException TODO: DOCUMENT ME!
200      */
201     public TableDataSet(Connection conn, String tableName, String columns, KeyDef keydef)
202             throws SQLException, DataSetException
203     {
204         super(conn, tableName, columns, keydef);
205     }
206 
207     /***
208      * Use the TDS fetchRecords instead of the DataSet.fetchRecords
209      *
210      * @return an instance of myself
211      *
212      * @exception SQLException
213      * @exception DataSetException
214      */
215     public DataSet fetchRecords()
216             throws SQLException, DataSetException
217     {
218         return fetchRecords(-1);
219     }
220 
221     /***
222      * Use the TDS fetchRecords instead of the DataSet.fetchRecords
223      *
224      * @param max
225      *
226      * @return an instance of myself
227      *
228      * @exception SQLException
229      * @exception DataSetException
230      */
231     public DataSet fetchRecords(int max)
232             throws SQLException, DataSetException
233     {
234         return fetchRecords(0, max);
235     }
236 
237     /***
238      * Fetch start to max records. start is at Record 0
239      *
240      * @param start
241      * @param max
242      *
243      * @return an instance of myself
244      *
245      * @exception SQLException
246      * @exception DataSetException
247      */
248     public DataSet fetchRecords(int start, int max)
249             throws SQLException, DataSetException
250     {
251         buildSelectString();
252 
253         return super.fetchRecords(start, max);
254     }
255 
256     /***
257      * this is a string that contains the columns for the table that this TableDataSet represents.
258      *
259      * @return columns separated by ","
260      */
261     public String attributes()
262     {
263         return super.getColumns();
264     }
265 
266     /***
267      * Returns the KeyDef for the DataSet
268      *
269      * @return a keydef
270      */
271     public KeyDef keydef()
272     {
273         return super.keydef();
274     }
275 
276     /***
277      * Returns the ResultSet for the DataSet
278      *
279      * @return a ResultSet
280      *
281      * @throws SQLException TODO: DOCUMENT ME!
282      * @throws DataSetException TODO: DOCUMENT ME!
283      */
284     public ResultSet resultSet()
285             throws SQLException, DataSetException
286     {
287         return super.resultSet();
288     }
289 
290     /***
291      * Returns the Schema for the DataSet
292      *
293      * @return a Schema
294      */
295     public Schema schema()
296     {
297         return super.schema();
298     }
299 
300     /***
301      * Saves all the records in the DataSet.
302      *
303      * @return total number of records updated/inserted/deleted
304      *
305      * @throws SQLException TODO: DOCUMENT ME!
306      * @throws DataSetException TODO: DOCUMENT ME!
307      */
308     public int save()
309             throws SQLException, DataSetException
310     {
311         return save(connection(), false);
312     }
313 
314     /***
315      * Saves all the records in the DataSet with the intransaction boolean value.
316      *
317      * @param intransaction TODO: DOCUMENT ME!
318      *
319      * @return total number of records updated/inserted/deleted
320      *
321      * @throws SQLException TODO: DOCUMENT ME!
322      * @throws DataSetException TODO: DOCUMENT ME!
323      */
324     public int save(boolean intransaction)
325             throws SQLException, DataSetException
326     {
327         return save(connection(), intransaction);
328     }
329 
330     /***
331      * Saves all the records in the DataSet with the given connection and intransaction boolean value.
332      *
333      * @param conn TODO: DOCUMENT ME!
334      * @param intransaction TODO: DOCUMENT ME!
335      *
336      * @return total number of records updated/inserted/deleted
337      *
338      * @throws SQLException TODO: DOCUMENT ME!
339      * @throws DataSetException TODO: DOCUMENT ME!
340      */
341     public int save(Connection conn, boolean intransaction)
342             throws SQLException, DataSetException
343     {
344         int j = 0;
345 
346         for (Enumeration e = records.elements(); e.hasMoreElements();)
347         {
348             Record rec = (Record) e.nextElement();
349             rec.save(conn);
350             j++;
351         }
352 
353         // now go through and remove any records
354         // that were previously marked as a zombie by the
355         // delete process
356         removeDeletedRecords();
357 
358         return j;
359     }
360 
361     /***
362      * Not yet implemented
363      *
364      * @param conn TODO: DOCUMENT ME!
365      *
366      * @return TODO: DOCUMENT ME!
367      *
368      * @throws SQLException TODO: DOCUMENT ME!
369      * @throws DataSetException TODO: DOCUMENT ME!
370      */
371     public int saveWithoutStatusUpdate(Connection conn)
372             throws SQLException, DataSetException
373     {
374         throw new DataSetException("Not yet implemented!");
375     }
376 
377     /***
378      * Hell if I know what this does.
379      *
380      * @return TODO: DOCUMENT ME!
381      */
382     public String debugInfo()
383     {
384         return "Not yet implemented!";
385     }
386 
387     /***
388      * Removes any records that are marked as a zombie.
389      *
390      * @throws DataSetException TODO: DOCUMENT ME!
391      */
392     public void removeDeletedRecords()
393             throws DataSetException
394     {
395         for (Enumeration e = records.elements(); e.hasMoreElements();)
396         {
397             Record rec = (Record) e.nextElement();
398 
399             if (rec.isAZombie())
400             {
401                 removeRecord(rec);
402             }
403         }
404     }
405 
406     /***
407      * Sets the table column used for optomistic locking.
408      *
409      * @param olc TODO: DOCUMENT ME!
410      */
411     public void setOptimisticLockingColumn(String olc)
412     {
413         this.optimisticLockingCol = olc;
414     }
415 
416     /***
417      * Gets the table column used for optomistic locking.
418      *
419      * @return string
420      */
421     public String optimisticLockingCol()
422     {
423         return this.optimisticLockingCol;
424     }
425 
426     /***
427      * Sets the value for the SQL portion of the WHERE statement
428      *
429      * @param where TODO: DOCUMENT ME!
430      *
431      * @return instance of self
432      *
433      * @throws DataSetException TODO: DOCUMENT ME!
434      */
435     public TableDataSet where(String where)
436             throws DataSetException
437     {
438         if (where == null)
439         {
440             throw new DataSetException("null not allowed for where clause");
441         }
442 
443         this.where = where;
444 
445         return this;
446     }
447 
448     /***
449      * Gets the value of the SQL portion of WHERE.
450      *
451      * @return string
452      */
453     String getWhere()
454     {
455         return this.where;
456     }
457 
458     /***
459      * Sets the value for the SQL portion of the ORDER statement
460      *
461      * @param order TODO: DOCUMENT ME!
462      *
463      * @return instance of self
464      *
465      * @throws DataSetException TODO: DOCUMENT ME!
466      */
467     public TableDataSet order(String order)
468             throws DataSetException
469     {
470         if (order == null)
471         {
472             throw new DataSetException("null not allowed for order clause");
473         }
474 
475         this.order = order;
476 
477         return this;
478     }
479 
480     /***
481      * Gets the value of the SQL portion of ORDER.
482      *
483      * @return string
484      */
485     String getOrder()
486     {
487         return this.order;
488     }
489 
490     /***
491      * Sets the value for the SQL portion of the OTHER statement
492      *
493      * @param other TODO: DOCUMENT ME!
494      *
495      * @return instance of self
496      *
497      * @throws DataSetException TODO: DOCUMENT ME!
498      */
499     public TableDataSet other(String other)
500             throws DataSetException
501     {
502         if (other == null)
503         {
504             throw new DataSetException("null not allowed for other clause");
505         }
506 
507         this.other = other;
508 
509         return this;
510     }
511 
512     /***
513      * Gets the value of the SQL portion of OTHER.
514      *
515      * @return string
516      */
517     String getOther()
518     {
519         return this.other;
520     }
521 
522     /***
523      * This method refreshes all of the Records stored in this TableDataSet.
524      *
525      * @param conn TODO: DOCUMENT ME!
526      *
527      * @throws SQLException TODO: DOCUMENT ME!
528      * @throws DataSetException TODO: DOCUMENT ME!
529      */
530     public void refresh(Connection conn)
531             throws SQLException, DataSetException
532     {
533         for (Enumeration e = records.elements(); e.hasMoreElements();)
534         {
535             Record rec = (Record) e.nextElement();
536             rec.refresh(conn);
537         }
538     }
539 
540     /***
541      * Setting this causes each Record to refresh itself when a save() is performed on it.
542      *
543      * <P>
544      * Default value is false.
545      * </p>
546      *
547      * @param val TODO: DOCUMENT ME!
548      */
549     public void setRefreshOnSave(boolean val)
550     {
551         this.refreshOnSave = val;
552     }
553 
554     /***
555      * Setting this causes each Record to refresh itself when a save() is performed on it.
556      *
557      * <P>
558      * Default value is false.
559      * </p>
560      *
561      * @return true if it is on; false otherwise
562      */
563     public boolean refreshOnSave()
564     {
565         return this.refreshOnSave;
566     }
567 
568     /***
569      * This sets additional SQL for the table name. The string appears after the table name. Sybase users would set this to
570      * "HOLDLOCK" to get repeatable reads.
571      *
572      * <P>
573      * FIXME: Is this right? I don't use Sybase.
574      * </p>
575      *
576      * @param tq TODO: DOCUMENT ME!
577      *
578      * @return an instance of self
579      */
580     public TableDataSet tableQualifier(String tq)
581     {
582         // go directly to schema() cause it is where tableName is stored
583         schema().appendTableName(tq);
584 
585         return this;
586     }
587 
588     /***
589      * The name of the table for which this TableDataSet was created.
590      *
591      * @return string
592      *
593      * @throws DataSetException TODO: DOCUMENT ME!
594      */
595     public String tableName()
596             throws DataSetException
597     {
598         return super.tableName();
599     }
600 
601     /***
602      * Not yet implemented
603      *
604      * @exception SQLException
605      * @exception DataSetException
606      */
607     public void updateStatus()
608             throws SQLException, DataSetException
609     {
610         throw new DataSetException("Not yet implemented!");
611     }
612 
613     /***
614      * Builds the select string that was used to populate this TableDataSet.
615      *
616      * @return SQL select string
617      *
618      * @throws DataSetException TODO: DOCUMENT ME!
619      */
620     public String getSelectString()
621             throws DataSetException
622     {
623         buildSelectString();
624 
625         return this.selectString.toString();
626     }
627 
628     /***
629      * Used by getSelectString to build the select string that was used to populate this TableDataSet.
630      *
631      * @throws DataSetException TODO: DOCUMENT ME!
632      */
633     private void buildSelectString()
634             throws DataSetException
635     {
636         if (selectString == null)
637         {
638             selectString = new StringBuffer(256);
639         }
640         else
641         {
642             selectString.setLength(0);
643         }
644 
645         selectString.append("SELECT ");
646         selectString.append(schema().attributes());
647         selectString.append(" FROM ");
648         selectString.append(schema().tableName());
649 
650         if ((this.where != null) && (this.where.length() > 0))
651         {
652             selectString.append(" WHERE " + this.where);
653         }
654 
655         if ((this.order != null) && (this.order.length() > 0))
656         {
657             selectString.append(" ORDER BY " + this.order);
658         }
659 
660         if ((this.other != null) && (this.other.length() > 0))
661         {
662             selectString.append(this.other);
663         }
664     }
665 }