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