我在 java 中尽可能简单的连接管理器是线程安全的吗?

Is my as-simple-as-possible connection manager in java thread-safe?

我尝试为在 tomcat7 服务器上运行的小型 Web 应用程序创建尽可能简单的连接管理器。我找到了很多关于如何实现它的例子,但几乎总是那些需要 JNDI 和 JEE,否则就没有完整的例子。

我希望我的连接管理器在我调用 getConnection() 时从连接池传递一个连接,并且它是线程安全的,这样在我关闭它之前没有其他人使用我的连接。

在我的应用程序中,我从 REST 服务调用 ConnectionManager.getConnection(),然后在整个请求中使用该连接进行所有数据库调用,并在 finally 子句中关闭它。

有人可以看看我的代码,看看它是否足以满足我的需求...?两个 REST 调用可能获得相同的连接或 tomcat DataSource 是否为我处理它是否有任何风险?

让我的 class 成为一个带有 getDataSource() 方法的 DataSourceManager 来传递数据源,并在我的 REST 服务中获得与 DataSourceManager.getDataSource() 的连接是否更合适。获取连接()?从技术上讲,这会有什么不同吗?

或者我必须以其他方式执行此操作才能使其正常工作...?

import java.sql.Connection;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;

import se.esvenska.util.Property;

public class ConnectionManager {

    private static DataSource dataSource;

    private static void initDataSource() throws DatabaseException {
        try {
            PoolProperties p = new PoolProperties();
            p.setUrl("...url...");
            p.setDriverClassName("org.postgresql.Driver");
            p.setUsername("...user...");
            p.setPassword("...password...");
            p.setDefaultAutoCommit(false);

            dataSource = new DataSource();
            dataSource.setPoolProperties(p);
        } catch (Exception e) {
            e.printStackTrace();
            throw new DatabaseException(e);
        }
    }

    public static Connection getConnection() throws Exception {
        if (dataSource == null) {
            initDataSource();
        }
        return dataSource.getConnection();
    }
}

这取决于你所说的线程安全是什么意思。我不认为它会爆炸,但你肯定有可能不止一次地调用 initDataSource

public static Connection getConnection() throws Exception {
    if (dataSource == null) {          // Another thread can come in after this
        initDataSource();              // and before this
    }
    return dataSource.getConnection();
}

你需要synchronize那个方法:

public static synchronized Connection getConnection() throws Exception {
// -----------^
    if (dataSource == null) {          // Now this region is
        initDataSource();              // protected
    }
    return dataSource.getConnection();
}

我建议进行一些更改:

  • 将构造函数设为私有
  • getConnection 应该是使用双锁的线程安全的,详情 here

    package com.test;
    
    import java.sql.Connection;
    
    import org.apache.tomcat.jdbc.pool.DataSource;
    import org.apache.tomcat.jdbc.pool.PoolProperties;
    
    import se.esvenska.util.Property;
    
    public class ConnectionManager {
    
    private ConnectionManager() {
    }
    
    private static DataSource dataSource;
    
    private static void initDataSource() throws DatabaseException {
        try {
            PoolProperties p = new PoolProperties();
            p.setUrl("...url...");
            p.setDriverClassName("org.postgresql.Driver");
            p.setUsername("...user...");
            p.setPassword("...password...");
            p.setDefaultAutoCommit(false);
    
            dataSource = new DataSource();
            dataSource.setPoolProperties(p);
        } catch (Exception e) {
            e.printStackTrace();
            throw new DatabaseException(e);
        }
    }
    
    public static Connection getConnection() throws Exception {
        if (dataSource == null) {
            synchronized(ConnectionManager. class) {
                if (dataSource == null) {
                    initDataSource();
                }
            }
        }
        return dataSource.getConnection();
    }
    }