Java 使用 PreparedStatement 的多线程和连接池

Java multithreading and connection pooling with PreparedStatement

我目前有一个数据库 class,其中包含在构造函数中初始化的 PreparedStatement 成员变量。像这样:

public class Database
{
    private Connection connection;
    private PreparedStatement statement1, statement2, ...;

    public Database(String url, String user, String pass)
    {
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        connection = DriverManager.getConnection(url, user, pass);

        statement1 = connection.prepareStatement("sql stuff");
        statement2 = connection.prepareStatement("sql stuff");
        // etc
    }

    public User getUser(int userId)
    {
        // execute getUser statement
    }
    // and other similar methods
}

该应用程序将是多线程的,我想使用 c3p0 进行连接池。但是我不知道该怎么做。

假设我为每个线程创建了一个数据库对象,构造函数现在从池中获取一个连接。每个线程应该只调用其中一种方法(最多 5 个查询),然后结束。我每次都必须初始化所有准备好的语句吗?如果是,会不会花太长时间?

我有更好的方法吗?

您需要使用提供池化连接方式的数据源,然后您的应用程序从池中获取连接。

您可以在应用程序启动时像 in this example 这样编程创建数据源,或者您可以从网络服务器控制台(取决于网络服务器)进行配置,然后通过 JNDI 在您的应用程序中获取数据源

Prepared Statement 的预编译和 DB 端缓存导致整体执行速度更快,并且能够重用相同的 SQL 语句。

连接池的优点之一是它可以重复使用现有连接,这是您当前的实现无法做到的。所以问题 "wouldn't it take too long to initialize all the prepared statements each time?" 并不是真正相关的,因为每次创建一个新的数据库连接很可能比每次初始化准备好的语句花费更长的时间。即使准备好的语句每次都被初始化并且从不重复使用,我怀疑你会注意到任何性能差异,因为执行数据库语句比初始化准备好的语句花费更长的时间。

也就是说,大多数 JDBC 驱动程序都可以选择缓存准备好的语句(即这并不严格取决于连接池)。例如,参见 MySQL 配置选项 here (cachePrepStmts, prepStmtCacheSize and prepStmtCacheSqlLimit). But please keep in mind that these optimizations are "nice to have", first and foremost make sure your program works correctly in the multi-threaded scenario (e.g. ensure you always return a connection borrowed from the pool to the pool, even when (runtime) exceptions occur) and is maintainable