简体   繁体   中英

Class with only static members which has an internal state

I feel as though I haven't got my design right here and that I'm stuck between two techniques. I'm attempting to write a class which hands out connections to a database. The code is below:

public final class DBUtil {

  private static String databaseDriver = null;
  private static String databaseConnectionString = null;
  private static String databaseUser = null;
  private static String databasePassword = null;
  private static String serverName = null;
  private static ComboPooledDataSource dataSource = null;

  private DBUtil() {
    throw new AssertionError();
  }

  private static void getParameters() {
    final Properties configFile = new Properties();
    try {
      configFile.load(DBUtil.class.getClassLoader().getResourceAsStream("my.properties"));
      if (configFile.containsKey("databaseConnectionString") && configFile.containsKey("databaseUser") && configFile.containsKey("databasePassword") && configFile.containsKey("databaseDriver")) {
        DBUtil.databaseConnectionString = configFile.getProperty("databaseConnectionString");
        DBUtil.databaseDriver = configFile.getProperty("databaseDriver");
        DBUtil.databaseUser = configFile.getProperty("databaseUser");
        DBUtil.databasePassword = configFile.getProperty("databasePassword");
      }
      else {
        // Properties file not configured correctly for database connection
      }
    }
    catch (IOException e) {}
  }

  public static Connection getDatabaseConnection() {
    if (Strings.isNullOrEmpty(databaseConnectionString) || Strings.isNullOrEmpty(databaseUser) || Strings.isNullOrEmpty(databasePassword) || Strings.isNullOrEmpty(databaseDriver)) {
      DBUtil.getParameters();
     }
    dataSource = getDataSource();
    int retryCount = 0;
    Connection connection = null;
    while (connection == null) {
      try {
        connection = dataSource.getConnection();
      }
      catch (SQLException sqle) {}
    }
    return connection;
  }

  private static ComboPooledDataSource getDataSource() {
    if (dataSource == null) {
      dataSource = new ComboPooledDataSource();
      try {
        dataSource.setDriverClass(databaseDriver);
        dataSource.setJdbcUrl(databaseConnectionString);
        dataSource.setUser(databaseUser);
        dataSource.setPassword(databasePassword);
      }
      catch (PropertyVetoException pve) {}
    }
    return dataSource;
  }

  public static void cleanUpDataSource() {
    try {
      DataSources.destroy(dataSource);
    }
    catch (SQLException sqle) {}
  }
}

FindBugs is returing Incorrect lazy initialization and update of static field when I do:

if (dataSource == null) {
      dataSource = new ComboPooledDataSource();
      ...

Any advice greatly appreciated. I feel as though I'm stuck here somewhere between the Singleton pattern and a class which consists of simply a set of static methods.

More generally, is this a good way to hand out database connections to DAOs?

Many thanks.

That method should be synchronized in order to avoid two parallel executions (two threads calling it at the same time).

Added

Not synchronized exception could lead to two threads executing this:

T1   if (datasource == null) YES
T2                               if (datasource == null) YES
T1   datasource = new Datasource...
T2                               datasource = new Datasource(...); AGAIN!

and T1 and T2 calling methods on one of two datasources (depending when T2 overrides the T1 object creation).

Volatile

As @Jean-Marc proposes you should declare datasource field as volatile . That keyword ensures that thread don't use a thread-local copy of the variable (that would potentially cause problems if a thread reads an outdated cached value).

I'm not sure if this case happens between different method invocations, or if syncrhonized deals with it but it's better to be sure :)

The problem with that code is that in the presence of multitheading you could end up creating multiple instances of ComboPooledDataSource , and having dataSource point to different instances at different times.

This could happen if the method were to be called by several threads at about the same time.

Let's say dataSource is null , and the execution of two threads is interleaved as follows:

Thread 1: if (dataSource == null) { // condition is true
Thread 2: if (dataSource == null) { // condition is true
Thread 1: dataSource = new ComboPooledDataSource();
Thread 2: dataSource = new ComboPooledDataSource();

An easy way to fix the concurrency issue is by adding synchronization.

Findbugs is complaining because your getDataSource method is not synchronized. With your current code it is possible for two concurrent threads to call getDataSource and retrieve to separate DataSource objects.

This is why FindBugs is complaining:

http://findbugs.sourceforge.net/bugDescriptions.html#LI_LAZY_INIT_UPDATE_STATIC

As mentioned, if you don't have multiple threads you shouldn't have a problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM