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