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