Coverage Report - org.apache.commons.configuration.DatabaseConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
DatabaseConfiguration
96%
183/189
96%
52/54
3,118
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.configuration;
 19  
 
 20  
 import java.sql.Connection;
 21  
 import java.sql.PreparedStatement;
 22  
 import java.sql.ResultSet;
 23  
 import java.sql.SQLException;
 24  
 import java.sql.Statement;
 25  
 import java.util.ArrayList;
 26  
 import java.util.Collection;
 27  
 import java.util.Iterator;
 28  
 import java.util.List;
 29  
 
 30  
 import javax.sql.DataSource;
 31  
 
 32  
 import org.apache.commons.logging.LogFactory;
 33  
 
 34  
 /**
 35  
  * Configuration stored in a database. The properties are retrieved from a
 36  
  * table containing at least one column for the keys, and one column for the
 37  
  * values. It's possible to store several configurations in the same table by
 38  
  * adding a column containing the name of the configuration. The name of the
 39  
  * table and the columns is specified in the constructor.
 40  
  *
 41  
  * <h4>Example 1 - One configuration per table</h4>
 42  
  *
 43  
  * <pre>
 44  
  * CREATE TABLE myconfig (
 45  
  *     `key`   VARCHAR NOT NULL PRIMARY KEY,
 46  
  *     `value` VARCHAR
 47  
  * );
 48  
  *
 49  
  * INSERT INTO myconfig (key, value) VALUES ('foo', 'bar');
 50  
  *
 51  
  *
 52  
  * Configuration config = new DatabaseConfiguration(datasource, "myconfig", "key", "value");
 53  
  * String value = config.getString("foo");
 54  
  * </pre>
 55  
  *
 56  
  * <h4>Example 2 - Multiple configurations per table</h4>
 57  
  *
 58  
  * <pre>
 59  
  * CREATE TABLE myconfigs (
 60  
  *     `name`  VARCHAR NOT NULL,
 61  
  *     `key`   VARCHAR NOT NULL,
 62  
  *     `value` VARCHAR,
 63  
  *     CONSTRAINT sys_pk_myconfigs PRIMARY KEY (`name`, `key`)
 64  
  * );
 65  
  *
 66  
  * INSERT INTO myconfigs (name, key, value) VALUES ('config1', 'key1', 'value1');
 67  
  * INSERT INTO myconfigs (name, key, value) VALUES ('config2', 'key2', 'value2');
 68  
  *
 69  
  *
 70  
  * Configuration config1 = new DatabaseConfiguration(datasource, "myconfigs", "name", "key", "value", "config1");
 71  
  * String value1 = conf.getString("key1");
 72  
  *
 73  
  * Configuration config2 = new DatabaseConfiguration(datasource, "myconfigs", "name", "key", "value", "config2");
 74  
  * String value2 = conf.getString("key2");
 75  
  * </pre>
 76  
  * The configuration can be instructed to perform commits after database updates.
 77  
  * This is achieved by setting the {@code commits} parameter of the
 78  
  * constructors to <b>true</b>. If commits should not be performed (which is the
 79  
  * default behavior), it should be ensured that the connections returned by the
 80  
  * {@code DataSource} are in auto-commit mode.
 81  
  *
 82  
  * <h1>Note: Like JDBC itself, protection against SQL injection is left to the user.</h1>
 83  
  * @since 1.0
 84  
  *
 85  
  * @author <a href="mailto:ebourg@apache.org">Emmanuel Bourg</a>
 86  
  * @version $Id: DatabaseConfiguration.java 1344442 2012-05-30 20:17:35Z oheger $
 87  
  */
 88  
 public class DatabaseConfiguration extends AbstractConfiguration
 89  
 {
 90  
     /** The datasource to connect to the database. */
 91  
     private final DataSource datasource;
 92  
 
 93  
     /** The name of the table containing the configurations. */
 94  
     private final String table;
 95  
 
 96  
     /** The column containing the name of the configuration. */
 97  
     private final String nameColumn;
 98  
 
 99  
     /** The column containing the keys. */
 100  
     private final String keyColumn;
 101  
 
 102  
     /** The column containing the values. */
 103  
     private final String valueColumn;
 104  
 
 105  
     /** The name of the configuration. */
 106  
     private final String name;
 107  
 
 108  
     /** A flag whether commits should be performed by this configuration. */
 109  
     private final boolean doCommits;
 110  
 
 111  
     /**
 112  
      * Build a configuration from a table containing multiple configurations.
 113  
      * No commits are performed by the new configuration instance.
 114  
      *
 115  
      * @param datasource    the datasource to connect to the database
 116  
      * @param table         the name of the table containing the configurations
 117  
      * @param nameColumn    the column containing the name of the configuration
 118  
      * @param keyColumn     the column containing the keys of the configuration
 119  
      * @param valueColumn   the column containing the values of the configuration
 120  
      * @param name          the name of the configuration
 121  
      */
 122  
     public DatabaseConfiguration(DataSource datasource, String table, String nameColumn,
 123  
             String keyColumn, String valueColumn, String name)
 124  
     {
 125  21
         this(datasource, table, nameColumn, keyColumn, valueColumn, name, false);
 126  21
     }
 127  
 
 128  
     /**
 129  
      * Creates a new instance of {@code DatabaseConfiguration} that operates on
 130  
      * a database table containing multiple configurations.
 131  
      *
 132  
      * @param datasource the {@code DataSource} to connect to the database
 133  
      * @param table the name of the table containing the configurations
 134  
      * @param nameColumn the column containing the name of the configuration
 135  
      * @param keyColumn the column containing the keys of the configuration
 136  
      * @param valueColumn the column containing the values of the configuration
 137  
      * @param name the name of the configuration
 138  
      * @param commits a flag whether the configuration should perform a commit
 139  
      *        after a database update
 140  
      */
 141  
     public DatabaseConfiguration(DataSource datasource, String table,
 142  
             String nameColumn, String keyColumn, String valueColumn,
 143  
             String name, boolean commits)
 144  46
     {
 145  46
         this.datasource = datasource;
 146  46
         this.table = table;
 147  46
         this.nameColumn = nameColumn;
 148  46
         this.keyColumn = keyColumn;
 149  46
         this.valueColumn = valueColumn;
 150  46
         this.name = name;
 151  46
         doCommits = commits;
 152  46
         setLogger(LogFactory.getLog(getClass()));
 153  46
         addErrorLogListener();  // log errors per default
 154  46
     }
 155  
 
 156  
     /**
 157  
      * Build a configuration from a table.
 158  
      *
 159  
      * @param datasource    the datasource to connect to the database
 160  
      * @param table         the name of the table containing the configurations
 161  
      * @param keyColumn     the column containing the keys of the configuration
 162  
      * @param valueColumn   the column containing the values of the configuration
 163  
      */
 164  
     public DatabaseConfiguration(DataSource datasource, String table, String keyColumn, String valueColumn)
 165  
     {
 166  19
         this(datasource, table, null, keyColumn, valueColumn, null);
 167  19
     }
 168  
 
 169  
     /**
 170  
      * Creates a new instance of {@code DatabaseConfiguration} that
 171  
      * operates on a database table containing a single configuration only.
 172  
      *
 173  
      * @param datasource the {@code DataSource} to connect to the database
 174  
      * @param table the name of the table containing the configurations
 175  
      * @param keyColumn the column containing the keys of the configuration
 176  
      * @param valueColumn the column containing the values of the configuration
 177  
      * @param commits a flag whether the configuration should perform a commit
 178  
      *        after a database update
 179  
      */
 180  
     public DatabaseConfiguration(DataSource datasource, String table,
 181  
             String keyColumn, String valueColumn, boolean commits)
 182  
     {
 183  15
         this(datasource, table, null, keyColumn, valueColumn, null, commits);
 184  15
     }
 185  
 
 186  
     /**
 187  
      * Returns a flag whether this configuration performs commits after database
 188  
      * updates.
 189  
      *
 190  
      * @return a flag whether commits are performed
 191  
      */
 192  
     public boolean isDoCommits()
 193  
     {
 194  36
         return doCommits;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Returns the value of the specified property. If this causes a database
 199  
      * error, an error event will be generated of type
 200  
      * {@code EVENT_READ_PROPERTY} with the causing exception. The
 201  
      * event's {@code propertyName} is set to the passed in property key,
 202  
      * the {@code propertyValue} is undefined.
 203  
      *
 204  
      * @param key the key of the desired property
 205  
      * @return the value of this property
 206  
      */
 207  
     public Object getProperty(String key)
 208  
     {
 209  12
         Object result = null;
 210  
 
 211  
         // build the query
 212  12
         StringBuilder query = new StringBuilder("SELECT * FROM ");
 213  12
         query.append(table).append(" WHERE ");
 214  12
         query.append(keyColumn).append("=?");
 215  12
         if (nameColumn != null)
 216  
         {
 217  4
             query.append(" AND " + nameColumn + "=?");
 218  
         }
 219  
 
 220  12
         Connection conn = null;
 221  12
         PreparedStatement pstmt = null;
 222  12
         ResultSet rs = null;
 223  
 
 224  
         try
 225  
         {
 226  12
             conn = getConnection();
 227  
 
 228  
             // bind the parameters
 229  11
             pstmt = conn.prepareStatement(query.toString());
 230  11
             pstmt.setString(1, key);
 231  11
             if (nameColumn != null)
 232  
             {
 233  4
                 pstmt.setString(2, name);
 234  
             }
 235  
 
 236  11
             rs = pstmt.executeQuery();
 237  
 
 238  11
             List<Object> results = new ArrayList<Object>();
 239  22
             while (rs.next())
 240  
             {
 241  11
                 Object value = rs.getObject(valueColumn);
 242  11
                 if (isDelimiterParsingDisabled())
 243  
                 {
 244  1
                     results.add(value);
 245  
                 }
 246  
                 else
 247  
                 {
 248  
                     // Split value if it contains the list delimiter
 249  10
                     Iterator<?> it = PropertyConverter.toIterator(value, getListDelimiter());
 250  26
                     while (it.hasNext())
 251  
                     {
 252  16
                         results.add(it.next());
 253  
                     }
 254  
                 }
 255  11
             }
 256  
 
 257  11
             if (!results.isEmpty())
 258  
             {
 259  9
                 result = (results.size() > 1) ? results : results.get(0);
 260  
             }
 261  
         }
 262  1
         catch (SQLException e)
 263  
         {
 264  1
             fireError(EVENT_READ_PROPERTY, key, null, e);
 265  
         }
 266  
         finally
 267  
         {
 268  12
             close(conn, pstmt, rs);
 269  12
         }
 270  
 
 271  12
         return result;
 272  
     }
 273  
 
 274  
     /**
 275  
      * Adds a property to this configuration. If this causes a database error,
 276  
      * an error event will be generated of type {@code EVENT_ADD_PROPERTY}
 277  
      * with the causing exception. The event's {@code propertyName} is
 278  
      * set to the passed in property key, the {@code propertyValue}
 279  
      * points to the passed in value.
 280  
      *
 281  
      * @param key the property key
 282  
      * @param obj the value of the property to add
 283  
      */
 284  
     @Override
 285  
     protected void addPropertyDirect(String key, Object obj)
 286  
     {
 287  
         // build the query
 288  20
         StringBuilder query = new StringBuilder("INSERT INTO " + table);
 289  20
         if (nameColumn != null)
 290  
         {
 291  3
             query.append(" (" + nameColumn + ", " + keyColumn + ", " + valueColumn + ") VALUES (?, ?, ?)");
 292  
         }
 293  
         else
 294  
         {
 295  17
             query.append(" (" + keyColumn + ", " + valueColumn + ") VALUES (?, ?)");
 296  
         }
 297  
 
 298  20
         Connection conn = null;
 299  20
         PreparedStatement pstmt = null;
 300  
 
 301  
         try
 302  
         {
 303  20
             conn = getConnection();
 304  
 
 305  
             // bind the parameters
 306  19
             pstmt = conn.prepareStatement(query.toString());
 307  19
             int index = 1;
 308  19
             if (nameColumn != null)
 309  
             {
 310  3
                 pstmt.setString(index++, name);
 311  
             }
 312  19
             pstmt.setString(index++, key);
 313  19
             pstmt.setString(index++, String.valueOf(obj));
 314  
 
 315  19
             pstmt.executeUpdate();
 316  19
             commitIfRequired(conn);
 317  
         }
 318  1
         catch (SQLException e)
 319  
         {
 320  1
             fireError(EVENT_ADD_PROPERTY, key, obj, e);
 321  
         }
 322  
         finally
 323  
         {
 324  
             // clean up
 325  20
             close(conn, pstmt, null);
 326  20
         }
 327  20
     }
 328  
 
 329  
     /**
 330  
      * Adds a property to this configuration. This implementation will
 331  
      * temporarily disable list delimiter parsing, so that even if the value
 332  
      * contains the list delimiter, only a single record will be written into
 333  
      * the managed table. The implementation of {@code getProperty()}
 334  
      * will take care about delimiters. So list delimiters are fully supported
 335  
      * by {@code DatabaseConfiguration}, but internally treated a bit
 336  
      * differently.
 337  
      *
 338  
      * @param key the key of the new property
 339  
      * @param value the value to be added
 340  
      */
 341  
     @Override
 342  
     public void addProperty(String key, Object value)
 343  
     {
 344  16
         boolean parsingFlag = isDelimiterParsingDisabled();
 345  
         try
 346  
         {
 347  16
             if (value instanceof String)
 348  
             {
 349  
                 // temporarily disable delimiter parsing
 350  16
                 setDelimiterParsingDisabled(true);
 351  
             }
 352  16
             super.addProperty(key, value);
 353  
         }
 354  
         finally
 355  
         {
 356  16
             setDelimiterParsingDisabled(parsingFlag);
 357  16
         }
 358  16
     }
 359  
 
 360  
     /**
 361  
      * Checks if this configuration is empty. If this causes a database error,
 362  
      * an error event will be generated of type {@code EVENT_READ_PROPERTY}
 363  
      * with the causing exception. Both the event's {@code propertyName}
 364  
      * and {@code propertyValue} will be undefined.
 365  
      *
 366  
      * @return a flag whether this configuration is empty.
 367  
      */
 368  
     public boolean isEmpty()
 369  
     {
 370  8
         boolean empty = true;
 371  
 
 372  
         // build the query
 373  8
         StringBuilder query = new StringBuilder("SELECT count(*) FROM " + table);
 374  8
         if (nameColumn != null)
 375  
         {
 376  3
             query.append(" WHERE " + nameColumn + "=?");
 377  
         }
 378  
 
 379  8
         Connection conn = null;
 380  8
         PreparedStatement pstmt = null;
 381  8
         ResultSet rs = null;
 382  
 
 383  
         try
 384  
         {
 385  8
             conn = getConnection();
 386  
 
 387  
             // bind the parameters
 388  7
             pstmt = conn.prepareStatement(query.toString());
 389  7
             if (nameColumn != null)
 390  
             {
 391  3
                 pstmt.setString(1, name);
 392  
             }
 393  
 
 394  7
             rs = pstmt.executeQuery();
 395  
 
 396  7
             if (rs.next())
 397  
             {
 398  7
                 empty = rs.getInt(1) == 0;
 399  
             }
 400  
         }
 401  1
         catch (SQLException e)
 402  
         {
 403  1
             fireError(EVENT_READ_PROPERTY, null, null, e);
 404  
         }
 405  
         finally
 406  
         {
 407  
             // clean up
 408  8
             close(conn, pstmt, rs);
 409  8
         }
 410  
 
 411  8
         return empty;
 412  
     }
 413  
 
 414  
     /**
 415  
      * Checks whether this configuration contains the specified key. If this
 416  
      * causes a database error, an error event will be generated of type
 417  
      * {@code EVENT_READ_PROPERTY} with the causing exception. The
 418  
      * event's {@code propertyName} will be set to the passed in key, the
 419  
      * {@code propertyValue} will be undefined.
 420  
      *
 421  
      * @param key the key to be checked
 422  
      * @return a flag whether this key is defined
 423  
      */
 424  
     public boolean containsKey(String key)
 425  
     {
 426  15
         boolean found = false;
 427  
 
 428  
         // build the query
 429  15
         StringBuilder query = new StringBuilder("SELECT * FROM " + table + " WHERE " + keyColumn + "=?");
 430  15
         if (nameColumn != null)
 431  
         {
 432  6
             query.append(" AND " + nameColumn + "=?");
 433  
         }
 434  
 
 435  15
         Connection conn = null;
 436  15
         PreparedStatement pstmt = null;
 437  15
         ResultSet rs = null;
 438  
 
 439  
         try
 440  
         {
 441  15
             conn = getConnection();
 442  
 
 443  
             // bind the parameters
 444  14
             pstmt = conn.prepareStatement(query.toString());
 445  14
             pstmt.setString(1, key);
 446  14
             if (nameColumn != null)
 447  
             {
 448  6
                 pstmt.setString(2, name);
 449  
             }
 450  
 
 451  14
             rs = pstmt.executeQuery();
 452  
 
 453  14
             found = rs.next();
 454  
         }
 455  1
         catch (SQLException e)
 456  
         {
 457  1
             fireError(EVENT_READ_PROPERTY, key, null, e);
 458  
         }
 459  
         finally
 460  
         {
 461  
             // clean up
 462  15
             close(conn, pstmt, rs);
 463  15
         }
 464  
 
 465  15
         return found;
 466  
     }
 467  
 
 468  
     /**
 469  
      * Removes the specified value from this configuration. If this causes a
 470  
      * database error, an error event will be generated of type
 471  
      * {@code EVENT_CLEAR_PROPERTY} with the causing exception. The
 472  
      * event's {@code propertyName} will be set to the passed in key, the
 473  
      * {@code propertyValue} will be undefined.
 474  
      *
 475  
      * @param key the key of the property to be removed
 476  
      */
 477  
     @Override
 478  
     protected void clearPropertyDirect(String key)
 479  
     {
 480  
         // build the query
 481  11
         StringBuilder query = new StringBuilder("DELETE FROM " + table + " WHERE " + keyColumn + "=?");
 482  11
         if (nameColumn != null)
 483  
         {
 484  3
             query.append(" AND " + nameColumn + "=?");
 485  
         }
 486  
 
 487  11
         Connection conn = null;
 488  11
         PreparedStatement pstmt = null;
 489  
 
 490  
         try
 491  
         {
 492  11
             conn = getConnection();
 493  
 
 494  
             // bind the parameters
 495  10
             pstmt = conn.prepareStatement(query.toString());
 496  10
             pstmt.setString(1, key);
 497  10
             if (nameColumn != null)
 498  
             {
 499  3
                 pstmt.setString(2, name);
 500  
             }
 501  
 
 502  10
             pstmt.executeUpdate();
 503  10
             commitIfRequired(conn);
 504  
         }
 505  1
         catch (SQLException e)
 506  
         {
 507  1
             fireError(EVENT_CLEAR_PROPERTY, key, null, e);
 508  
         }
 509  
         finally
 510  
         {
 511  
             // clean up
 512  11
             close(conn, pstmt, null);
 513  11
         }
 514  11
     }
 515  
 
 516  
     /**
 517  
      * Removes all entries from this configuration. If this causes a database
 518  
      * error, an error event will be generated of type
 519  
      * {@code EVENT_CLEAR} with the causing exception. Both the
 520  
      * event's {@code propertyName} and the {@code propertyValue}
 521  
      * will be undefined.
 522  
      */
 523  
     @Override
 524  
     public void clear()
 525  
     {
 526  6
         fireEvent(EVENT_CLEAR, null, null, true);
 527  
         // build the query
 528  6
         StringBuilder query = new StringBuilder("DELETE FROM " + table);
 529  6
         if (nameColumn != null)
 530  
         {
 531  1
             query.append(" WHERE " + nameColumn + "=?");
 532  
         }
 533  
 
 534  6
         Connection conn = null;
 535  6
         PreparedStatement pstmt = null;
 536  
 
 537  
         try
 538  
         {
 539  6
             conn = getConnection();
 540  
 
 541  
             // bind the parameters
 542  5
             pstmt = conn.prepareStatement(query.toString());
 543  5
             if (nameColumn != null)
 544  
             {
 545  1
                 pstmt.setString(1, name);
 546  
             }
 547  
 
 548  5
             pstmt.executeUpdate();
 549  5
             commitIfRequired(conn);
 550  
         }
 551  1
         catch (SQLException e)
 552  
         {
 553  1
             fireError(EVENT_CLEAR, null, null, e);
 554  
         }
 555  
         finally
 556  
         {
 557  
             // clean up
 558  6
             close(conn, pstmt, null);
 559  6
         }
 560  6
         fireEvent(EVENT_CLEAR, null, null, false);
 561  6
     }
 562  
 
 563  
     /**
 564  
      * Returns an iterator with the names of all properties contained in this
 565  
      * configuration. If this causes a database
 566  
      * error, an error event will be generated of type
 567  
      * {@code EVENT_READ_PROPERTY} with the causing exception. Both the
 568  
      * event's {@code propertyName} and the {@code propertyValue}
 569  
      * will be undefined.
 570  
      * @return an iterator with the contained keys (an empty iterator in case
 571  
      * of an error)
 572  
      */
 573  
     public Iterator<String> getKeys()
 574  
     {
 575  6
         Collection<String> keys = new ArrayList<String>();
 576  
 
 577  
         // build the query
 578  6
         StringBuilder query = new StringBuilder("SELECT DISTINCT " + keyColumn + " FROM " + table);
 579  6
         if (nameColumn != null)
 580  
         {
 581  1
             query.append(" WHERE " + nameColumn + "=?");
 582  
         }
 583  
 
 584  6
         Connection conn = null;
 585  6
         PreparedStatement pstmt = null;
 586  6
         ResultSet rs = null;
 587  
 
 588  
         try
 589  
         {
 590  6
             conn = getConnection();
 591  
 
 592  
             // bind the parameters
 593  5
             pstmt = conn.prepareStatement(query.toString());
 594  5
             if (nameColumn != null)
 595  
             {
 596  1
                 pstmt.setString(1, name);
 597  
             }
 598  
 
 599  5
             rs = pstmt.executeQuery();
 600  
 
 601  16
             while (rs.next())
 602  
             {
 603  11
                 keys.add(rs.getString(1));
 604  
             }
 605  
         }
 606  1
         catch (SQLException e)
 607  
         {
 608  1
             fireError(EVENT_READ_PROPERTY, null, null, e);
 609  
         }
 610  
         finally
 611  
         {
 612  
             // clean up
 613  6
             close(conn, pstmt, rs);
 614  6
         }
 615  
 
 616  6
         return keys.iterator();
 617  
     }
 618  
 
 619  
     /**
 620  
      * Returns the used {@code DataSource} object.
 621  
      *
 622  
      * @return the data source
 623  
      * @since 1.4
 624  
      */
 625  
     public DataSource getDatasource()
 626  
     {
 627  71
         return datasource;
 628  
     }
 629  
 
 630  
     /**
 631  
      * Returns a {@code Connection} object. This method is called when
 632  
      * ever the database is to be accessed. This implementation returns a
 633  
      * connection from the current {@code DataSource}.
 634  
      *
 635  
      * @return the {@code Connection} object to be used
 636  
      * @throws SQLException if an error occurs
 637  
      * @since 1.4
 638  
      * @deprecated Use a custom data source to change the connection used by the
 639  
      * class. To be removed in Commons Configuration 2.0
 640  
      */
 641  
     @Deprecated
 642  
     protected Connection getConnection() throws SQLException
 643  
     {
 644  71
         return getDatasource().getConnection();
 645  
     }
 646  
 
 647  
     /**
 648  
      * Close the specified database objects.
 649  
      * Avoid closing if null and hide any SQLExceptions that occur.
 650  
      *
 651  
      * @param conn The database connection to close
 652  
      * @param stmt The statement to close
 653  
      * @param rs the result set to close
 654  
      */
 655  
     private void close(Connection conn, Statement stmt, ResultSet rs)
 656  
     {
 657  
         try
 658  
         {
 659  78
             if (rs != null)
 660  
             {
 661  37
                 rs.close();
 662  
             }
 663  
         }
 664  0
         catch (SQLException e)
 665  
         {
 666  0
             getLogger().error("An error occurred on closing the result set", e);
 667  78
         }
 668  
 
 669  
         try
 670  
         {
 671  78
             if (stmt != null)
 672  
             {
 673  71
                 stmt.close();
 674  
             }
 675  
         }
 676  0
         catch (SQLException e)
 677  
         {
 678  0
             getLogger().error("An error occured on closing the statement", e);
 679  78
         }
 680  
 
 681  
         try
 682  
         {
 683  78
             if (conn != null)
 684  
             {
 685  71
                 conn.close();
 686  
             }
 687  
         }
 688  0
         catch (SQLException e)
 689  
         {
 690  0
             getLogger().error("An error occured on closing the connection", e);
 691  78
         }
 692  78
     }
 693  
 
 694  
     /**
 695  
      * Performs a commit if needed. This method is called after updates of the
 696  
      * managed database table. If the configuration should perform commits, it
 697  
      * does so now.
 698  
      *
 699  
      * @param conn the active connection
 700  
      * @throws SQLException if an error occurs
 701  
      */
 702  
     private void commitIfRequired(Connection conn) throws SQLException
 703  
     {
 704  34
         if (isDoCommits())
 705  
         {
 706  3
             conn.commit();
 707  
         }
 708  34
     }
 709  
 }