ulrich
2024-01-22 a59fcaadb5c928a243f198c91d69313234e07afe
src/de/uhilger/baselink/PersistenceManager.java
@@ -17,19 +17,14 @@
 */
package de.uhilger.baselink;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
 import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -121,6 +116,7 @@
   * @throws NamingException  when JNDI lookup fails
   */
  public void setDataSourceName(String dataSourceName) throws NamingException {
    logger.finest(dataSourceName);
    Context initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");
    ds = (DataSource) envCtx.lookup(dataSourceName);
@@ -129,20 +125,23 @@
  /**
   * Get a database connection
   * @return  a database connection
   * @throws SQLException
   */
  public Connection getConnection() {
    Connection c = null;
    try {
      if (dburl != null) {
        logger.finest("getting Connection for URL " + dburl);
        c = DriverManager.getConnection(dburl);
      } else if(ds != null) {
        logger.finest("getting Connection for DataSource " + ds.toString());
        c = ds.getConnection();
      } else {
        //throw new SQLException("Unable to get connection, DataSource and database URL are null.");
        logger.finest("Unable to get connection, DataSource and database URL are null.");
      }
    } catch(Exception ex) {
      ex.printStackTrace();
      //ex.printStackTrace();
      logger.log(Level.FINEST, ex.getLocalizedMessage(), ex);
    }
    return c;
  }
@@ -162,7 +161,7 @@
   * @return  the deleted object
   */
  public Object delete(Object o) {
    return delete(o, new GenericRecord(o.getClass()));
    return new Eraser(this).delete(o, new GenericRecord(o.getClass()));
  }
  /**
@@ -179,7 +178,7 @@
   * @return  the deleted object
   */
  public Object delete(Connection c, Object o) {
    return delete(c, o, new GenericRecord(o.getClass()));
    return new Eraser(this).delete(c, o, new GenericRecord(o.getClass()));
  }
  /**
@@ -189,18 +188,7 @@
   * @return  the deleted object
   */
  public Object delete(Object o, Record record) {
    Connection c = null;
    try {
      c = getConnection();
      o = delete(c, o, record);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return o;
    return new Eraser(this).delete(o, record);
  }
  /**
@@ -211,20 +199,7 @@
   * @return the deleted object
   */
  public Object delete(Connection c, Object o, Record record) {
    Object deletedObject = null;
    PreparedStatement ps = null;
    try {
      ps = record.getDeleteStatment(c, o);
      ps.executeUpdate();
      ps.close();
      ps = null;
      deletedObject = o;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(ps);
    }
    return deletedObject;
    return new Eraser(this).delete(c, o, record);
  }
  
  /* ---------------------- inserts -------------------- */
@@ -242,7 +217,7 @@
   * @return  the added object
   */
  public Object insert(Object o) {
    return insert(o, new GenericRecord(o.getClass()));
    return new Inserter(this).insert(o, new GenericRecord(o.getClass()));
  }
  /**
@@ -259,7 +234,7 @@
   * @return  the object added to the database
   */
  public Object insert(Connection c, Object o) {
    return insert(c, o, new GenericRecord(o.getClass()));
    return new Inserter(this).insert(c, o, new GenericRecord(o.getClass()));
  }
  /**
@@ -269,18 +244,7 @@
   * @return  the added object
   */
  public Object insert(Object o, Record record) {
    Connection c = null;
    try {
      c = getConnection();
      o = insert(c, o, record);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return o;
    return new Inserter(this).insert(o, record);
  }
  /**
@@ -291,20 +255,7 @@
   * @return  the object that was added
   */
  public Object insert(Connection c, Object o, Record record) {
    Object addedObject = null;
    PreparedStatement ps = null;
    try {
      ps = record.getInsertStatment(c, o);
      ps.executeUpdate();
      ps.close();
      ps = null;
      addedObject = o;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(ps);
    }
    return addedObject;
    return new Inserter(this).insert(c, o, record);
  }
  
  /* --------------------------------- updates --------------------- */
@@ -322,7 +273,7 @@
   * @return  the object that was updated
   */
  public Object update(Object o) {
    return update(o, new GenericRecord(o.getClass()));
    return new Updater(this).update(o, new GenericRecord(o.getClass()));
  }
  /**
@@ -339,7 +290,7 @@
   * @return  the object that was updated
   */
  public Object update(Connection c, Object o) {
    return update(c, o, new GenericRecord(o.getClass()));
    return new Updater(this).update(c, o, new GenericRecord(o.getClass()));
  }
  /**
@@ -349,18 +300,7 @@
   * @return  the object that was updated
   */
  public Object update(Object o, Record record) {
    Connection c = null;
    try {
      c = getConnection();
      o = update(c, o, record);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return o;
    return new Updater(this).update(o, record);
  }
  /**
@@ -371,20 +311,7 @@
   * @return  the object that was updated
   */
  public Object update(Connection c, Object o, Record record) {
    Object updatedObject = null;
    PreparedStatement ps = null;
    try {
      ps = record.getUpdateStatment(c, o);
      ps.executeUpdate();
      ps.close();
      ps = null;
      updatedObject = o;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(ps);
    }
    return updatedObject;
    return new Updater(this).update(c, o, record);
  }
  
  /* --------------- selects ---------------- */
@@ -396,7 +323,7 @@
   * @return a list of objects that match the given query
   */
  public List<Object> select(String sql, Record record) {
    return select(sql, record, Record.WITH_BLOBS);
    return new Selector(this).select(sql, record, Record.WITH_BLOBS);
  }
  /**
@@ -407,19 +334,7 @@
   * @return  a list of objects that match the given query
   */
  public List<Object> select(String sql, Record record, boolean includeBlobs) {
    Connection c = null;
    ArrayList<Object> list = new ArrayList<Object>();
    try {
      c = getConnection();
      list = (ArrayList<Object>) select(c, sql, record, includeBlobs);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return list;
    return new Selector(this).select(sql, record, includeBlobs);
  }
  /**
@@ -431,19 +346,7 @@
   * @return  a list of objects that match the given query
   */
  public List<Object> select(String sql, Record record, boolean includeBlobs, Object... params) {
    Connection c = null;
    ArrayList<Object> list = new ArrayList<Object>();
    try {
      c = getConnection();
      list = (ArrayList<Object>) select(c, sql, record, includeBlobs, params);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return list;
    return new Selector(this).select(sql, record, includeBlobs, params);
  }
  /**
@@ -455,29 +358,7 @@
   * @return  a list of objects that match the given query
   */
  public List<Object> select(Connection c, String sql, Record record, boolean includeBlobs) {
    PreparedStatement ps = null;
    ResultSet rs = null;
    ArrayList<Object> list = new ArrayList<Object>();
    try {
      ps = c.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
      rs = ps.executeQuery();
      if (rs.first()) {
        while (!rs.isAfterLast()) {
          list.add(record.toObject(rs, includeBlobs));
          rs.next();
        }
      }
      rs.close();
      rs = null;
      ps.close();
      ps = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeResultSetFinally(rs);
      closeStatementFinally(ps);
    }
    return list;
    return new Selector(this).select(c, sql, record, includeBlobs);
  }
  /**
@@ -490,32 +371,8 @@
   * @return  a list of objects that match the given query
   */
  public List<Object> select(Connection c, String sql, Record record, boolean includeBlobs, Object... params) {
    PreparedStatement ps = null;
    ResultSet rs = null;
    ArrayList<Object> list = new ArrayList<Object>();
    try {
      //ps = c.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
      ps = buildQuery(c, sql, params);
      rs = ps.executeQuery();
      if (rs.first()) {
        while (!rs.isAfterLast()) {
          list.add(record.toObject(rs, includeBlobs));
          rs.next();
        }
      }
      rs.close();
      rs = null;
      ps.close();
      ps = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeResultSetFinally(rs);
      closeStatementFinally(ps);
    }
    return list;
    return new Selector(this).select(c, sql, record, includeBlobs, params);
  }
  /**
   * Select a list of objects that match a given SQL statement
@@ -524,20 +381,7 @@
   * list can be accessed with list.get(recordno).get("fieldname")
   */
  public List<Map<String, Object>> select(String sql) {
    //Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).finest(sql);
    Connection c = null;
    List<Map<String, Object>> list = null;
    try {
      c = getConnection();
      list = select(c, sql);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return list;
    return new Selector(this).select(sql);
  }
  /**
@@ -548,19 +392,7 @@
   * list can be accessed with list.get(recordno).get(fieldno), each element is of type String
   */
  public List<List<String>> select(String sql, boolean includeBlobs) {
    Connection c = null;
    List<List<String>> list = null;
    try {
      c = getConnection();
      list = select(c, sql, includeBlobs);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return list;
    return new Selector(this).select(sql, includeBlobs);
  }
  /**
@@ -571,24 +403,7 @@
   * list can be accessed with list.get(recordno).get("fieldname")
   */
  public List<Map<String, Object>> select(Connection c, String sql) {
    PreparedStatement ps = null;
    ResultSet rs = null;
    List<Map<String, Object>> list = null;
    try {
      ps = c.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
      rs = ps.executeQuery();
      list = toList(rs);
      rs.close();
      rs = null;
      ps.close();
      ps = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeResultSetFinally(rs);
      closeStatementFinally(ps);
    }
    return list;
    return new Selector(this).select(c, sql);
  }
  /**
@@ -600,24 +415,7 @@
   * list can be accessed with list.get(recordno).get(fieldno), each element is of type String
   */
  public List<List<String>> select(Connection c, String sql, boolean includeBlobs) {
    PreparedStatement ps = null;
    ResultSet rs = null;
    List<List<String>> list = null;
    try {
      ps = c.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
      rs = ps.executeQuery();
      list = toList(rs, includeBlobs);
      rs.close();
      rs = null;
      ps.close();
      ps = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeResultSetFinally(rs);
      closeStatementFinally(ps);
    }
    return list;
    return new Selector(this).select(c, sql, includeBlobs);
  }
  /**
@@ -629,19 +427,7 @@
   * list can be accessed with list.get(recordno).get(fieldno), each element is of type String
   */
  public List<List<String>> select(String sql, boolean includeBlobs, Object... params) {
    Connection c = null;
    List<List<String>> list = null;
    try {
      c = getConnection();
      list = select(c, sql, includeBlobs, params);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return list;
    return new Selector(this).select(sql, includeBlobs, params);
  }
  
  /**
@@ -654,24 +440,7 @@
   * list can be accessed with list.get(recordno).get(fieldno), each element is of type String
   */
  public List<List<String>> select(Connection c, String sql, boolean includeBlobs, Object... params) {
    PreparedStatement ps = null;
    ResultSet rs = null;
    List<List<String>> list = null;
    try {
      ps = buildQuery(c, sql, params);
      rs = ps.executeQuery();
      list = toList(rs, includeBlobs);
      rs.close();
      rs = null;
      ps.close();
      ps = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeResultSetFinally(rs);
      closeStatementFinally(ps);
    }
    return list;
    return new Selector(this).select(c, sql, includeBlobs, params);
  }
  
  /* ------------------ generic SQL execution ---------- */
@@ -682,19 +451,7 @@
   * @return  a list of generated keys
   */
  public List<Map<String, Object>> executeWithKeys(String sql) {
    List<Map<String, Object>> keys = null;
    Connection c = null;
    try {
      c = getConnection();
      keys = executeWithKeys(c, sql);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return keys;
    return new Script(this).executeWithKeys(sql);
  }
  /**
@@ -704,20 +461,7 @@
   * @return  a list of generated keys
   */
  public List<Map<String, Object>> executeWithKeys(Connection c, String sql) {
    List<Map<String, Object>> keys = null;
    Statement s = null;
    try {
      s = c.createStatement();
      s.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
      keys = toList(s.getGeneratedKeys());
      s.close();
      s = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(s);
    }
    return keys;
    return new Script(this).executeWithKeys(c, sql);
  }
  /**
@@ -726,19 +470,7 @@
   * @return  the number of records affected by this statement or -1 if none
   */
  public int execute(String sql) {
    int numRows = -1;
    Connection c = null;
    try {
      c = getConnection();
      numRows = execute(c, sql);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return numRows;
    return new Script(this).execute(sql);
  }
  /**
@@ -748,19 +480,7 @@
   * @return  the number of records affected by this statement or -1 if none
   */
  public int execute(Connection c, String sql) {
    int numRows = -1;
    Statement s = null;
    try {
      s = c.createStatement();
      numRows = s.executeUpdate(sql);
      s.close();
      s = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(s);
    }
    return numRows;
    return new Script(this).execute(c, sql);
  }
  /**
@@ -770,19 +490,7 @@
   * @return  the number of records affected by this statement or -1 if none
   */
  public int execute(String sql, Object... params) {
    int numRows = -1;
    Connection c = null;
    try {
      c = getConnection();
      numRows = execute(c, sql, params);
      c.close();
      c = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeConnectionFinally(c);
    }
    return numRows;
    return new Script(this).execute(sql, params);
  }
  /**
@@ -793,23 +501,7 @@
   * @return  the number of records affected by this statement or -1 if none
   */
  public int execute(Connection c, String sql, Object... params) {
    int numRows = -1;
    Statement s = null;
    try {
      //s = c.createStatement();
      s = buildQuery(c, sql, params);
      if(s != null && s instanceof PreparedStatement) {
        PreparedStatement ps = (PreparedStatement) s;
        numRows = ps.executeUpdate();
        ps.close();
      }
      s = null;
    } catch (Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(s);
    }
    return numRows;
    return new Script(this).execute(c, sql, params);
  }
  
  /**
@@ -822,24 +514,7 @@
   * about the number of affected rows
   */
  public int[] executeScript(String sqlScript) {
    int[] ergebnisse = null;
    Connection c = null;
    Statement s = null;
    try {
      c = getConnection();
      s = c.createStatement();
      String[] sqlKommandos = sqlScript.split(";");
      for(int i = 0; i < sqlKommandos.length; i++) {
        s.addBatch(sqlKommandos[i]);
      }
      ergebnisse = s.executeBatch();
    } catch(Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(s);
      closeConnectionFinally(c);
    }
    return ergebnisse;
    return new Script(this).executeScript(sqlScript);
  }
  
  /**
@@ -853,21 +528,7 @@
   * about the number of affected rows
   */
  public int[] executeScript(Connection c, String sqlScript) {
    int[] ergebnisse = null;
    Statement s = null;
    try {
      s = c.createStatement();
      String[] sqlKommandos = sqlScript.split(";");
      for(int i = 0; i < sqlKommandos.length; i++) {
        s.addBatch(sqlKommandos[i]);
      }
      ergebnisse = s.executeBatch();
    } catch(Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      closeStatementFinally(s);
    }
    return ergebnisse;
    return new Script(this).executeScript(c, sqlScript);
  }
  
  /* -------- transactions ------------ */
@@ -893,7 +554,7 @@
    try {
      c.setAutoCommit(false);
    } catch(SQLException ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
      logger.log(Level.SEVERE, ex.getMessage(), ex);
    } finally {
      // ..
    }
@@ -902,22 +563,28 @@
  public void commit(Connection c) {
    try {
      c.commit();
      c.setAutoCommit(true);
    } catch(SQLException ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
      logger.log(Level.SEVERE, ex.getMessage(), ex);
    } finally {
      // ...
      try {
        c.setAutoCommit(true);
      } catch (SQLException ex) {
        logger.log(Level.SEVERE, ex.getMessage(), ex);
      }
    }
  }
  
  public void rollback(Connection c) {
    try {
      c.rollback();
      c.setAutoCommit(true);
    } catch(SQLException ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    } finally {
      // ...
      try {
        c.setAutoCommit(true);
      } catch (SQLException ex) {
        logger.log(Level.SEVERE, ex.getMessage(), ex);
      }
    }
  }
  
@@ -997,39 +664,7 @@
   * This first row has the field names
   */
  public List<List<String>> toList(ResultSet rs, boolean includeBlobs) throws SQLException {
    List<List<String>> rows = new ArrayList<List<String>>();
    ResultSetMetaData meta = rs.getMetaData();
    int columnCount = meta.getColumnCount();
    List<String> header = new ArrayList<String>();
    for (int i = 1; i <= columnCount; i++) {
      header.add(meta.getColumnName(i));
    }
    rows.add(header);
    while (rs.next()) {
      List<String> row = new ArrayList<String>();
      for (int i = 1; i <= columnCount; i++) {
        String data = null;
        if (meta.getColumnType(i) == Types.BLOB) {
          if (includeBlobs) {
            Blob blob = rs.getBlob(i);
            if(blob != null) {
              data = new String(blob.getBytes((long) 1, (int) blob.length()));
            }
          }
        } else {
          Object o = rs.getObject(i);
          if(o != null) {
            data = o.toString();
          } else {
            data = NULL_STR;
          }
          logger.finest(data.toString());
        }
        row.add(data);
      }
      rows.add(row);
    }
    return rows;
    return new ListConverter().toList(rs, includeBlobs);
  }
  /**
@@ -1039,9 +674,8 @@
   * @return list of maps, one per row, with column name as the key
   * @throws SQLException if the connection fails
   */
  private List<Map<String, Object>> toList(ResultSet rs) throws SQLException {
    List<String> wantedColumnNames = getColumnNames(rs);
    return toList(rs, wantedColumnNames);
  public List<Map<String, Object>> toList(ResultSet rs) throws SQLException {
    return new ListConverter().toList(rs);
  }
  /**
@@ -1052,42 +686,8 @@
   * @return list of maps, one per column row, with column names as keys
   * @throws SQLException if the connection fails
   */
  private List<Map<String, Object>> toList(ResultSet rs, List<String> wantedColumnNames) throws SQLException {
    // TODO BLOB handling
    List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();
    int numWantedColumns = wantedColumnNames.size();
    while (rs.next()) {
      Map<String, Object> row = new TreeMap<String, Object>();
      for (int i = 0; i < numWantedColumns; ++i) {
        String columnName = wantedColumnNames.get(i);
        Object value = rs.getObject(columnName);
        if (value != null) {
          row.put(columnName, value);
          //Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).finest(columnName + " " + value);
        } else {
          row.put(columnName, "");
        }
      }
      rows.add(row);
    }
    return rows;
  public List<Map<String, Object>> toList(ResultSet rs, List<String> wantedColumnNames) throws SQLException {
    return new ListConverter().toList(rs, wantedColumnNames);
  }
  /**
   * Return all column names as a list of strings
   *
   * @param database query result set
   * @return list of column name strings
   * @throws SQLException if the query fails
   */
  private List<String> getColumnNames(ResultSet rs) throws SQLException {
    List<String> columnNames = new ArrayList<String>();
    ResultSetMetaData meta = rs.getMetaData();
    int numColumns = meta.getColumnCount();
    for (int i = 1; i <= numColumns; ++i) {
      String cName = meta.getColumnName(i);
      columnNames.add(cName);
    }
    return columnNames;
  }
}