仅具有内部状态的静态成员的类

时间:2012-01-25 14:23:23

标签: java design-patterns jdbc singleton

我觉得好像我没有把我的设计放在这里,而且我被困在两种技术之间。我正在尝试编写一个类,它将连接分发给数据库。代码如下:

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正在撤回Incorrect lazy initialization and update of static field

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

任何建议都非常感谢。我觉得好像我被困在Singleton模式和一个只包含一组静态方法的类之间。

更一般地说,这是将数据库连接分发给DAO的好方法吗?

非常感谢。

4 个答案:

答案 0 :(得分:2)

该方法应为synchronized,以避免两个并行执行(两个线程同时调用它)。

<强>加

未同步的异常可能导致两个线程执行此操作:

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

和T1和T2在两个数据源之一上调用方法(取决于T2是否覆盖T1对象创建)。

<强>挥发性

正如@ Jean-Marc建议你应该将数据源字段声明为volatile。该关键字确保线程不使用变量的线程局部副本(如果线程读取过时的缓存值,则可能会导致问题)。

我不确定这种情况是否发生在不同的方法调用之间,或者如果syncrhonized处理它,但最好确定:)

答案 1 :(得分:1)

该代码存在的问题是,在存在多重标题的情况下,最终可能会创建ComboPooledDataSource的多个实例,并且dataSource会在不同时间指向不同的实例。

如果几乎​​同时由多个线程调用该方法,就会发生这种情况。

假设dataSourcenull,并且两个线程的执行交错​​,如下所示:

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();

解决并发问题的一种简单方法是添加同步。

答案 2 :(得分:1)

Findbugs抱怨,因为您的getDataSource方法未同步。使用当前代码,两个并发线程可以调用getDataSource并检索单独的DataSource对象。

答案 3 :(得分:1)

这就是FindBugs抱怨的原因:

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

如上所述,如果您没有多个线程,则不应该有问题。