我觉得好像我没有把我的设计放在这里,而且我被困在两种技术之间。我正在尝试编写一个类,它将连接分发给数据库。代码如下:
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的好方法吗?
非常感谢。
答案 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
会在不同时间指向不同的实例。
如果几乎同时由多个线程调用该方法,就会发生这种情况。
假设dataSource
为null
,并且两个线程的执行交错,如下所示:
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
如上所述,如果您没有多个线程,则不应该有问题。