Show
Ignore:
Timestamp:
06/11/10 19:05:19 (2 years ago)
Author:
bturner
Message:

Convert all db calls to follow canonical pattern of resource usage.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • branches/ds_pool/src/org/bridgedb/rdb/SimpleGdb.java

    r405 r408  
    1818 
    1919import java.sql.Connection; 
    20 import java.sql.DriverManager; 
    21 import java.sql.PreparedStatement; 
    2220import java.sql.ResultSet; 
    2321import java.sql.SQLException; 
    24  
     22import java.sql.Statement; 
     23 
     24import org.apache.commons.logging.Log; 
     25import org.apache.commons.logging.LogFactory; 
    2526import org.bridgedb.DataSource; 
    2627import org.bridgedb.IDMapperException; 
    2728 
    2829/** 
    29  * SimpleGdb is the main implementation of the Gdb interface, 
    30  * for dealing with single SQL-based pgdb's. 
    31  * It's responsible for creating and querying a single  
     30 * SimpleGdb is the main implementation of the Gdb interface, for dealing with 
     31 * single SQL-based pgdb's. It's responsible for creating and querying a single 
    3232 * pgdb relational database through the JDBC interface. 
    3333 * <p> 
    34  * It wraps SQL statements in methods,  
    35  * so the rest of the apps don't need to know the 
    36  * details of the Database schema. 
    37  * <p> 
    38  * It delegates dealing with the differences between  
    39  * various RDBMS's (Derby, Hsqldb etc.) 
    40  * to a DBConnector instance. 
    41  * A correct DBConnector instance needs to be  
    42  * passed to the constructor of SimpleGdb.  
    43  * <p> 
    44  * In the PathVisio GUI environment, use GdbManager 
    45  * to create and connect one or two centralized Gdb's.  
    46  * This will also automatically 
    47  * find the right DBConnector from the preferences. 
    48  * <p> 
    49  * In a head-less or test environment, you can bypass GdbManager 
    50  * and use SimpleGdb directly  
    51  * to create or connect to one or more pgdb's of any type. 
     34 * It wraps SQL statements in methods, so the rest of the apps don't need to 
     35 * know the details of the Database schema. 
     36 * <p> 
     37 * It delegates dealing with the differences between various RDBMS's (Derby, 
     38 * Hsqldb etc.) to a DBConnector instance. A correct DBConnector instance needs 
     39 * to be passed to the constructor of SimpleGdb. 
     40 * <p> 
     41 * In the PathVisio GUI environment, use GdbManager to create and connect one or 
     42 * two centralized Gdb's. This will also automatically find the right 
     43 * DBConnector from the preferences. 
     44 * <p> 
     45 * In a head-less or test environment, you can bypass GdbManager and use 
     46 * SimpleGdb directly to create or connect to one or more pgdb's of any type. 
    5247 */ 
    5348public abstract class SimpleGdb extends IDMapperRdb 
    5449{ 
    5550     
     51    private static final Log LOG = LogFactory.getLog(SimpleGdb.class); 
     52     
    5653    private JdbcConnectionPool pool; 
    5754     
    58      
    59         private final String connectionString; 
    60         /** 
    61          * Create IDMapper based on an existing SQL connection. 
    62          * @param con Existing SQL Connection. 
    63          */ 
    64         SimpleGdb(String dbName, String connectionString) 
    65         { 
    66                 this.connectionString = connectionString; 
    67                 this.dbName = dbName; 
    68                  
    69                 try 
    70         { 
    71                     // there's something a bit weird that this class does not know 
    72                     // it's underlying driver... 
    73                     String driverClassName = null; 
    74             pool = new JdbcConnectionPool(dbName, driverClassName, connectionString); 
     55    private boolean connected; 
     56     
     57    protected final String dbName; 
     58     
     59    /** 
     60     * Create IDMapper based on an existing SQL connection. 
     61     *  
     62     * @param data_ 
     63     *            .con Existing SQL Connection. 
     64     */ 
     65    SimpleGdb(String theDbName, 
     66              String connectionString) 
     67    { 
     68         
     69        dbName = theDbName; 
     70         
     71        try 
     72        { 
     73            // there's something a bit weird that this class does not know 
     74            // it's underlying driver... 
     75            String driverClassName = null; 
     76            pool = new JdbcConnectionPool(dbName, 
     77                                          driverClassName, 
     78                                          connectionString); 
     79            connected = true; 
    7580        } 
    7681        catch (IDMapperException e) 
     
    7883            throw new RuntimeException(e); 
    7984        } 
    80         } 
    81  
    82         private boolean singleConnection = true; 
    83         private boolean neverCloseConnection = true; 
    84          
    85         /** 
    86          * helper class that handles the life cycle of a connection, query and resultset. 
    87          * <p> 
    88          * The sql for a query is passed in at construction time. 
    89          * Before each query, call init(). This will lead to lazy initialization of the 
    90          * connection and preparedstatement objects, if necessary. Set the query parameters 
    91          * using setString(int, String). Get the resultSet using  
    92          * Do not close the resultset! This will be closed for you when you call cleanup(). 
    93          * Always call cleanup() in a finally block. 
    94          * <p> 
    95          * The advantages of using QueryLifeCycle are: 
    96          * <ul> 
    97          * <li>guarantee to close preparedstatement, resultset and connection if necessary. 
    98          * <li>in case of connection pooling, preparedstatement and connection are kept together as long 
    99          *   as possible. 
    100          * <li>lazy initialization of prepared statement 
    101          * <li>always uses preparedstatement, so safe from SQL injection. 
    102          * </ul>  
    103          * <p> 
    104          * This class is not static because it needs SimpleGdb.getConnection(). 
    105          */ 
    106         final class QueryLifeCycle 
    107         { 
    108                 /** 
    109                  * Initialize with given SQL string, but don't create PreparedStatement yet. 
    110                  * Valid to call before database connection is created. 
    111                  * @param aSql SQL query 
    112                  */ 
    113                 public QueryLifeCycle(String aSql) 
    114                 { 
    115                         sql = aSql; 
    116                 } 
    117                  
    118                 private Connection con = null; 
    119                 private ResultSet rs = null; 
    120                 private PreparedStatement pst = null; 
    121                 private final String sql; 
    122                 private boolean inited = false; 
    123  
    124                 public static final int QUERY_TIMEOUT = 20; //seconds 
    125                 public static final int NO_LIMIT = 0; 
    126                 public static final int NO_TIMEOUT = 0; 
    127  
    128                 public void init(int limit) throws SQLException 
    129                 { 
    130                         init(); 
    131                         pst.setQueryTimeout(QUERY_TIMEOUT);                      
    132                         if(limit > NO_LIMIT)  
    133                         { 
    134                                 pst.setMaxRows(limit); 
    135                         } 
    136                 } 
    137                  
    138                 /** 
    139                  * Initialize connection and PreparedStatement lazily. 
    140                  * <p> 
    141                  * @throws SQLException when a PreparedStatement could not be created 
    142                  */ 
    143                 public void init() throws SQLException 
    144                 { 
    145                         if (inited) throw new IllegalStateException("Must call cleanup() between two init() calls"); 
    146                         try 
    147                         { 
    148                                 if (con == null) con = getConnection(); 
    149                                 if (pst == null) 
    150                                 { 
    151                                         pst = con.prepareStatement(sql); 
    152                                 } 
    153                         } 
    154                         finally { inited = true; } 
    155                 } 
    156                  
    157                 public void setString (int index, String val) throws SQLException 
    158                 { 
    159                         if (!inited) throw new IllegalStateException("Must call init() before setString()"); 
    160                         pst.setString(index, val); 
    161                 } 
    162                  
    163                 public ResultSet executeQuery() throws SQLException 
    164                 { 
    165                         if (!inited) throw new IllegalStateException("Must call init() before executeQuery()"); 
    166                         rs = pst.executeQuery(); 
    167                         return rs; 
    168                 } 
    169  
    170                 /**  
    171                  * Clean up resultset. If keepConnection is false, preparedstatement 
    172                  * and connection are cached. If keepConnection is true, they are closed as well. 
    173                  * The later is useful when using connection pooling. 
    174                  * <p> 
    175                  * Always call this in a finally block!  
    176                  * */ 
    177                 public void cleanup() 
    178                 { 
    179                         if (!inited) throw new IllegalStateException("Must call init() before cleanup()"); 
    180                         inited = false; 
    181                         if (rs != null) try { rs.close(); } catch (SQLException ignore) {} 
    182                         if (neverCloseConnection) return; 
    183                         if (pst != null) try { pst.close(); } catch (SQLException ignore) {} 
    184                         pst = null; 
    185                         if (con != null) try { con.close(); } catch (SQLException ignore) {} 
    186                         con = null; 
    187                 } 
    188         } 
    189  
    190         private Connection con = null; 
    191          
    192         synchronized public Connection getConnection() throws SQLException 
    193         { 
    194             /* 
    195                 // if singleConnection is true, each call to getConnection() will return the same object. 
    196                 // if singleConnection is false, each call to getConneciton() will lead to a new connection object being created. 
    197                 if (!singleConnection || con == null) 
    198                 { 
    199                         con = DriverManager.getConnection(connectionString);  
    200                         con.setReadOnly(true); 
    201                 } 
    202                 return con; 
    203                 */ 
    204             return pool.getConnection(); 
    205         } 
    206          
    207         /** 
    208          * The {@link Connection} to the Gene Database. 
    209          */ 
    210          
    211         //private Connection con = null; 
    212  
    213         /** {@inheritDoc} */ 
    214         final public boolean isConnected() {  
    215                 //return con != null;  
    216                 return true; 
    217         } 
    218  
    219         protected final String dbName; 
    220          
    221         /** {@inheritDoc} */ 
    222         @Override final public String getDbName() { return dbName; } 
    223          
    224         /** {@inheritDoc} */ 
    225         final public void close() throws IDMapperException  
    226         { 
    227 //              try 
    228 //              { 
    229 //                      con.close(); 
    230 //              } 
    231 //              catch (SQLException ex) 
    232 //              { 
    233 //                      throw new IDMapperException (ex); 
    234 //              } 
    235 //              con = null; 
    236         } 
    237          
    238         public static final int NO_LIMIT = 0; 
    239         public static final int NO_TIMEOUT = 0; 
    240         public static final int QUERY_TIMEOUT = 5; //seconds 
    241  
    242         /** 
    243            @return number of rows in gene table. 
    244            @throws IDMapperException on failure 
    245          */ 
    246         final public int getGeneCount() throws IDMapperException 
    247         { 
    248                 int result = 0; 
    249                 try 
    250                 { 
    251                         ResultSet r = getConnection().createStatement().executeQuery("SELECT COUNT(*) FROM " + "datanode"); 
    252                         r.next(); 
    253                         result = r.getInt (1); 
    254                         r.close(); 
    255                 } 
    256                 catch (SQLException e) 
    257                 { 
    258                         throw new IDMapperException (e); 
    259                 } 
    260                 return result; 
    261         } 
    262  
    263         /** 
    264          * @param ds DataSource to count identifiers for. 
    265            @return number of identifiers table for the given datasource 
    266            @throws IDMapperException on failure 
    267          */ 
    268         final public int getGeneCount(DataSource ds) throws IDMapperException 
    269         { 
    270                 int result = 0; 
    271                 try 
    272                 { 
    273                         ResultSet r = getConnection().createStatement().executeQuery( 
    274                                         "SELECT COUNT(*) FROM datanode WHERE code = '" + ds.getSystemCode() + "'"); 
    275                         r.next(); 
    276                         result = r.getInt (1); 
    277                         r.close(); 
    278                 } 
    279                 catch (SQLException e) 
    280                 { 
    281                         throw new IDMapperException (e); 
    282                 } 
    283                 return result; 
    284         } 
    285          
     85    } 
     86     
     87    public Connection getConnection() throws SQLException 
     88    { 
     89         
     90        return pool.getConnection(); 
     91    } 
     92     
     93    /** {@inheritDoc} */ 
     94    @Override 
     95    final public String getDbName() 
     96    { 
     97        return dbName; 
     98    } 
     99     
     100    /** 
     101     * @return number of rows in gene table. 
     102     * @throws IDMapperException 
     103     *             on failure 
     104     */ 
     105    final public int getGeneCount() throws IDMapperException 
     106    { 
     107        int result = 0; 
     108         
     109        ResultSet r = null; 
     110        Statement s = null; 
     111        Connection c = null; 
     112        try 
     113        { 
     114            c = getConnection(); 
     115            s = c.createStatement(); 
     116            r = s.executeQuery("SELECT COUNT(*) FROM " 
     117                                                       + "datanode"); 
     118            r.next(); 
     119            result = r.getInt(1); 
     120        } 
     121        catch (SQLException e) 
     122        { 
     123            throw new IDMapperException(e); 
     124        } 
     125        finally 
     126        {    
     127            closeSilently(r, s, c); 
     128        } 
     129        return result; 
     130    } 
     131     
     132    /** 
     133     * @param ds 
     134     *            DataSource to count identifiers for. 
     135     * @return number of identifiers table for the given datasource 
     136     * @throws IDMapperException 
     137     *             on failure 
     138     */ 
     139    final public int getGeneCount(DataSource ds) throws IDMapperException 
     140    { 
     141        int result = 0; 
     142         
     143        ResultSet r = null; 
     144        Statement s = null; 
     145        Connection c = null; 
     146         
     147        try 
     148        { 
     149            c = getConnection(); 
     150            s = c.createStatement(); 
     151            r = s.executeQuery("SELECT COUNT(*) FROM datanode WHERE code = '" 
     152                               + ds.getSystemCode() + "'"); 
     153             
     154            r.next(); 
     155            result = r.getInt(1); 
     156        } 
     157        catch (SQLException e) 
     158        { 
     159            throw new IDMapperException(e); 
     160        } 
     161        finally 
     162        { 
     163            closeSilently(r, s, c); 
     164        } 
     165         
     166        return result; 
     167    } 
     168     
     169    protected void closeSilently(Connection c) 
     170    { 
     171        if (c != null) 
     172        { 
     173            try 
     174            { 
     175                c.close(); 
     176            } 
     177            catch (SQLException e) 
     178            { 
     179                LOG.error("Error closing result set: " + e, e); 
     180            } 
     181        } 
     182    } 
     183     
     184    protected void closeSilently(Statement s) 
     185    { 
     186        if (s != null) 
     187        { 
     188            try 
     189            { 
     190                s.close(); 
     191            } 
     192            catch (SQLException e) 
     193            { 
     194                LOG.error("Error closing statement: " + e, e); 
     195            } 
     196        } 
     197    } 
     198     
     199    protected void closeSilently(ResultSet rs) 
     200    { 
     201        if (rs != null) 
     202        { 
     203            try 
     204            { 
     205                rs.close(); 
     206            } 
     207            catch (SQLException e) 
     208            { 
     209                LOG.error("Error closing conection: " + e, e); 
     210            } 
     211        } 
     212    } 
     213     
     214    public void closeSilently(ResultSet r, 
     215                              Statement s, 
     216                              Connection c) 
     217    { 
     218         
     219        closeSilently(r); 
     220         
     221        closeSilently(s); 
     222         
     223        closeSilently(c); 
     224         
     225    } 
     226 
     227    @Override 
     228    public void close() throws IDMapperException 
     229    { 
     230        if (pool != null) 
     231        { 
     232             
     233            pool.shutdownDriver(); 
     234             
     235        } 
     236        connected = false; 
     237    } 
     238 
     239    @Override 
     240    public boolean isConnected() 
     241    { 
     242        
     243        return connected; 
     244    } 
     245     
     246     
    286247}