Initial revision
[mir.git] / source / mir / storage / Database.java
1 /*
2  * put your module comment here
3  */
4 package mir.storage;
5
6 import  java.sql.*;
7 import  java.lang.*;
8 import  java.io.*;
9 import  java.util.*;
10 import  freemarker.template.*;
11 import  com.javaexchange.dbConnectionBroker.*;
12 import  mir.storage.StorageObject;
13 import  mir.entity.*;
14 import  mir.misc.*;
15
16
17 /**
18  * Diese Klasse implementiert die Zugriffsschicht auf die Datenbank.
19  * Alle Projektspezifischen Datenbankklassen erben von dieser Klasse.
20  * In den Unterklassen wird im Minimalfall nur die Tabelle angegeben.
21  * Im Konfigurationsfile findet sich eine Verweis auf den verwendeten
22  * Treiber, Host, User und Passwort, ueber den der Zugriff auf die
23  * Datenbank erfolgt.
24  *
25  * @author RK
26  * @version 16.7.1999
27  */
28 public class Database implements StorageObject {
29
30         protected DbConnectionBroker        myBroker;
31         protected String                    theTable;
32         protected String                    theCoreTable=null;
33         protected String                    thePKeyName="id";
34         protected int                       thePKeyType;
35         protected boolean                   evaluatedMetaData=false;
36         protected ArrayList                 metadataFields,metadataLabels,metadataNotNullFields;
37         protected int[]                     metadataTypes;
38         protected Class                     theEntityClass;
39         protected StorageObject             myselfDatabase;
40         protected HashMap                   cache;
41         protected SimpleList                popupCache=null;
42         protected boolean                                               hasPopupCache = false;
43         protected SimpleHash                hashCache=null;
44         protected boolean                   hasTimestamp=true;
45         private       String                database_driver;
46         private       String                database_url;
47         private int                         defaultLimit;
48         private DatabaseAdaptor             theAdaptor;
49         protected Logfile                   theLog;
50         protected Connection                con;
51
52         /**
53          * Kontruktor bekommt den Filenamen des Konfigurationsfiles übergeben.
54          * Aus diesem file werden <code>Database.Logfile</code>,
55          * <code>Database.Username</code>,<code>Database.Password</code>,
56          * <code>Database.Host</code> und <code>Database.Adaptor</code>
57          * ausgelesen und ein Broker für die Verbindugen zur Datenbank
58          * erzeugt.
59          *
60          * @param   String confFilename Dateiname der Konfigurationsdatei
61          */
62         public Database() {
63                 theLog = Logfile.getInstance(Configuration.getProperty("Home") + Configuration.getProperty("Database.Logfile"));
64                 String database_username=Configuration.getProperty("Database.Username");
65                 String database_password=Configuration.getProperty("Database.Password");
66                 String database_host=Configuration.getProperty("Database.Host");
67                 String theAdaptorName=Configuration.getProperty("Database.Adaptor");
68                 try {
69                         theEntityClass = Class.forName("mir.entity.GenericEntity");
70                         theAdaptor = (DatabaseAdaptor)Class.forName(theAdaptorName).newInstance();
71                         defaultLimit = Integer.parseInt(Configuration.getProperty("Database.Limit"));
72                         database_driver=theAdaptor.getDriver();
73                         database_url=theAdaptor.getURL(database_username,database_password,database_host);
74                         theLog.printDebugInfo("adding Broker with: " +database_driver+":"+database_url  );
75                         Configuration.addBroker(database_driver,database_url);
76                         myBroker=Configuration.getBroker();
77                 }
78                 catch (Exception e){
79                         theLog.printError("Bei Konstruktion von Database() with " + theAdaptorName + " -- " +e.toString());
80                 }
81         }
82
83         /**
84          * Liefert die Entity-Klasse zurück, in der eine Datenbankzeile gewrappt
85          * wird. Wird die Entity-Klasse durch die erbende Klasse nicht überschrieben,
86          * wird eine mir.entity.GenericEntity erzeugt.
87          *
88          * @return Class-Objekt der Entity
89          */
90         public java.lang.Class getEntityClass () {
91                 return  theEntityClass;
92         }
93
94         /**
95          * Liefert die Standardbeschränkung von select-Statements zurück, also
96          * wieviel Datensätze per Default selektiert werden.
97          *
98          * @return Standard-Anzahl der Datensätze
99          */
100         public int getLimit () {
101                 return  defaultLimit;
102         }
103
104         /**
105          * Liefert den Namen des Primary-Keys zurück. Wird die Variable nicht von
106          * der erbenden Klasse überschrieben, so ist der Wert <code>PKEY</code>
107          * @return Name des Primary-Keys
108          */
109         public String getIdName () {
110                 return  thePKeyName;
111         }
112
113         /**
114          * Liefert den Namen der Tabelle, auf das sich das Datenbankobjekt bezieht.
115          *
116          * @return Name der Tabelle
117          */
118         public String getTableName () {
119                 return  theTable;
120         }
121
122         /*
123          *   Dient dazu vererbte Tabellen bei objectrelationalen DBMS
124          *   zu speichern, wenn die id einer Tabelle in der parenttabelle verwaltet wird.
125          *   @return liefert theCoreTabel als String zurueck, wenn gesetzt, sonst
126          *    the Table
127          */
128
129         public String getCoreTable(){
130                 if (theCoreTable!=null) return theCoreTable;
131                 else return theTable;
132         }
133
134         /**
135          * Liefert Feldtypen der Felder der Tabelle zurueck (s.a. java.sql.Types)
136          * @return int-Array mit den Typen der Felder
137          * @exception StorageObjectException
138          */
139         public int[] getTypes () throws StorageObjectException {
140                 if (metadataTypes == null)
141                         get_meta_data();
142                 return  metadataTypes;
143         }
144
145         /**
146          * Liefert eine Liste der Labels der Tabellenfelder
147          * @return ArrayListe mit Labeln
148          * @exception StorageObjectException
149          */
150         public ArrayList getLabels () throws StorageObjectException {
151                 if (metadataLabels == null)
152                         get_meta_data();
153                 return  metadataLabels;
154         }
155
156         /**
157          * Liefert eine Liste der Felder der Tabelle
158          * @return ArrayList mit Feldern
159          * @exception StorageObjectException
160          */
161         public ArrayList getFields () throws StorageObjectException {
162                 if (metadataFields == null)
163                         get_meta_data();
164                 return  metadataFields;
165         }
166
167
168         /*
169          *   Uebersetzt die Datenbankwerte in einen String.
170          *   @param inValue  Wert aus ResultSet.
171          *   @param aType  Datenbanktyp.
172          *   @return liefert den Wert als String zurueck. Wenn keine Umwandlung moeglich
173          *           dann /unsupported value/
174          */
175         private String getValueAsString (ResultSet rs, int valueIndex, int aType) throws StorageObjectException {
176                 String outValue = null;
177                 if (rs != null) {
178                         try {
179                                 switch (aType) {
180                                         case java.sql.Types.BIT:
181                                                 outValue = (rs.getBoolean(valueIndex) == true) ? "1" : "0";
182                                                 break;
183                                         case java.sql.Types.INTEGER:case java.sql.Types.SMALLINT:case java.sql.Types.TINYINT:case java.sql.Types.BIGINT:
184                                                 int out = rs.getInt(valueIndex);
185                                                 if (!rs.wasNull())
186                                                         outValue = new Integer(out).toString();
187                                                 break;
188                                         case java.sql.Types.NUMERIC:
189                                                 long outl = rs.getLong(valueIndex);
190                                                 if (!rs.wasNull())
191                                                         outValue = new Long(outl).toString();
192                                                 break;
193                                         case java.sql.Types.REAL:
194                                                 float tempf = rs.getFloat(valueIndex);
195                                                 if (!rs.wasNull()) {
196                                                         tempf *= 10;
197                                                         tempf += 0.5;
198                                                         int tempf_int = (int)tempf;
199                                                         tempf = (float)tempf_int;
200                                                         tempf /= 10;
201                                                         outValue = "" + tempf;
202                                                         outValue = outValue.replace('.', ',');
203                                                 }
204                                                 break;
205                                         case java.sql.Types.DOUBLE:
206                                                 double tempd = rs.getDouble(valueIndex);
207                                                 if (!rs.wasNull()) {
208                                                         tempd *= 10;
209                                                         tempd += 0.5;
210                                                         int tempd_int = (int)tempd;
211                                                         tempd = (double)tempd_int;
212                                                         tempd /= 10;
213                                                         outValue = "" + tempd;
214                                                         outValue = outValue.replace('.', ',');
215                                                 }
216                                                 break;
217                                         case java.sql.Types.CHAR:case java.sql.Types.VARCHAR:case java.sql.Types.LONGVARCHAR:
218                                                 outValue = rs.getString(valueIndex);
219                                                 if (outValue != null)
220                                                         outValue = StringUtil.encodeHtml(StringUtil.unquote(outValue));
221                                                 break;
222                                         case java.sql.Types.LONGVARBINARY:
223                                                 outValue = rs.getString(valueIndex);
224                                                 if (outValue != null)
225                                                         outValue = StringUtil.encodeHtml(StringUtil.unquote(outValue));
226                                                 break;
227                                         case java.sql.Types.TIMESTAMP:
228                                                 Timestamp timestamp = (rs.getTimestamp(valueIndex));
229                                                 if (!rs.wasNull()) {
230                                                         outValue = timestamp.toString();
231                                                 }
232                                                 break;
233                                         default:
234                                                 outValue = "<unsupported value>";
235                                                 theLog.printWarning("Unsupported Datatype: at " + valueIndex +
236                                                                 " (" + aType + ")");
237                                 }
238                         } catch (SQLException e) {
239                                 throw  new StorageObjectException("Could not get Value out of Resultset -- "
240                                                 + e.toString());
241                         }
242                 }
243                 return  outValue;
244         }
245
246         /*
247          *   select-Operator um einen Datensatz zu bekommen.
248          *   @param id Primaerschluessel des Datensatzes.
249          *   @return liefert EntityObject des gefundenen Datensatzes oder null.
250          */
251         public Entity selectById(String id)
252     throws StorageObjectException {
253
254                 if (id==null||id.equals(""))
255       throw new StorageObjectException("id war null");
256                 if (cache != null && cache.containsKey(id))
257                         return (Entity)cache.get(id);  // wenn cache gesetzt, evtl. kein roundtrip zur Datenbank
258
259                 Statement stmt=null;Connection con=getPooledCon();
260                 Entity returnEntity=null;
261                 try {
262                         ResultSet rs;
263                         String selectSql = "select * from " + theTable + " where " + thePKeyName + "=" + id;
264                         stmt = con.createStatement();
265                         rs = executeSql(stmt, selectSql);
266                         if (rs != null) {
267                                 if (evaluatedMetaData==false) evalMetaData(rs.getMetaData());
268                                 if (rs.next())
269                                         returnEntity = makeEntityFromResultSet(rs);
270                                 else theLog.printDebugInfo("Keine daten fuer id: " + id + "in Tabelle" + theTable);
271                                 rs.close();
272                         }       else {
273         theLog.printDebugInfo("No Data for Id " + id + " in Table " + theTable);
274       }
275                 } catch (SQLException sqe){
276       throwSQLException(sqe,"selectById"); return null;
277     } catch (NumberFormatException e) {
278       theLog.printError("ID ist keine Zahl: " + id);
279     } finally {
280       freeConnection(con,stmt);
281     }
282
283                 return returnEntity;
284         }
285
286         /**
287          *   select-Operator um Datensaetze zu bekommen, die key = value erfuellen.
288          *   @param key  Datenbankfeld der Bedingung.
289          *   @param value  Wert die der key anehmen muss.
290          *   @return EntityList mit den gematchten Entities
291          */
292
293         public EntityList selectByFieldValue(String aField, String aValue)
294                 throws StorageObjectException {
295
296                 return selectByFieldValue(aField, aValue, 0);
297         }
298
299         /**
300          *   select-Operator um Datensaetze zu bekommen, die key = value erfuellen.
301          *   @param key  Datenbankfeld der Bedingung.
302          *   @param value  Wert die der key anehmen muss.
303          *   @param offset  Gibt an ab welchem Datensatz angezeigt werden soll.
304          *   @return EntityList mit den gematchten Entities
305          */
306
307         public EntityList selectByFieldValue(String aField, String aValue, int offset)
308                 throws StorageObjectException   {
309
310                 return selectByWhereClause(aField + "=" + aValue, offset);
311         }
312
313
314         /**
315          * select-Operator liefert eine EntityListe mit den gematchten Datensätzen zurück.
316          * Also offset wird der erste Datensatz genommen.
317          *
318          * @param wc where-Clause
319          * @return EntityList mit den gematchten Entities
320          * @exception StorageObjectException
321          */
322         public EntityList selectByWhereClause(String where)
323                 throws StorageObjectException   {
324
325                 return selectByWhereClause(where, 0);
326         }
327
328
329         /**
330          * select-Operator liefert eine EntityListe mit den gematchten Datensätzen zurück.
331          * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.
332          *
333          * @param wc where-Clause
334          * @param offset ab welchem Datensatz.
335          * @return EntityList mit den gematchten Entities
336          * @exception StorageObjectException
337          */
338         public EntityList selectByWhereClause(String whereClause, int offset)
339                 throws StorageObjectException   {
340
341                 return selectByWhereClause(whereClause, null, offset);
342         }
343
344         /**
345          * select-Operator liefert eine EntityListe mit den gematchten Datensätzen zurück.
346          * Also offset wird der erste Datensatz genommen.
347          * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.
348          *
349          * @param wc where-Clause
350          * @param ob orderBy-Clause
351          * @return EntityList mit den gematchten Entities
352          * @exception StorageObjectException
353          */
354
355         public EntityList selectByWhereClause(String where, String order)
356                 throws StorageObjectException   {
357
358                 return selectByWhereClause(where, order, 0);
359         }
360         /**
361          * select-Operator liefert eine EntityListe mit den gematchten Datensätzen zurück.
362          * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.
363          *
364          * @param wc where-Clause
365          * @param ob orderBy-Clause
366          * @param offset ab welchem Datensatz
367          * @return EntityList mit den gematchten Entities
368          * @exception StorageObjectException
369          */
370
371         public EntityList selectByWhereClause(String whereClause, String orderBy, int offset)
372                 throws StorageObjectException   {
373
374                 return selectByWhereClause(whereClause, orderBy, offset, defaultLimit);
375         }
376
377
378         /**
379          * select-Operator liefert eine EntityListe mit den gematchten Datensätzen zurück.
380          * @param wc where-Clause
381          * @param ob orderBy-Clause
382          * @param offset ab welchem Datensatz
383          * @param limit wieviele Datensätze
384          * @return EntityList mit den gematchten Entities
385          * @exception StorageObjectException
386          */
387
388         public EntityList selectByWhereClause(String wc, String ob, int offset, int limit)
389                 throws StorageObjectException   {
390
391                 // local
392                 EntityList    theReturnList=null;
393                 Connection    con=null;
394                 Statement     stmt=null;
395                 ResultSet     rs;
396                 int               offsetCount = 0;
397                 int           count=0;
398
399
400                 // build sql-statement
401                 if (wc != null && wc.length() == 0) {
402                         wc = null;
403                 }
404                 StringBuffer countSql = new StringBuffer("select count(*) from ").append(theTable);
405                 StringBuffer selectSql = new StringBuffer("select * from ").append(theTable);
406                 if (wc != null) {
407                         selectSql.append(" where ").append(wc);
408                         countSql.append(" where ").append(wc);
409                 }
410                 if (ob != null && !(ob.length() == 0)) {
411                         selectSql.append(" order by ").append(ob);
412                 }
413                 if (theAdaptor.hasLimit()) {
414                         if (limit > -1 && offset > -1) {
415                                 selectSql.append(" limit ");
416                                 if (theAdaptor.reverseLimit()) {
417                                         selectSql.append(limit).append(",").append(offset);
418                                 }
419                                 else {
420                                         selectSql.append(offset).append(",").append(limit);
421                                 }
422                         }
423                 }
424
425                         // execute sql
426                 try {
427                         con = getPooledCon();
428                         stmt = con.createStatement();
429                         // counting rows
430                         if (theAdaptor.hasLimit()) {
431                                 rs = executeSql(stmt, countSql.toString());
432                                 if (rs != null) {
433                                         if (rs.next())
434                                                 count = rs.getInt(1);
435                                         rs.close();
436                                 }
437                                 else
438                                         theLog.printError("Mh. Konnte nicht zaehlen: " + countSql);
439                         }
440                         // hier select
441                         rs = executeSql(stmt, selectSql.toString());
442                         if (rs != null) {
443                                 theReturnList = new EntityList();
444                                 if (evaluatedMetaData == false) {
445                                         evalMetaData(rs.getMetaData());
446                                 }
447                                 Entity theResultEntity;
448                                 while (rs.next()) {
449                                         theResultEntity = makeEntityFromResultSet(rs);
450                                         theReturnList.add(theResultEntity);
451                                         offsetCount++;
452                                 }
453                                 rs.close();
454                         }
455                         // making entitylist
456                         if (!(theAdaptor.hasLimit()))
457                                 count = offsetCount;
458                         if (theReturnList != null) {
459                                 theReturnList.setCount(count);
460                                 theReturnList.setOffset(offset);
461                                 theReturnList.setWhere(wc);
462                                 theReturnList.setOrder(ob);
463                                 if (offset >= limit) {
464                                         theReturnList.setPrevBatch(offset - limit);
465                                 }
466                                 if (offset + offsetCount < count) {
467                                         theReturnList.setNextBatch(offset + limit);
468                                 }
469                         }
470                 } catch (SQLException sqe) {
471                         throwSQLException(sqe, "selectByWhereClause");
472                 } finally {
473                         freeConnection(con, stmt);
474                 }
475                 return  theReturnList;
476         }
477
478         /**
479          *  Bastelt aus einer Zeile der Datenbank ein EntityObjekt.
480          *
481          *  @param rs Das ResultSetObjekt.
482          *  @return Entity Die Entity.
483          */
484
485         public Entity makeEntityFromResultSet (ResultSet rs) throws StorageObjectException {
486                 HashMap theResultHash = new HashMap();
487                 String theResult = null;
488                 int theType;
489                 Entity returnEntity = null;
490                 try {
491                         int size = metadataFields.size();
492                         for (int i = 0; i < size; i++) {
493                                 // alle durchlaufen bis nix mehr da
494                                 theType = metadataTypes[i];
495                                 if (theType == java.sql.Types.LONGVARBINARY) {
496                                         InputStream us = rs.getAsciiStream(i + 1);
497                                         if (us != null) {
498                                                 InputStreamReader is = new InputStreamReader(us);
499                                                 char[] data = new char[32768];
500                                                 StringBuffer theResultString = new StringBuffer();
501                                                 int len;
502                                                 while ((len = is.read(data)) > 0) {
503                                                         theResultString.append(data, 0, len);
504                                                 }
505                                                 is.close();
506                                                 theResult = theResultString.toString();
507                                         }
508                                         else {
509                                                 theResult = null;
510                                         }
511                                 }
512                                 else {
513                                         theResult = getValueAsString(rs, (i + 1), theType);
514                                 }
515                                 if (theResult != null) {
516                                         theResultHash.put(metadataFields.get(i), theResult);
517                                 }
518                         }
519                         if (cache != null && theResultHash.containsKey(thePKeyName) && cache.containsKey((String)theResultHash.get(thePKeyName))) {
520                                 //theLog.printDebugInfo("CACHE: (out) "+ theResultHash.get(thePKeyName)+ " :"+theTable);
521                                 returnEntity = (Entity)cache.get((String)theResultHash.get(thePKeyName));
522                         }
523                         else {
524                                 if (theEntityClass != null) {
525                                         returnEntity = (Entity)theEntityClass.newInstance();
526                                         returnEntity.setValues(theResultHash);
527                                         returnEntity.setStorage(myselfDatabase);
528                                         if (cache != null) {
529                                                 //theLog.printDebugInfo("CACHE: ( in) " + returnEntity.getId() + " :"+theTable);
530                                                 cache.put(returnEntity.getId(), returnEntity);
531                                         }
532                                 }
533                                 else {
534                                         throwStorageObjectException("Interner Fehler theEntityClass nicht gesetzt!");
535                                 }
536                         }
537                 }           // try
538                 catch (IllegalAccessException e) {
539                         throwStorageObjectException("Kein Zugriff! -- " + e.toString());
540                 } catch (IOException e) {
541                         throwStorageObjectException("IOException! -- " + e.toString());
542                 } catch (InstantiationException e) {
543                         throwStorageObjectException("Keine Instantiiierung! -- " + e.toString());
544                 } catch (SQLException sqe) {
545                         throwSQLException(sqe, "makeEntityFromResultSet");
546                         return  null;
547                 }
548                 return  returnEntity;
549         }
550
551         /**
552          * insert-Operator: fügt eine Entity in die Tabelle ein. Eine Spalte WEBDB_CREATE
553          * wird automatisch mit dem aktuellen Datum gefuellt.
554          *
555          * @param theEntity
556          * @return der Wert des Primary-keys der eingefügten Entity
557          */
558         public String insert (Entity theEntity) throws StorageObjectException {
559                 String returnId = "0";
560                 Connection con = null;
561                 PreparedStatement pstmt = null;
562                 //cache
563                 invalidatePopupCache();
564                 try {
565                         HashMap theEntityValues = theEntity.getValues();
566                         ArrayList streamedInput = theEntity.streamedInput();
567                         StringBuffer f = new StringBuffer();
568                         StringBuffer v = new StringBuffer();
569                         String aField, aValue;
570                         boolean firstField = true;
571                         // make sql-string
572                         for (int i = 0; i < getFields().size(); i++) {
573                                 aField = (String)getFields().get(i);
574                                 if (!aField.equals(thePKeyName)) {
575                                         aValue = null;
576                                         // sonderfaelle
577                                         if (aField.equals("webdb_create")) {
578                                                 aValue = "NOW()";
579                                         }
580                                         else {
581                                                 if (streamedInput != null && streamedInput.contains(aField)) {
582                                                         aValue = "?";
583                                                 }
584                                                 else {
585                                                         if (theEntityValues.containsKey(aField)) {
586                                                                 aValue = "'" + StringUtil.quote((String)theEntityValues.get(aField))
587                                                                                 + "'";
588                                                         }
589                                                 }
590                                         }
591                                         // wenn Wert gegeben, dann einbauen
592                                         if (aValue != null) {
593                                                 if (firstField == false) {
594                                                         f.append(",");
595                                                         v.append(",");
596                                                 }
597                                                 else {
598                                                         firstField = false;
599                                                 }
600                                                 f.append(aField);
601                                                 v.append(aValue);
602                                         }
603                                 }
604                         }         // end for
605                         // insert into db
606                         StringBuffer sqlBuf = new StringBuffer("insert into ").append(theTable).append("(").append(f).append(") values (").append(v).append(")");
607                         String sql = sqlBuf.toString();
608                         theLog.printInfo("INSERT: " + sql);
609                         con = getPooledCon();
610                         con.setAutoCommit(false);
611                         pstmt = con.prepareStatement(sql);
612                         if (streamedInput != null) {
613                                 for (int i = 0; i < streamedInput.size(); i++) {
614                                         String inputString = (String)theEntityValues.get(streamedInput.get(i));
615                                         pstmt.setBytes(i + 1, inputString.getBytes());
616                                 }
617                         }
618                         pstmt.execute();
619                         pstmt = con.prepareStatement(theAdaptor.getLastInsertSQL((Database)myselfDatabase));
620                         ResultSet rs = pstmt.executeQuery();
621                         rs.next();
622                         returnId = rs.getString(1);
623                         theEntity.setId(returnId);
624                 } catch (SQLException sqe) {
625                         throwSQLException(sqe, "insert");
626                 } finally {
627                         try {
628                                 con.setAutoCommit(true);
629                         } catch (Exception e) {
630                                 ;
631                         }
632                         freeConnection(con, pstmt);
633                 }
634                 return  returnId;
635         }
636
637         /**
638          * update-Operator: aktualisiert eine Entity. Eine Spalte WEBDB_LASTCHANGE
639          * wird automatisch mit dem aktuellen Datum gefuellt.
640          *
641          * @param theEntity
642          */
643         public void update (Entity theEntity) throws StorageObjectException {
644                 Connection con = null;
645                 PreparedStatement pstmt = null;
646                 ArrayList streamedInput = theEntity.streamedInput();
647                 HashMap theEntityValues = theEntity.getValues();
648                 String id = theEntity.getId();
649                 String aField;
650                 StringBuffer fv = new StringBuffer();
651                 boolean firstField = true;
652                 //cache
653                 invalidatePopupCache();
654                 // build sql statement
655                 for (int i = 0; i < getFields().size(); i++) {
656                         aField = (String)metadataFields.get(i);
657                         // only normal cases
658                         if (!(aField.equals(thePKeyName) || aField.equals("webdb_create") ||
659                                         aField.equals("webdb_lastchange") || (streamedInput != null && streamedInput.contains(aField)))) {
660                                 if (theEntityValues.containsKey(aField)) {
661                                         if (firstField == false) {
662                                                 fv.append(", ");
663                                         }
664                                         else {
665                                                 firstField = false;
666                                         }
667                                         fv.append(aField).append("='").append(StringUtil.quote((String)theEntityValues.get(aField))).append("'");
668                                 }
669                         }
670                 }
671                 StringBuffer sql = new StringBuffer("update ").append(theTable).append(" set ").append(fv);
672                 // exceptions
673                 if (metadataFields.contains("webdb_lastchange")) {
674                         sql.append(",webdb_lastchange=NOW()");
675                 }
676                 if (streamedInput != null) {
677                         for (int i = 0; i < streamedInput.size(); i++) {
678                                 sql.append(",").append(streamedInput.get(i)).append("=?");
679                         }
680                 }
681                 sql.append(" where id=").append(id);
682                 theLog.printInfo("UPDATE: " + sql);
683                 // execute sql
684                 try {
685                         con = getPooledCon();
686                         con.setAutoCommit(false);
687                         pstmt = con.prepareStatement(sql.toString());
688                         if (streamedInput != null) {
689                                 for (int i = 0; i < streamedInput.size(); i++) {
690                                         String inputString = (String)theEntityValues.get(streamedInput.get(i));
691                                         pstmt.setBytes(i + 1, inputString.getBytes());
692                                 }
693                         }
694                         pstmt.executeUpdate();
695                 } catch (SQLException sqe) {
696                         throwSQLException(sqe, "update");
697                 } finally {
698                         try {
699                                 con.setAutoCommit(true);
700                         } catch (Exception e) {
701                                 ;
702                         }
703                         freeConnection(con, pstmt);
704                 }
705         }
706
707         /*
708          *   delete-Operator
709          *   @param id des zu loeschenden Datensatzes
710          *   @return boolean liefert true zurueck, wenn loeschen erfolgreich war.
711          */
712         public boolean delete (String id) throws StorageObjectException {
713                 Statement stmt = null;
714                 Connection con = null;
715                 String sql;
716                 int res = 0;
717                 // loeschen des caches
718                 invalidatePopupCache();
719                 sql = "delete from " + theTable + " where " + thePKeyName + "='" + id +
720                                 "'";
721                 theLog.printInfo("DELETE " + sql);
722                 try {
723                         con = getPooledCon();
724                         stmt = con.createStatement();
725                         res = stmt.executeUpdate(sql);
726                 } catch (SQLException sqe) {
727                         throwSQLException(sqe, "delete");
728                 } finally {
729                         freeConnection(con, stmt);
730                 }
731                 if (cache != null) {
732                         theLog.printInfo("CACHE: deleted " + id);
733                         cache.remove(id);
734                 }
735                 return  (res > 0) ? true : false;
736         }
737
738         /* noch nicht implementiert.
739          * @return immer false
740          */
741         public boolean delete (EntityList theEntityList) {
742                 invalidatePopupCache();
743                 return  false;
744         }
745
746         /**
747          * Diese Methode sollte ueberschrieben werden, wenn fuer die abgeleitete Database-Klasse
748          * eine SimpleList mit Standard-Popupdaten erzeugt werden koennen soll.
749          * @return null
750          */
751         public SimpleList getPopupData () {
752                 return  null;
753         }
754
755         /**
756          *  Holt Daten fuer Popups.
757          *  @param name  Name des Feldes.
758          *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.
759          *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.
760          */
761         public SimpleList getPopupData (String name, boolean hasNullValue) {
762                 return  getPopupData(name, hasNullValue, null);
763         }
764
765         /**
766          *  Holt Daten fuer Popups.
767          *  @param name  Name des Feldes.
768          *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.
769          *  @param where  Schraenkt die Selektion der Datensaetze ein.
770          *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.
771          */
772         public SimpleList getPopupData (String name, boolean hasNullValue, String where) {
773                 return  getPopupData(name, hasNullValue, where, null);
774         }
775
776         /**
777          *  Holt Daten fuer Popups.
778          *  @param name  Name des Feldes.
779          *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.
780          *  @param where  Schraenkt die Selektion der Datensaetze ein.
781          *  @param order  Gibt ein Feld als Sortierkriterium an.
782          *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.
783          */
784         public SimpleList getPopupData (String name, boolean hasNullValue, String where,
785                         String order) {
786                 // caching
787                 if (hasPopupCache && popupCache != null)
788                         return  popupCache;
789                 SimpleList simpleList = null;
790                 Connection con = null;
791                 Statement stmt = null;
792                 // build sql
793                 StringBuffer sql = new StringBuffer("select ").append(thePKeyName).append(",").append(name).append(" from ").append(theTable);
794                 if (where != null && !(where.length() == 0))
795                         sql.append(" where ").append(where);
796                 sql.append(" order by ");
797                 if (order != null && !(order.length() == 0))
798                         sql.append(order);
799                 else
800                         sql.append(name);
801                 // execute sql
802                 try {
803                         con = getPooledCon();
804                         stmt = con.createStatement();
805                         ResultSet rs = executeSql(stmt, sql.toString());
806                         if (rs != null) {
807                                 if (evaluatedMetaData == false)
808                                         get_meta_data();
809                                 simpleList = new SimpleList();
810                                 SimpleHash popupDict;
811                                 if (hasNullValue) {
812                                         popupDict = new SimpleHash();
813                                         popupDict.put("key", "");
814                                         popupDict.put("value", "--");
815                                         simpleList.add(popupDict);
816                                 }
817                                 while (rs.next()) {
818                                         popupDict = new SimpleHash();
819                                         popupDict.put("key", getValueAsString(rs, 1, thePKeyType));
820                                         popupDict.put("value", rs.getString(2));
821                                         simpleList.add(popupDict);
822                                 }
823                                 rs.close();
824                         }
825                 } catch (Exception e) {
826                         theLog.printDebugInfo(e.toString());
827                 } finally {
828                         freeConnection(con, stmt);
829                 }
830                 if (hasPopupCache) {
831                         popupCache = simpleList;
832                 }
833                 return  simpleList;
834         }
835
836         /**
837          * Liefert alle Daten der Tabelle als SimpleHash zurueck. Dies wird verwandt,
838          * wenn in den Templates ein Lookup-Table benoetigt wird. Sollte nur bei kleinen
839          * Tabellen Verwendung finden.
840          * @return SimpleHash mit den Tabellezeilen.
841          */
842         public SimpleHash getHashData () {
843                 if (hashCache == null) {
844                         try {
845                                 hashCache = HTMLTemplateProcessor.makeSimpleHash(selectByWhereClause("",
846                                                 -1));
847                         } catch (StorageObjectException e) {
848                                 theLog.printDebugInfo(e.toString());
849                         }
850                 }
851                 return  hashCache;
852         }
853
854         /* invalidates the popupCache
855          */
856         private void invalidatePopupCache () {
857
858                 /** @todo  invalidates toooo much */
859                 popupCache = null;
860                 hashCache = null;
861         }
862
863         /**
864          * Diese Methode fuehrt den Sqlstring <i>sql</i> aus und timed im Logfile.
865          * @param stmt Statemnt
866          * @param sql Sql-String
867          * @return ResultSet
868          * @exception StorageObjectException, SQLException
869          */
870         public ResultSet executeSql (Statement stmt, String sql) throws StorageObjectException,
871                         SQLException {
872                 long startTime = (new java.util.Date()).getTime();
873                 ResultSet rs = stmt.executeQuery(sql);
874                 theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms. for: "
875                                 + sql);
876                 return  rs;
877         }
878
879         /**
880          * Fuehrt Statement stmt aus und liefert Resultset zurueck. Das SQL-Statment wird
881          * getimed und geloggt.
882          * @param stmt PreparedStatement mit der SQL-Anweisung
883          * @return Liefert ResultSet des Statements zurueck.
884          * @exception StorageObjectException, SQLException
885          */
886         public ResultSet executeSql (PreparedStatement stmt) throws StorageObjectException,
887                         SQLException {
888                 long startTime = (new java.util.Date()).getTime();
889                 ResultSet rs = stmt.executeQuery();
890                 theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms.");
891                 return  rs;
892         }
893
894         /**
895          * returns the number of rows in the table
896          */
897         public int getSize(String where)
898                 throws SQLException,StorageObjectException
899         {
900                 long  startTime = (new java.util.Date()).getTime();
901                 String sql = "SELECT count(*) FROM "+ theTable + " where " + where;
902                 //theLog.printDebugInfo("trying: "+ sql);
903                 Connection con = null;
904                 Statement stmt = null;
905                 int result = 0;
906
907                 try {
908                         con = getPooledCon();
909                         stmt = con.createStatement();
910                         ResultSet rs = executeSql(stmt,sql);
911                         while(rs.next()){
912                                 result = rs.getInt(1);
913                         }
914                 } catch (SQLException e) {
915                         theLog.printError(e.toString());
916                 } finally {
917                         freeConnection(con,stmt);
918                 }
919                 theLog.printInfo(theTable + " has "+ result +" rows where " + where);
920                 theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms. for: " + sql);
921                 return result;
922         }
923
924         public int executeUpdate(Statement stmt, String sql)
925                 throws StorageObjectException, SQLException
926         {
927                 long  startTime = (new java.util.Date()).getTime();
928                 //theLog.printDebugInfo("trying: "+ sql);
929                 int rs = stmt.executeUpdate(sql);
930                 theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms. for: " + sql);
931                 return rs;
932         }
933
934         public int executeUpdate(String sql)
935                 throws StorageObjectException, SQLException
936         {
937                 int result=-1;
938                 long  startTime = (new java.util.Date()).getTime();
939                 Connection con=null;PreparedStatement pstmt=null;
940                 try {
941                         con=getPooledCon();
942                         pstmt = con.prepareStatement(sql);
943                         result = pstmt.executeUpdate();
944                 }
945                 catch (Exception e) {theLog.printDebugInfo("settimage :: setImage gescheitert: "+e.toString());}
946                 finally {       freeConnection(con,pstmt); }
947                 theLog.printInfo((new java.util.Date().getTime() - startTime) + "ms. for: " + sql);
948                 return result;
949         }
950
951         /**
952          * Wertet ResultSetMetaData aus und setzt interne Daten entsprechend
953          * @param md ResultSetMetaData
954          * @exception StorageObjectException
955          */
956         private void evalMetaData (ResultSetMetaData md) throws StorageObjectException {
957                 this.evaluatedMetaData = true;
958                 this.metadataFields = new ArrayList();
959                 this.metadataLabels = new ArrayList();
960                 this.metadataNotNullFields = new ArrayList();
961                 try {
962                         int numFields = md.getColumnCount();
963                         this.metadataTypes = new int[numFields];
964                         String aField;
965                         int aType;
966                         for (int i = 1; i <= numFields; i++) {
967                                 aField = md.getColumnName(i);
968                                 metadataFields.add(aField);
969                                 metadataLabels.add(md.getColumnLabel(i));
970                                 aType = md.getColumnType(i);
971                                 metadataTypes[i - 1] = aType;
972                                 if (aField.equals(thePKeyName)) {
973                                         thePKeyType = aType;
974                                 }
975                                 if (md.isNullable(i) == md.columnNullable) {
976                                         metadataNotNullFields.add(aField);
977                                 }
978                         }
979                 } catch (SQLException e) {
980                         throwSQLException(e, "evalMetaData");
981                 }
982         }
983
984         /**
985          *  Wertet die Metadaten eines Resultsets fuer eine Tabelle aus,
986          *  um die alle Columns und Typen einer Tabelle zu ermitteln.
987          */
988         private void get_meta_data () throws StorageObjectException {
989                 Connection con = null;
990                 PreparedStatement pstmt = null;
991                 String sql = "select * from " + theTable + " where 0=1";
992                 try {
993                         con = getPooledCon();
994                         pstmt = con.prepareStatement(sql);
995                         theLog.printInfo("METADATA: " + sql);
996                         ResultSet rs = pstmt.executeQuery();
997                         evalMetaData(rs.getMetaData());
998                         rs.close();
999                 } catch (SQLException e) {
1000                         throwSQLException(e, "get_meta_data");
1001                 } finally {
1002                         freeConnection(con, pstmt);
1003                 }
1004         }
1005
1006         /**
1007          * Datenbankverbindung wird geschlossen
1008          */
1009         public void disconnectPool () {
1010                 try {
1011                         myBroker.destroy(100);
1012                 } catch (SQLException sqe) {
1013                         ;
1014                 }
1015         }
1016
1017         /**
1018          * Liefert ein Connection-Objekt aus dem ConnectionPool.
1019          * @return Connection Objekt.
1020          */
1021         public Connection getPooledCon () throws StorageObjectException {
1022                 if (myBroker != null) {
1023                         Connection con = myBroker.getConnection();
1024                         if (con != null)
1025                                 return  con;
1026                 }
1027                 throw  new StorageObjectException("KEINE VERBINDUNG ZUR DATENBANK");
1028         }
1029
1030         /**
1031          * Connection und StatementObjekt werden geschlossen und an den Connectionpool
1032          * zurückgeben
1033          * @param con Connection zur Datenbank
1034          * @param stmt Statement-Objekt
1035          */
1036         public void freeConnection (Connection con, Statement stmt) {
1037                 try {
1038                         if (stmt != null)
1039                                 stmt.close();
1040                 } catch (SQLException e1) {
1041                         theLog.printDebugInfo(e1.toString());
1042                 }
1043                 if (con != null)
1044                         myBroker.freeConnection(con);
1045                 else
1046                         theLog.printDebugInfo("Con was null!");
1047         }
1048
1049         /**
1050          * Wertet SQLException aus und wirft dannach eine StorageObjectException
1051          * @param sqe SQLException
1052          * @param wo Funktonsname, in der die SQLException geworfen wurde
1053          * @exception StorageObjectException
1054          */
1055         void throwSQLException (SQLException sqe, String wo) throws StorageObjectException {
1056                 String state = "";
1057                 String message = "";
1058                 int vendor = 0;
1059                 if (sqe != null) {
1060                         state = sqe.getSQLState();
1061                         message = sqe.getMessage();
1062                         vendor = sqe.getErrorCode();
1063                 }
1064                 theLog.printError(state + ": " + vendor + " : " + message + " Funktion: "
1065                                 + wo);
1066                 throw  new StorageObjectException((sqe == null) ? "undefined sql exception" :
1067                                 sqe.toString());
1068         }
1069
1070         /**
1071          * Loggt Fehlermeldung mit dem Parameter Message und wirft dannach eine StorageObjectException
1072          * @param message Nachricht mit dem Fehler
1073          * @exception StorageObjectException
1074          */
1075         void throwStorageObjectException (String message) throws StorageObjectException {
1076                 theLog.printError(message);
1077                 throw  new StorageObjectException(message);
1078         }
1079 }
1080
1081
1082