some small changes delting unneeded imports. two new exceptions in mir.storage. usage...
[mir.git] / source / mir / storage / Database.java
1 /*\r
2  * Copyright (C) 2001, 2002  The Mir-coders group\r
3  *\r
4  * This file is part of Mir.\r
5  *\r
6  * Mir is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * Mir is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with Mir; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  *\r
20  * In addition, as a special exception, The Mir-coders gives permission to link\r
21  * the code of this program with the com.oreilly.servlet library, any library\r
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced\r
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of\r
24  * the above that use the same license as the above), and distribute linked\r
25  * combinations including the two.  You must obey the GNU General Public\r
26  * License in all respects for all of the code used other than the above\r
27  * mentioned libraries.  If you modify this file, you may extend this exception\r
28  * to your version of the file, but you are not obligated to do so.  If you do\r
29  * not wish to do so, delete this exception statement from your version.\r
30  */\r
31 \r
32 package mir.storage;\r
33 \r
34 import java.io.IOException;\r
35 import java.io.InputStreamReader;\r
36 import java.sql.Connection;\r
37 import java.sql.PreparedStatement;\r
38 import java.sql.ResultSet;\r
39 import java.sql.ResultSetMetaData;\r
40 import java.sql.SQLException;\r
41 import java.sql.Statement;\r
42 import java.sql.Timestamp;\r
43 import java.text.ParseException;\r
44 import java.text.SimpleDateFormat;\r
45 import java.util.ArrayList;\r
46 import java.util.Calendar;\r
47 import java.util.GregorianCalendar;\r
48 import java.util.HashMap;\r
49 \r
50 import mir.config.MirPropertiesConfiguration;\r
51 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;\r
52 import mir.entity.Entity;\r
53 import mir.entity.EntityList;\r
54 import mir.entity.StorableObjectEntity;\r
55 import mir.misc.HTMLTemplateProcessor;\r
56 import mir.misc.Logfile;\r
57 import mir.misc.StringUtil;\r
58 import mir.storage.store.ObjectStore;\r
59 import mir.storage.store.StorableObject;\r
60 import mir.storage.store.StoreContainerType;\r
61 import mir.storage.store.StoreIdentifier;\r
62 import mir.storage.store.StoreUtil;\r
63 import mir.util.JDBCStringRoutines;\r
64 \r
65 import com.codestudio.util.SQLManager;\r
66 \r
67 import freemarker.template.SimpleHash;\r
68 import freemarker.template.SimpleList;\r
69 \r
70 \r
71 /**\r
72  * Diese Klasse implementiert die Zugriffsschicht auf die Datenbank.\r
73  * Alle Projektspezifischen Datenbankklassen erben von dieser Klasse.\r
74  * In den Unterklassen wird im Minimalfall nur die Tabelle angegeben.\r
75  * Im Konfigurationsfile findet sich eine Verweis auf den verwendeten\r
76  * Treiber, Host, User und Passwort, ueber den der Zugriff auf die\r
77  * Datenbank erfolgt.\r
78  *\r
79  * @version $Id: Database.java,v 1.31 2003/01/25 17:45:19 idfx Exp $\r
80  * @author rk\r
81  *\r
82  */\r
83 public class Database implements StorageObject {\r
84 \r
85   protected MirPropertiesConfiguration configuration;\r
86   protected String                    theTable;\r
87   protected String                    theCoreTable=null;\r
88   protected String                    thePKeyName="id";\r
89   protected int                       thePKeyType, thePKeyIndex;\r
90   protected boolean                   evaluatedMetaData=false;\r
91   protected ArrayList                 metadataFields,metadataLabels,\r
92   metadataNotNullFields;\r
93   protected int[]                     metadataTypes;\r
94   protected Class                     theEntityClass;\r
95   protected StorageObject             myselfDatabase;\r
96   protected SimpleList                popupCache=null;\r
97   protected boolean                   hasPopupCache = false;\r
98   protected SimpleHash                hashCache=null;\r
99   protected boolean                   hasTimestamp=true;\r
100   private String                      database_driver, database_url;\r
101   private int                         defaultLimit;\r
102   protected DatabaseAdaptor           theAdaptor;\r
103   protected Logfile                   theLog;\r
104   private static Class                GENERIC_ENTITY_CLASS=null,\r
105   STORABLE_OBJECT_ENTITY_CLASS=null;\r
106   private static SimpleHash           POPUP_EMTYLINE=new SimpleHash();\r
107   protected static final ObjectStore  o_store=ObjectStore.getInstance();\r
108   private SimpleDateFormat _dateFormatterOut =\r
109       new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");\r
110   private SimpleDateFormat _dateFormatterIn =\r
111       new SimpleDateFormat("yyyy-MM-dd HH:mm");\r
112   private Calendar _cal = new GregorianCalendar();\r
113 \r
114   private static final int _millisPerHour = 60 * 60 * 1000;\r
115   private static final int _millisPerMinute = 60 * 1000;\r
116 \r
117   static {\r
118     // always same object saves a little space\r
119     POPUP_EMTYLINE.put("key", ""); POPUP_EMTYLINE.put("value", "--");\r
120     try {\r
121       GENERIC_ENTITY_CLASS = Class.forName("mir.entity.StorableObjectEntity");\r
122       STORABLE_OBJECT_ENTITY_CLASS = Class.forName("mir.entity.StorableObjectEntity");\r
123     }\r
124     catch (Exception e) {\r
125       System.err.println("FATAL: Database.java could not initialize" + e.getMessage());\r
126     }\r
127   }\r
128 \r
129 \r
130   /**\r
131    * Kontruktor bekommt den Filenamen des Konfigurationsfiles ?bergeben.\r
132    * Aus diesem file werden <code>Database.Logfile</code>,\r
133    * <code>Database.Username</code>,<code>Database.Password</code>,\r
134    * <code>Database.Host</code> und <code>Database.Adaptor</code>\r
135    * ausgelesen und ein Broker f?r die Verbindugen zur Datenbank\r
136    * erzeugt.\r
137    *\r
138    * @param   String confFilename Dateiname der Konfigurationsdatei\r
139    */\r
140   public Database() throws StorageObjectFailure {\r
141     try {\r
142       configuration = MirPropertiesConfiguration.instance();\r
143     } catch (PropertiesConfigExc e) {\r
144       throw new StorageObjectFailure(e);\r
145     }\r
146     theLog = Logfile.getInstance(configuration.getStringWithHome("Database.Logfile"));\r
147     String theAdaptorName=configuration.getString("Database.Adaptor");\r
148     defaultLimit = Integer.parseInt(configuration.getString("Database.Limit"));\r
149     try {\r
150       theEntityClass = GENERIC_ENTITY_CLASS;\r
151       theAdaptor = (DatabaseAdaptor)Class.forName(theAdaptorName).newInstance();\r
152     } catch (Exception e){\r
153       theLog.printError("Error in Database() constructor with "+\r
154                         theAdaptorName + " -- " +e.getMessage());\r
155       throw new StorageObjectFailure("Error in Database() constructor.",e);\r
156     }\r
157   }\r
158 \r
159   /**\r
160    * Liefert die Entity-Klasse zur?ck, in der eine Datenbankzeile gewrappt\r
161    * wird. Wird die Entity-Klasse durch die erbende Klasse nicht ?berschrieben,\r
162    * wird eine mir.entity.GenericEntity erzeugt.\r
163    *\r
164    * @return Class-Objekt der Entity\r
165    */\r
166   public java.lang.Class getEntityClass () {\r
167     return  theEntityClass;\r
168   }\r
169 \r
170   /**\r
171    * Liefert die Standardbeschr?nkung von select-Statements zur?ck, also\r
172    * wieviel Datens?tze per Default selektiert werden.\r
173    *\r
174    * @return Standard-Anzahl der Datens?tze\r
175    */\r
176   public int getLimit () {\r
177     return  defaultLimit;\r
178   }\r
179 \r
180   /**\r
181    * Liefert den Namen des Primary-Keys zur?ck. Wird die Variable nicht von\r
182    * der erbenden Klasse ?berschrieben, so ist der Wert <code>PKEY</code>\r
183    * @return Name des Primary-Keys\r
184    */\r
185   public String getIdName () {\r
186     return  thePKeyName;\r
187   }\r
188 \r
189   /**\r
190    * Liefert den Namen der Tabelle, auf das sich das Datenbankobjekt bezieht.\r
191    *\r
192    * @return Name der Tabelle\r
193    */\r
194   public String getTableName () {\r
195     return  theTable;\r
196   }\r
197 \r
198       /*\r
199   *   Dient dazu vererbte Tabellen bei objectrelationalen DBMS\r
200   *   zu speichern, wenn die id einer Tabelle in der parenttabelle verwaltet\r
201   *   wird.\r
202   *   @return liefert theCoreTabel als String zurueck, wenn gesetzt, sonst\r
203   *    the Table\r
204        */\r
205 \r
206   public String getCoreTable(){\r
207     if (theCoreTable!=null) return theCoreTable;\r
208     else return theTable;\r
209   }\r
210 \r
211   /**\r
212    * Liefert Feldtypen der Felder der Tabelle zurueck (s.a. java.sql.Types)\r
213    * @return int-Array mit den Typen der Felder\r
214    * @exception StorageObjectException\r
215    */\r
216   public int[] getTypes () throws StorageObjectFailure {\r
217     if (metadataTypes == null)\r
218       get_meta_data();\r
219     return  metadataTypes;\r
220   }\r
221 \r
222   /**\r
223    * Liefert eine Liste der Labels der Tabellenfelder\r
224    * @return ArrayListe mit Labeln\r
225    * @exception StorageObjectException\r
226    */\r
227   public ArrayList getLabels () throws StorageObjectFailure {\r
228     if (metadataLabels == null)\r
229       get_meta_data();\r
230     return  metadataLabels;\r
231   }\r
232 \r
233   /**\r
234    * Liefert eine Liste der Felder der Tabelle\r
235    * @return ArrayList mit Feldern\r
236    * @exception StorageObjectException\r
237    */\r
238   public ArrayList getFields () throws StorageObjectFailure {\r
239     if (metadataFields == null)\r
240       get_meta_data();\r
241     return  metadataFields;\r
242   }\r
243 \r
244 \r
245       /*\r
246   *   Gets value out of ResultSet according to type and converts to String\r
247   *   @param inValue  Wert aus ResultSet.\r
248   *   @param aType  Datenbanktyp.\r
249   *   @return liefert den Wert als String zurueck. Wenn keine Umwandlung moeglich\r
250   *           dann /unsupported value/\r
251        */\r
252   private String getValueAsString (ResultSet rs, int valueIndex, int aType) \r
253         throws StorageObjectFailure {\r
254     String outValue = null;\r
255     if (rs != null) {\r
256       try {\r
257         switch (aType) {\r
258           case java.sql.Types.BIT:\r
259             outValue = (rs.getBoolean(valueIndex) == true) ? "1" : "0";\r
260             break;\r
261           case java.sql.Types.INTEGER:case java.sql.Types.SMALLINT:case java.sql.Types.TINYINT:case java.sql.Types.BIGINT:\r
262             int out = rs.getInt(valueIndex);\r
263             if (!rs.wasNull())\r
264               outValue = new Integer(out).toString();\r
265             break;\r
266           case java.sql.Types.NUMERIC:\r
267             /** @todo Numeric can be float or double depending upon\r
268              *  metadata.getScale() / especially with oracle */\r
269             long outl = rs.getLong(valueIndex);\r
270             if (!rs.wasNull())\r
271               outValue = new Long(outl).toString();\r
272             break;\r
273           case java.sql.Types.REAL:\r
274             float tempf = rs.getFloat(valueIndex);\r
275             if (!rs.wasNull()) {\r
276               tempf *= 10;\r
277               tempf += 0.5;\r
278               int tempf_int = (int)tempf;\r
279               tempf = (float)tempf_int;\r
280               tempf /= 10;\r
281               outValue = "" + tempf;\r
282               outValue = outValue.replace('.', ',');\r
283             }\r
284             break;\r
285           case java.sql.Types.DOUBLE:\r
286             double tempd = rs.getDouble(valueIndex);\r
287             if (!rs.wasNull()) {\r
288               tempd *= 10;\r
289               tempd += 0.5;\r
290               int tempd_int = (int)tempd;\r
291               tempd = (double)tempd_int;\r
292               tempd /= 10;\r
293               outValue = "" + tempd;\r
294               outValue = outValue.replace('.', ',');\r
295             }\r
296             break;\r
297           case java.sql.Types.CHAR:case java.sql.Types.VARCHAR:case java.sql.Types.LONGVARCHAR:\r
298             outValue = rs.getString(valueIndex);\r
299             break;\r
300           case java.sql.Types.LONGVARBINARY:\r
301             outValue = rs.getString(valueIndex);\r
302             break;\r
303           case java.sql.Types.TIMESTAMP:\r
304             // it's important to use Timestamp here as getting it\r
305             // as a string is undefined and is only there for debugging\r
306             // according to the API. we can make it a string through formatting.\r
307             // -mh\r
308             Timestamp timestamp = (rs.getTimestamp(valueIndex));\r
309             if(!rs.wasNull()) {\r
310               java.util.Date date = new java.util.Date(timestamp.getTime());\r
311               outValue = _dateFormatterOut.format(date);\r
312               _cal.setTime(date);\r
313               int offset = _cal.get(Calendar.ZONE_OFFSET)+\r
314                            _cal.get(Calendar.DST_OFFSET);\r
315               String tzOffset = StringUtil.zeroPaddingNumber(\r
316                   offset/_millisPerHour,2,2);\r
317               outValue = outValue+"+"+tzOffset;\r
318             }\r
319             break;\r
320           default:\r
321             outValue = "<unsupported value>";\r
322           theLog.printWarning("Unsupported Datatype: at " + valueIndex +\r
323                               " (" + aType + ")");\r
324         }\r
325       } catch (SQLException e) {\r
326         throw new StorageObjectFailure("Could not get Value out of Resultset -- ",e);            \r
327       }\r
328     }\r
329     return  outValue;\r
330   }\r
331 \r
332       /*\r
333   *   select-Operator um einen Datensatz zu bekommen.\r
334   *   @param id Primaerschluessel des Datensatzes.\r
335   *   @return liefert EntityObject des gefundenen Datensatzes oder null.\r
336        */\r
337   public Entity selectById(String id)   \r
338         throws StorageObjectExc {\r
339     if (id==null||id.equals(""))\r
340       throw new StorageObjectExc("id war null");\r
341 \r
342     // ask object store for object\r
343     if ( StoreUtil.implementsStorableObject(theEntityClass) ) {\r
344       String uniqueId = id;\r
345       if ( theEntityClass.equals(StorableObjectEntity.class) )\r
346         uniqueId+="@"+theTable;\r
347       StoreIdentifier search_sid = new StoreIdentifier(theEntityClass, uniqueId);\r
348       theLog.printDebugInfo("CACHE: (dbg) looking for sid " + search_sid.toString());\r
349       Entity hit = (Entity)o_store.use(search_sid);\r
350       if ( hit!=null ) return hit;\r
351     }\r
352 \r
353     Statement stmt=null;Connection con=getPooledCon();\r
354     Entity returnEntity=null;\r
355     try {\r
356       ResultSet rs;\r
357       /** @todo better prepared statement */\r
358       String selectSql = "select * from " + theTable + " where " + thePKeyName + "=" + id;\r
359       stmt = con.createStatement();\r
360       rs = executeSql(stmt, selectSql);\r
361       if (rs != null) {\r
362         if (evaluatedMetaData==false) evalMetaData(rs.getMetaData());\r
363         if (rs.next())\r
364           returnEntity = makeEntityFromResultSet(rs);\r
365         else theLog.printDebugInfo("Keine daten fuer id: " + id + "in Tabelle" + theTable);\r
366         rs.close();\r
367       }\r
368       else {\r
369         theLog.printDebugInfo("No Data for Id " + id + " in Table " + theTable);\r
370       }\r
371     }\r
372     catch (SQLException sqe){\r
373       throwSQLException(sqe,"selectById"); return null;\r
374     }\r
375     catch (NumberFormatException e) {\r
376       theLog.printError("ID ist keine Zahl: " + id);\r
377     }\r
378     finally { freeConnection(con,stmt); }\r
379 \r
380     /** @todo OS: Entity should be saved in ostore */\r
381     return returnEntity;\r
382   }\r
383 \r
384 \r
385   /**\r
386    *   select-Operator um Datensaetze zu bekommen, die key = value erfuellen.\r
387    *   @param key  Datenbankfeld der Bedingung.\r
388    *   @param value  Wert die der key anehmen muss.\r
389    *   @return EntityList mit den gematchten Entities\r
390    */\r
391   public EntityList selectByFieldValue(String aField, String aValue)\r
392       throws StorageObjectFailure\r
393   {\r
394     return selectByFieldValue(aField, aValue, 0);\r
395   }\r
396 \r
397   /**\r
398    *   select-Operator um Datensaetze zu bekommen, die key = value erfuellen.\r
399    *   @param key  Datenbankfeld der Bedingung.\r
400    *   @param value  Wert die der key anehmen muss.\r
401    *   @param offset  Gibt an ab welchem Datensatz angezeigt werden soll.\r
402    *   @return EntityList mit den gematchten Entities\r
403    */\r
404   public EntityList selectByFieldValue(String aField, String aValue, int offset)\r
405       throws StorageObjectFailure\r
406   {\r
407     return selectByWhereClause(aField + "=" + aValue, offset);\r
408   }\r
409 \r
410 \r
411   /**\r
412    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.\r
413    * Also offset wird der erste Datensatz genommen.\r
414    *\r
415    * @param wc where-Clause\r
416    * @return EntityList mit den gematchten Entities\r
417    * @exception StorageObjectException\r
418    */\r
419   public EntityList selectByWhereClause(String where)\r
420       throws StorageObjectFailure\r
421   {\r
422     return selectByWhereClause(where, 0);\r
423   }\r
424 \r
425 \r
426   /**\r
427    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.\r
428    * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.\r
429    *\r
430    * @param wc where-Clause\r
431    * @param offset ab welchem Datensatz.\r
432    * @return EntityList mit den gematchten Entities\r
433    * @exception StorageObjectException\r
434    */\r
435   public EntityList selectByWhereClause(String whereClause, int offset)\r
436       throws StorageObjectFailure\r
437   {\r
438     return selectByWhereClause(whereClause, null, offset);\r
439   }\r
440 \r
441 \r
442   /**\r
443    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.\r
444    * Also offset wird der erste Datensatz genommen.\r
445    * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.\r
446    *\r
447    * @param wc where-Clause\r
448    * @param ob orderBy-Clause\r
449    * @return EntityList mit den gematchten Entities\r
450    * @exception StorageObjectException\r
451    */\r
452 \r
453   public EntityList selectByWhereClause(String where, String order)\r
454       throws StorageObjectFailure {\r
455     return selectByWhereClause(where, order, 0);\r
456   }\r
457 \r
458 \r
459   /**\r
460    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.\r
461    * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.\r
462    *\r
463    * @param wc where-Clause\r
464    * @param ob orderBy-Clause\r
465    * @param offset ab welchem Datensatz\r
466    * @return EntityList mit den gematchten Entities\r
467    * @exception StorageObjectException\r
468    */\r
469 \r
470   public EntityList selectByWhereClause(String whereClause, String orderBy, int offset)\r
471       throws StorageObjectFailure {\r
472     return selectByWhereClause(whereClause, orderBy, offset, defaultLimit);\r
473   }\r
474 \r
475 \r
476   /**\r
477    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.\r
478    * @param wc where-Clause\r
479    * @param ob orderBy-Clause\r
480    * @param offset ab welchem Datensatz\r
481    * @param limit wieviele Datens?tze\r
482    * @return EntityList mit den gematchten Entities\r
483    * @exception StorageObjectException\r
484    */\r
485 \r
486   public EntityList selectByWhereClause(String wc, String ob, int offset,\r
487                                         int limit) throws\r
488       StorageObjectFailure {\r
489 \r
490     // check o_store for entitylist\r
491     if (StoreUtil.implementsStorableObject(theEntityClass)) {\r
492       StoreIdentifier search_sid =\r
493           new StoreIdentifier(theEntityClass,\r
494                               StoreContainerType.STOC_TYPE_ENTITYLIST,\r
495                               StoreUtil.getEntityListUniqueIdentifierFor(\r
496           theTable, wc, ob, offset, limit));\r
497       EntityList hit = (EntityList) o_store.use(search_sid);\r
498       if (hit != null) {\r
499         theLog.printDebugInfo("CACHE (hit): " + search_sid.toString());\r
500         return hit;\r
501       }\r
502     }\r
503 \r
504     // local\r
505     EntityList theReturnList = null;\r
506     Connection con = null;\r
507     Statement stmt = null;\r
508     ResultSet rs;\r
509     int offsetCount = 0, count = 0;\r
510 \r
511     // build sql-statement\r
512 \r
513     /** @todo count sql string should only be assembled if we really count\r
514      *  see below at the end of method //rk */\r
515 \r
516     if (wc != null && wc.length() == 0) {\r
517       wc = null;\r
518     }\r
519     StringBuffer countSql = new StringBuffer("select count(*) from ").append(\r
520         theTable);\r
521     StringBuffer selectSql = new StringBuffer("select * from ").append(theTable);\r
522     if (wc != null) {\r
523       selectSql.append(" where ").append(wc);\r
524       countSql.append(" where ").append(wc);\r
525     }\r
526     if (ob != null && ! (ob.length() == 0)) {\r
527       selectSql.append(" order by ").append(ob);\r
528     }\r
529     if (theAdaptor.hasLimit()) {\r
530       if (limit > -1 && offset > -1) {\r
531         selectSql.append(" LIMIT ").append(limit).append(" OFFSET ").append(offset);\r
532       }\r
533     }\r
534 \r
535     // execute sql\r
536     try {\r
537       con = getPooledCon();\r
538       stmt = con.createStatement();\r
539 \r
540       // selecting...\r
541       rs = executeSql(stmt, selectSql.toString());\r
542       if (rs != null) {\r
543         if (!evaluatedMetaData)\r
544           evalMetaData(rs.getMetaData());\r
545 \r
546         theReturnList = new EntityList();\r
547         Entity theResultEntity;\r
548         while (rs.next()) {\r
549           theResultEntity = makeEntityFromResultSet(rs);\r
550           theReturnList.add(theResultEntity);\r
551           offsetCount++;\r
552         }\r
553         rs.close();\r
554       }\r
555 \r
556       // making entitylist infos\r
557       if (! (theAdaptor.hasLimit()))\r
558         count = offsetCount;\r
559 \r
560       if (theReturnList != null) {\r
561         // now we decide if we have to know an overall count...\r
562         count = offsetCount;\r
563         if (limit > -1 && offset > -1) {\r
564           if (offsetCount == limit) {\r
565             /** @todo counting should be deffered to entitylist\r
566              *  getSize() should be used */\r
567             rs = executeSql(stmt, countSql.toString());\r
568             if (rs != null) {\r
569               if (rs.next())\r
570                 count = rs.getInt(1);\r
571               rs.close();\r
572             }\r
573             else\r
574               theLog.printError("Could not count: " + countSql);\r
575           }\r
576         }\r
577         theReturnList.setCount(count);\r
578         theReturnList.setOffset(offset);\r
579         theReturnList.setWhere(wc);\r
580         theReturnList.setOrder(ob);\r
581         theReturnList.setStorage(this);\r
582         theReturnList.setLimit(limit);\r
583         if (offset >= limit)\r
584           theReturnList.setPrevBatch(offset - limit);\r
585         if (offset + offsetCount < count)\r
586           theReturnList.setNextBatch(offset + limit);\r
587         if (StoreUtil.implementsStorableObject(theEntityClass)) {\r
588           StoreIdentifier sid = theReturnList.getStoreIdentifier();\r
589           theLog.printDebugInfo("CACHE (add): " + sid.toString());\r
590           o_store.add(sid);\r
591         }\r
592       }\r
593     }\r
594     catch (SQLException sqe) {\r
595       throwSQLException(sqe, "selectByWhereClause");\r
596     }\r
597     finally {\r
598       try {\r
599         if (con != null)\r
600           freeConnection(con, stmt);\r
601       }\r
602       catch (Throwable t) {\r
603       }\r
604 \r
605     }\r
606 \r
607     return theReturnList;\r
608   }\r
609 \r
610 \r
611   /**\r
612    *  Bastelt aus einer Zeile der Datenbank ein EntityObjekt.\r
613    *\r
614    *  @param rs Das ResultSetObjekt.\r
615    *  @return Entity Die Entity.\r
616    */\r
617   private Entity makeEntityFromResultSet (ResultSet rs)\r
618       throws StorageObjectFailure\r
619   {\r
620     /** @todo OS: get Pkey from ResultSet and consult ObjectStore */\r
621     HashMap theResultHash = new HashMap();\r
622     String theResult = null;\r
623     int theType;\r
624     Entity returnEntity = null;\r
625     try {\r
626       int size = metadataFields.size();\r
627       for (int i = 0; i < size; i++) {\r
628         // alle durchlaufen bis nix mehr da\r
629 \r
630         theType = metadataTypes[i];\r
631         if (theType == java.sql.Types.LONGVARBINARY) {\r
632           InputStreamReader is = (InputStreamReader)rs.getCharacterStream(i + 1);\r
633           if (is != null) {\r
634             char[] data = new char[32768];\r
635             StringBuffer theResultString = new StringBuffer();\r
636             int len;\r
637             while ((len = is.read(data)) > 0) {\r
638               theResultString.append(data, 0, len);\r
639             }\r
640             is.close();\r
641             theResult = theResultString.toString();\r
642           }\r
643           else {\r
644             theResult = null;\r
645           }\r
646         }\r
647         else {\r
648           theResult = getValueAsString(rs, (i + 1), theType);\r
649         }\r
650         if (theResult != null) {\r
651           theResultHash.put(metadataFields.get(i), theResult);\r
652         }\r
653       }\r
654       if (theEntityClass != null) {\r
655         returnEntity = (Entity)theEntityClass.newInstance();\r
656         returnEntity.setValues(theResultHash);\r
657         returnEntity.setStorage(myselfDatabase);\r
658         if ( returnEntity instanceof StorableObject ) {\r
659           theLog.printDebugInfo("CACHE: ( in) " + returnEntity.getId() + " :"+theTable);\r
660           o_store.add(((StorableObject)returnEntity).getStoreIdentifier());\r
661         }\r
662       } else {\r
663         throwStorageObjectException("Internal Error: theEntityClass not set!");\r
664       }\r
665     }\r
666     catch (IllegalAccessException e) {\r
667       throwStorageObjectException("No access! -- " + e.getMessage());\r
668     }\r
669     catch (IOException e) {\r
670       throwStorageObjectException("IOException! -- " + e.getMessage());\r
671     }\r
672     catch (InstantiationException e) {\r
673       throwStorageObjectException("No Instatiation! -- " + e.getMessage());\r
674     }\r
675     catch (SQLException sqe) {\r
676       throwSQLException(sqe, "makeEntityFromResultSet");\r
677       return  null;\r
678     }\r
679     return  returnEntity;\r
680   }\r
681 \r
682   /**\r
683    * insert-Operator: f?gt eine Entity in die Tabelle ein. Eine Spalte WEBDB_CREATE\r
684    * wird automatisch mit dem aktuellen Datum gefuellt.\r
685    *\r
686    * @param theEntity\r
687    * @return der Wert des Primary-keys der eingef?gten Entity\r
688    */\r
689   public String insert (Entity theEntity) throws StorageObjectFailure {\r
690     //cache\r
691     invalidatePopupCache();\r
692 \r
693     // invalidating all EntityLists corresponding with theEntityClass\r
694     if ( StoreUtil.implementsStorableObject(theEntityClass) ) {\r
695       StoreContainerType stoc_type =\r
696           StoreContainerType.valueOf( theEntityClass,\r
697           StoreContainerType.STOC_TYPE_ENTITYLIST);\r
698       o_store.invalidate(stoc_type);\r
699     }\r
700 \r
701     String returnId = null;\r
702     Connection con = null; PreparedStatement pstmt = null;\r
703 \r
704     try {\r
705       ArrayList streamedInput = theEntity.streamedInput();\r
706       StringBuffer f = new StringBuffer();\r
707       StringBuffer v = new StringBuffer();\r
708       String aField, aValue;\r
709       boolean firstField = true;\r
710       // make sql-string\r
711       for (int i = 0; i < getFields().size(); i++) {\r
712         aField = (String)getFields().get(i);\r
713         if (!aField.equals(thePKeyName)) {\r
714           aValue = null;\r
715           // sonderfaelle\r
716           if (aField.equals("webdb_create") ||\r
717               aField.equals("webdb_lastchange")) {\r
718             aValue = "NOW()";\r
719           }\r
720           else {\r
721             if (streamedInput != null && streamedInput.contains(aField)) {\r
722               aValue = "?";\r
723             }\r
724             else {\r
725               if (theEntity.hasValueForField(aField)) {\r
726                 aValue = "'" + JDBCStringRoutines.escapeStringLiteral((String)theEntity.getValue(aField)) + "'";\r
727               }\r
728             }\r
729           }\r
730           // wenn Wert gegeben, dann einbauen\r
731           if (aValue != null) {\r
732             if (firstField == false) {\r
733               f.append(",");\r
734               v.append(",");\r
735             }\r
736             else {\r
737               firstField = false;\r
738             }\r
739             f.append(aField);\r
740             v.append(aValue);\r
741           }\r
742         }\r
743       }         // end for\r
744       // insert into db\r
745       StringBuffer sqlBuf = new StringBuffer("insert into ").append(theTable).append("(").append(f).append(") values (").append(v).append(")");\r
746       String sql = sqlBuf.toString();\r
747       //theLog.printInfo("INSERT: " + sql);\r
748       con = getPooledCon();\r
749       con.setAutoCommit(false);\r
750       pstmt = con.prepareStatement(sql);\r
751       if (streamedInput != null) {\r
752         for (int i = 0; i < streamedInput.size(); i++) {\r
753           String inputString = (String)theEntity.getValue((String)streamedInput.get(i));\r
754           pstmt.setBytes(i + 1, inputString.getBytes());\r
755         }\r
756       }\r
757       int ret = pstmt.executeUpdate();\r
758       if(ret == 0){\r
759         //insert failed\r
760         return null;\r
761       }\r
762       pstmt = con.prepareStatement(theAdaptor.getLastInsertSQL((Database)myselfDatabase));\r
763       ResultSet rs = pstmt.executeQuery();\r
764       rs.next();\r
765       returnId = rs.getString(1);\r
766       theEntity.setId(returnId);\r
767     } catch (SQLException sqe) {\r
768       throwSQLException(sqe, "insert");\r
769     } finally {\r
770       try {\r
771         con.setAutoCommit(true);\r
772       } catch (Exception e) {\r
773         ;\r
774       }\r
775       freeConnection(con, pstmt);\r
776     }\r
777     /** @todo store entity in o_store */\r
778     return  returnId;\r
779   }\r
780 \r
781   /**\r
782    * update-Operator: aktualisiert eine Entity. Eine Spalte WEBDB_LASTCHANGE\r
783    * wird automatisch mit dem aktuellen Datum gefuellt.\r
784    *\r
785    * @param theEntity\r
786    */\r
787   public void update (Entity theEntity) throws StorageObjectFailure\r
788   {\r
789     Connection con = null; PreparedStatement pstmt = null;\r
790     /** @todo this is stupid: why do we prepare statement, when we\r
791      *  throw it away afterwards. should be regular statement\r
792      *  update/insert could better be one routine called save()\r
793      *  that chooses to either insert or update depending if we\r
794      *  have a primary key in the entity. i don't know if we\r
795      *  still need the streamed input fields. // rk  */\r
796 \r
797     /** @todo extension: check if Entity did change, otherwise we don't need\r
798      *  the roundtrip to the database */\r
799 \r
800     /** invalidating corresponding entitylists in o_store*/\r
801     if ( StoreUtil.implementsStorableObject(theEntityClass) ) {\r
802       StoreContainerType stoc_type =\r
803           StoreContainerType.valueOf( theEntityClass,\r
804           StoreContainerType.STOC_TYPE_ENTITYLIST);\r
805       o_store.invalidate(stoc_type);\r
806     }\r
807 \r
808     ArrayList streamedInput = theEntity.streamedInput();\r
809     String id = theEntity.getId();\r
810     String aField;\r
811     StringBuffer fv = new StringBuffer();\r
812     boolean firstField = true;\r
813     //cache\r
814     invalidatePopupCache();\r
815     // build sql statement\r
816     for (int i = 0; i < getFields().size(); i++) {\r
817       aField = (String)metadataFields.get(i);\r
818       // only normal cases\r
819       if (!(aField.equals(thePKeyName) || aField.equals("webdb_create") ||\r
820             aField.equals("webdb_lastchange") || (streamedInput != null && streamedInput.contains(aField)))) {\r
821         if (theEntity.hasValueForField(aField)) {\r
822           if (firstField == false) {\r
823             fv.append(", ");\r
824           }\r
825           else {\r
826             firstField = false;\r
827           }\r
828           fv.append(aField).append("='").append(JDBCStringRoutines.escapeStringLiteral((String) theEntity.getValue(aField))).append("'");\r
829 \r
830 //              fv.append(aField).append("='").append(StringUtil.quote((String)theEntity.getValue(aField))).append("'");\r
831         }\r
832       }\r
833     }\r
834     StringBuffer sql = new StringBuffer("update ").append(theTable).append(" set ").append(fv);\r
835     // exceptions\r
836     if (metadataFields.contains("webdb_lastchange")) {\r
837       sql.append(",webdb_lastchange=NOW()");\r
838     }\r
839     // special case: the webdb_create requires the field in yyyy-mm-dd HH:mm\r
840     // format so anything extra will be ignored. -mh\r
841     if (metadataFields.contains("webdb_create") &&\r
842         theEntity.hasValueForField("webdb_create")) {\r
843       // minimum of 10 (yyyy-mm-dd)...\r
844       if (theEntity.getValue("webdb_create").length() >= 10) {\r
845         String dateString = theEntity.getValue("webdb_create");\r
846         // if only 10, then add 00:00 so it doesn't throw a ParseException\r
847         if (dateString.length() == 10)\r
848           dateString=dateString+" 00:00";\r
849 \r
850         // TimeStamp stuff\r
851         try {\r
852           java.util.Date d = _dateFormatterIn.parse(dateString);\r
853           Timestamp tStamp = new Timestamp(d.getTime());\r
854           sql.append(",webdb_create='"+tStamp.toString()+"'");\r
855         } catch (ParseException e) {\r
856           throw new StorageObjectFailure(e);\r
857         }\r
858       }\r
859     }\r
860     if (streamedInput != null) {\r
861       for (int i = 0; i < streamedInput.size(); i++) {\r
862         sql.append(",").append(streamedInput.get(i)).append("=?");\r
863       }\r
864     }\r
865     sql.append(" where id=").append(id);\r
866     //theLog.printInfo("UPDATE: " + sql);\r
867     // execute sql\r
868     try {\r
869       con = getPooledCon();\r
870       con.setAutoCommit(false);\r
871       pstmt = con.prepareStatement(sql.toString());\r
872       if (streamedInput != null) {\r
873         for (int i = 0; i < streamedInput.size(); i++) {\r
874           String inputString = theEntity.getValue((String)streamedInput.get(i));\r
875           pstmt.setBytes(i + 1, inputString.getBytes());\r
876         }\r
877       }\r
878       pstmt.executeUpdate();\r
879     }\r
880     catch (SQLException sqe) {\r
881       throwSQLException(sqe, "update");\r
882     }\r
883     finally {\r
884       try {\r
885         con.setAutoCommit(true);\r
886       } catch (Exception e) {\r
887         ;\r
888       }\r
889       freeConnection(con, pstmt);\r
890     }\r
891   }\r
892 \r
893       /*\r
894   *   delete-Operator\r
895   *   @param id des zu loeschenden Datensatzes\r
896   *   @return boolean liefert true zurueck, wenn loeschen erfolgreich war.\r
897        */\r
898   public boolean delete (String id) throws StorageObjectFailure {\r
899 \r
900     invalidatePopupCache();\r
901     // ostore send notification\r
902     if ( StoreUtil.implementsStorableObject(theEntityClass) ) {\r
903       String uniqueId = id;\r
904       if ( theEntityClass.equals(StorableObjectEntity.class) )\r
905         uniqueId+="@"+theTable;\r
906       theLog.printInfo("CACHE: (del) " + id);\r
907       StoreIdentifier search_sid =\r
908           new StoreIdentifier(theEntityClass, StoreContainerType.STOC_TYPE_ENTITY, uniqueId);\r
909       o_store.invalidate(search_sid);\r
910     }\r
911 \r
912     /** @todo could be prepared Statement */\r
913     Statement stmt = null; Connection con = null;\r
914     int res = 0;\r
915     String sql="delete from "+theTable+" where "+thePKeyName+"='"+id+"'";\r
916     //theLog.printInfo("DELETE " + sql);\r
917     try {\r
918       con = getPooledCon(); stmt = con.createStatement();\r
919       res = stmt.executeUpdate(sql);\r
920     }\r
921     catch (SQLException sqe) { throwSQLException(sqe, "delete"); }\r
922     finally { freeConnection(con, stmt); }\r
923 \r
924     return  (res > 0) ? true : false;\r
925   }\r
926 \r
927       /* noch nicht implementiert.\r
928   * @return immer false\r
929        */\r
930   public boolean delete (EntityList theEntityList) {\r
931     invalidatePopupCache();\r
932     return  false;\r
933   }\r
934 \r
935   /**\r
936    * Diese Methode sollte ueberschrieben werden, wenn fuer die abgeleitete Database-Klasse\r
937    * eine SimpleList mit Standard-Popupdaten erzeugt werden koennen soll.\r
938    * @return null\r
939    */\r
940   public SimpleList getPopupData () throws StorageObjectFailure {\r
941     return  null;\r
942   }\r
943 \r
944   /**\r
945    *  Holt Daten fuer Popups.\r
946    *  @param name  Name des Feldes.\r
947    *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.\r
948    *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.\r
949    */\r
950   public SimpleList getPopupData (String name, boolean hasNullValue)\r
951       throws StorageObjectFailure {\r
952     return  getPopupData(name, hasNullValue, null);\r
953   }\r
954 \r
955   /**\r
956    *  Holt Daten fuer Popups.\r
957    *  @param name  Name des Feldes.\r
958    *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.\r
959    *  @param where  Schraenkt die Selektion der Datensaetze ein.\r
960    *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.\r
961    */\r
962   public SimpleList getPopupData (String name, boolean hasNullValue, String where) throws StorageObjectFailure {\r
963     return  getPopupData(name, hasNullValue, where, null);\r
964   }\r
965 \r
966   /**\r
967    *  Holt Daten fuer Popups.\r
968    *  @param name  Name des Feldes.\r
969    *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.\r
970    *  @param where  Schraenkt die Selektion der Datensaetze ein.\r
971    *  @param order  Gibt ein Feld als Sortierkriterium an.\r
972    *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.\r
973    */\r
974   public SimpleList getPopupData (String name, boolean hasNullValue, String where, String order) throws StorageObjectFailure {\r
975     // caching\r
976     if (hasPopupCache && popupCache != null)\r
977       return  popupCache;\r
978     SimpleList simpleList = null;\r
979     Connection con = null;\r
980     Statement stmt = null;\r
981     // build sql\r
982     StringBuffer sql = new StringBuffer("select ").append(thePKeyName)\r
983                      .append(",").append(name).append(" from ")\r
984                      .append(theTable);\r
985     if (where != null && !(where.length() == 0))\r
986       sql.append(" where ").append(where);\r
987     sql.append(" order by ");\r
988     if (order != null && !(order.length() == 0))\r
989       sql.append(order);\r
990     else\r
991       sql.append(name);\r
992     // execute sql\r
993     try {\r
994       con = getPooledCon();\r
995     }\r
996     catch (Exception e) {\r
997       throw new StorageObjectFailure(e);\r
998     }\r
999     try {\r
1000       stmt = con.createStatement();\r
1001       ResultSet rs = executeSql(stmt, sql.toString());\r
1002 \r
1003       if (rs != null) {\r
1004         if (!evaluatedMetaData) get_meta_data();\r
1005         simpleList = new SimpleList();\r
1006         // if popup has null-selector\r
1007         if (hasNullValue) simpleList.add(POPUP_EMTYLINE);\r
1008 \r
1009         SimpleHash popupDict;\r
1010         while (rs.next()) {\r
1011           popupDict = new SimpleHash();\r
1012           popupDict.put("key", getValueAsString(rs, 1, thePKeyType));\r
1013           popupDict.put("value", rs.getString(2));\r
1014           simpleList.add(popupDict);\r
1015         }\r
1016         rs.close();\r
1017       }\r
1018     }\r
1019     catch (Exception e) {\r
1020       theLog.printError("getPopupData: "+e.getMessage());\r
1021       throw new StorageObjectFailure(e);\r
1022     }\r
1023     finally {\r
1024       freeConnection(con, stmt);\r
1025     }\r
1026 \r
1027     if (hasPopupCache) popupCache = simpleList;\r
1028     return  simpleList;\r
1029   }\r
1030 \r
1031   /**\r
1032    * Liefert alle Daten der Tabelle als SimpleHash zurueck. Dies wird verwandt,\r
1033    * wenn in den Templates ein Lookup-Table benoetigt wird. Sollte nur bei kleinen\r
1034    * Tabellen Verwendung finden.\r
1035    * @return SimpleHash mit den Tabellezeilen.\r
1036    */\r
1037   public SimpleHash getHashData () {\r
1038     /** @todo dangerous! this should have a flag to be enabled, otherwise\r
1039      *  very big Hashes could be returned */\r
1040     if (hashCache == null) {\r
1041       try {\r
1042         hashCache = HTMLTemplateProcessor.makeSimpleHash(selectByWhereClause("",\r
1043             -1));\r
1044       }\r
1045       catch (StorageObjectFailure e) {\r
1046         theLog.printDebugInfo(e.getMessage());\r
1047       }\r
1048     }\r
1049     return  hashCache;\r
1050   }\r
1051 \r
1052       /* invalidates the popupCache\r
1053        */\r
1054   protected void invalidatePopupCache () {\r
1055     /** @todo  invalidates toooo much */\r
1056     popupCache = null;\r
1057     hashCache = null;\r
1058   }\r
1059 \r
1060   /**\r
1061    * Diese Methode fuehrt den Sqlstring <i>sql</i> aus und timed im Logfile.\r
1062    * @param stmt Statemnt\r
1063    * @param sql Sql-String\r
1064    * @return ResultSet\r
1065    * @exception StorageObjectException\r
1066    */\r
1067   public ResultSet executeSql (Statement stmt, String sql)\r
1068       throws StorageObjectFailure, SQLException\r
1069   {\r
1070     long startTime = System.currentTimeMillis();\r
1071     ResultSet rs;\r
1072     try {\r
1073       rs = stmt.executeQuery(sql);\r
1074       //theLog.printInfo((System.currentTimeMillis() - startTime) + "ms. for: " + sql);\r
1075     }\r
1076     catch (SQLException e)\r
1077     {\r
1078       theLog.printDebugInfo("Failed: " + (System.currentTimeMillis() - startTime) + "ms. for: "+ sql);\r
1079       throw e;\r
1080     }\r
1081 \r
1082     return  rs;\r
1083   }\r
1084 \r
1085   /**\r
1086    * Fuehrt Statement stmt aus und liefert Resultset zurueck. Das SQL-Statment wird\r
1087    * getimed und geloggt.\r
1088    * @param stmt PreparedStatement mit der SQL-Anweisung\r
1089    * @return Liefert ResultSet des Statements zurueck.\r
1090    * @exception StorageObjectException, SQLException\r
1091    */\r
1092   public ResultSet executeSql (PreparedStatement stmt)\r
1093       throws StorageObjectFailure, SQLException {\r
1094 \r
1095     long startTime = (new java.util.Date()).getTime();\r
1096     ResultSet rs = stmt.executeQuery();\r
1097     theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms.");\r
1098     return  rs;\r
1099   }\r
1100 \r
1101   /**\r
1102    * returns the number of rows in the table\r
1103    */\r
1104   public int getSize(String where)\r
1105       throws SQLException,StorageObjectFailure\r
1106   {\r
1107     long  startTime = System.currentTimeMillis();\r
1108     String sql = "SELECT Count(*) FROM "+ theTable;\r
1109     if (where != null && !(where.length() == 0))\r
1110       sql = sql + " where " + where;\r
1111     Connection con = null;\r
1112     Statement stmt = null;\r
1113     int result = 0;\r
1114 \r
1115     try {\r
1116       con = getPooledCon();\r
1117       stmt = con.createStatement();\r
1118       ResultSet rs = executeSql(stmt,sql);\r
1119       while(rs.next()){\r
1120         result = rs.getInt(1);\r
1121       }\r
1122     }\r
1123     catch (SQLException e) {\r
1124       theLog.printError(e.getMessage());\r
1125     }\r
1126     finally {\r
1127       freeConnection(con,stmt);\r
1128     }\r
1129     //theLog.printInfo(theTable + " has "+ result +" rows where " + where);\r
1130     //theLog.printInfo((System.currentTimeMillis() - startTime) + "ms. for: " + sql);\r
1131     return result;\r
1132   }\r
1133 \r
1134   public int executeUpdate(Statement stmt, String sql)\r
1135       throws StorageObjectFailure, SQLException\r
1136   {\r
1137     int rs;\r
1138     long  startTime = (new java.util.Date()).getTime();\r
1139     try\r
1140     {\r
1141       rs = stmt.executeUpdate(sql);\r
1142       //theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms. for: " + sql);\r
1143     }\r
1144     catch (SQLException e)\r
1145     {\r
1146       theLog.printDebugInfo("Failed: " + (new java.util.Date().getTime()\r
1147           - startTime) + "ms. for: "+ sql);\r
1148       throw e;\r
1149     }\r
1150     return rs;\r
1151   }\r
1152 \r
1153   public int executeUpdate(String sql) throws StorageObjectFailure, SQLException\r
1154   {\r
1155     int result=-1;\r
1156     long  startTime = (new java.util.Date()).getTime();\r
1157     Connection con=null;\r
1158     PreparedStatement pstmt=null;\r
1159     try {\r
1160       con=getPooledCon();\r
1161       pstmt = con.prepareStatement(sql);\r
1162       result = pstmt.executeUpdate();\r
1163     }\r
1164     catch (Exception e) {\r
1165       theLog.printDebugInfo("executeUpdate failed: "+e.getMessage());\r
1166       throw new StorageObjectFailure("executeUpdate failed",e);\r
1167     }\r
1168     finally {\r
1169       freeConnection(con,pstmt);\r
1170     }\r
1171     //theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms. for: " + sql);\r
1172     return result;\r
1173   }\r
1174 \r
1175   /**\r
1176    * Wertet ResultSetMetaData aus und setzt interne Daten entsprechend\r
1177    * @param md ResultSetMetaData\r
1178    * @exception StorageObjectException\r
1179    */\r
1180   private void evalMetaData (ResultSetMetaData md)\r
1181       throws StorageObjectFailure {\r
1182 \r
1183     this.evaluatedMetaData = true;\r
1184     this.metadataFields = new ArrayList();\r
1185     this.metadataLabels = new ArrayList();\r
1186     this.metadataNotNullFields = new ArrayList();\r
1187     try {\r
1188       int numFields = md.getColumnCount();\r
1189       this.metadataTypes = new int[numFields];\r
1190       String aField;\r
1191       int aType;\r
1192       for (int i = 1; i <= numFields; i++) {\r
1193         aField = md.getColumnName(i);\r
1194         metadataFields.add(aField);\r
1195         metadataLabels.add(md.getColumnLabel(i));\r
1196         aType = md.getColumnType(i);\r
1197         metadataTypes[i - 1] = aType;\r
1198         if (aField.equals(thePKeyName)) {\r
1199           thePKeyType = aType; thePKeyIndex = i;\r
1200         }\r
1201         if (md.isNullable(i) == ResultSetMetaData.columnNullable) {\r
1202           metadataNotNullFields.add(aField);\r
1203         }\r
1204       }\r
1205     } catch (SQLException e) {\r
1206       throwSQLException(e, "evalMetaData");\r
1207     }\r
1208   }\r
1209 \r
1210   /**\r
1211    *  Wertet die Metadaten eines Resultsets fuer eine Tabelle aus,\r
1212    *  um die alle Columns und Typen einer Tabelle zu ermitteln.\r
1213    */\r
1214   private void get_meta_data () throws StorageObjectFailure {\r
1215     Connection con = null;\r
1216     PreparedStatement pstmt = null;\r
1217     String sql = "select * from " + theTable + " where 0=1";\r
1218     try {\r
1219       con = getPooledCon();\r
1220       pstmt = con.prepareStatement(sql);\r
1221       //theLog.printInfo("METADATA: " + sql);\r
1222       ResultSet rs = pstmt.executeQuery();\r
1223       evalMetaData(rs.getMetaData());\r
1224       rs.close();\r
1225     } catch (SQLException e) {\r
1226       throwSQLException(e, "get_meta_data");\r
1227     } finally {\r
1228       freeConnection(con, pstmt);\r
1229     }\r
1230   }\r
1231 \r
1232 \r
1233   public Connection getPooledCon() throws StorageObjectFailure {\r
1234               /* @todo , doublecheck but I'm pretty sure that this is unnecessary. -mh\r
1235                       try{\r
1236                       Class.forName("com.codestudio.sql.PoolMan").newInstance();\r
1237               } catch (Exception e){\r
1238                       throw new StorageObjectException("Could not find the PoolMan Driver"\r
1239                           +e.toString());\r
1240               }*/\r
1241     Connection con = null;\r
1242 \r
1243     try{\r
1244       con = SQLManager.getInstance().requestConnection();\r
1245     }\r
1246     catch(SQLException e){\r
1247       theLog.printError("could not connect to the database "+e.getMessage());\r
1248       System.err.println("could not connect to the database "+e.getMessage());\r
1249       throw new StorageObjectFailure("Could not connect to the database",e);\r
1250     }\r
1251 \r
1252     return con;\r
1253   }\r
1254 \r
1255   public void freeConnection (Connection con, Statement stmt) throws StorageObjectFailure {\r
1256     SQLManager.closeStatement(stmt);\r
1257     SQLManager.getInstance().returnConnection(con);\r
1258   }\r
1259 \r
1260   /**\r
1261    * Wertet SQLException aus und wirft dannach eine StorageObjectException\r
1262    * @param sqe SQLException\r
1263    * @param wo Funktonsname, in der die SQLException geworfen wurde\r
1264    * @exception StorageObjectException\r
1265    */\r
1266   protected void throwSQLException (SQLException sqe, String wo) \r
1267         throws StorageObjectFailure {\r
1268     String state = "";\r
1269     String message = "";\r
1270     int vendor = 0;\r
1271     if (sqe != null) {\r
1272       state = sqe.getSQLState();\r
1273       message = sqe.getMessage();\r
1274       vendor = sqe.getErrorCode();\r
1275     }\r
1276     theLog.printError(state + ": " + vendor + " : " + message + " Funktion: "\r
1277                       + wo);\r
1278     throw new StorageObjectFailure(\r
1279         (sqe == null) ? "undefined sql exception" :sqe.getMessage(),sqe\r
1280         );\r
1281   }\r
1282 \r
1283   protected void _throwStorageObjectException (Exception e, String wo)\r
1284       throws StorageObjectFailure {\r
1285 \r
1286     if (e != null) {\r
1287       theLog.printError(e.getMessage()+ wo);\r
1288       throw  new StorageObjectFailure(wo, e);\r
1289     }\r
1290     else {\r
1291       theLog.printError(wo);\r
1292       throw  new StorageObjectFailure(wo,null);\r
1293     }\r
1294 \r
1295   }\r
1296 \r
1297   /**\r
1298    * Loggt Fehlermeldung mit dem Parameter Message und wirft dannach\r
1299    * eine StorageObjectException\r
1300    * @param message Nachricht mit dem Fehler\r
1301    * @exception StorageObjectException\r
1302    */\r
1303   void throwStorageObjectException (String message)\r
1304       throws StorageObjectFailure {\r
1305     _throwStorageObjectException(null, message);\r
1306   }\r
1307 \r
1308 }\r
1309 \r
1310 \r
1311 \r