8

我一直在单线程环境的 JavaSE 中使用 JDBC。但是现在我需要使用一个连接池并让许多线程与数据库(MSSQL 和 Oracle)进行交互,我很难做到这一点,因为我似乎对 api 缺乏一些基本的理解。

连接并记录 a 后的 AFAIKConnection表示与数据库的物理 tcp/ip 连接。它创建Statement的(s)可以看作是与数据库的 SQL 交互Connection

  • 事务和回滚从何而来?是在Connection还是Statement级别。
  • “一个”Connection创建N条语句并将其提供给不同的线程以便让每个人都拥有它的使用是否安全Statement

如果没有,并且在配置池之后是这样的:

OracleDataSource ods = new OracleDataSource(); 
ods.setURL("jdbc:oracle:thin:@tnsentryname");
ods.setUser("u");
ods.setPassword("p");
  • 顺便说一句,我在哪里设置连接池大小?

  • 这是为了正确使用连接我将在每个线程中执行的操作吗?

//头部运行方法

Connection conn = ods.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("the sql");
// do what I need to do with rs
rs.close();
int updateStatus = stmt.executeUpdate("the update");
stmt.close();
conn.close();

// 线程运行方法结束

  • 如果池的任何物理连接以某种方式崩溃或断开连接,池是否会自动尝试重新连接并将新连接注入池中,以便后续 pool.getConnection() 将获得健康连接?

非常感谢,请原谅我糟糕的英语。

4

7 回答 7

8

如果您已经掌握了单线程的 JDBC,那么使用多线程和连接池应该没什么大不了的。您需要做的不同是: 1. 当您需要连接时,从池中获取它,而不是直接获取。2. 每个线程都应该有自己的连接。

澄清第 2 点:如果您获得一个连接,然后将其传递给多个线程,则可能有两个线程尝试同时针对同一连接执行查询。Java 将对此抛出异常。每个连接只能有一个活动语句,每个语句只能有一个活动查询(即结果集)。如果两个线程都持有相同的 Connection 对象,它们很可能会立即违反此规则。

另一个警告:使用连接池,完成后始终要非常小心地关闭连接。池管理器没有明确的方法可以知道您何时完成连接,因此如果您未能关闭连接,它会在那里悬空很长时间,可能永远取决于池管理器。我总是总是在每个“getConnection”后面加上一个 try 块,并在 finally 块中关闭连接。然后我知道我在函数退出之前已经关闭了它。

除此之外,一切都应该和你习惯的一样。

于 2009-08-13T17:01:53.753 回答
7

连接池使用自己的包装器实现来装饰 Connection 和 Statement 实例。当您在连接上调用 close 时,您实际上只是将其释放回池中。当您在准备好的语句上调用 close 时,您实际上只是将其释放回连接的语句缓存。当您准备语句时,您可能只是从连接中获取缓存的语句实例。所有这一切都隐藏在视线之外,因此您不必担心。

当一个连接被提供给一个客户端时,它不再可供任何其他客户端使用,直到该连接被释放回池中。您通常只在需要时获取连接,然后在完成后立即返回它们。因为连接在池中保持打开状态,所以获取和释放连接的开销很小。

您应该像使用单个 JBDC 连接一样使用池中的连接,并遵循有关关闭资源的最佳实践,以免泄漏任何连接或语句。请参阅其他一些答案中的 try/catch/finally 示例。

池可以管理连接资源并在将它们分发给客户端之前对其进行测试,以确保它们不会过时。此外,池将根据需要创建和销毁连接。

于 2009-08-13T15:02:39.547 回答
3
  1. 事务发生在连接级别。

  2. 不会。通常,JDBC 驱动程序会确保您不能在同一连接上执行第二条语句,而另一个语句处于活动状态。

如果您需要连接池,请尝试DBCP 框架。它提供了相当不错的故障处理(比如注意到过时的连接和客户端代码没有返回的连接)。

至于您的代码:始终将代码包装在try{...}finally{...}

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
     conn = ds.getConnection ();
     stmt = ...
     rs = ...
}
finally {
     rs = close (rs);
     stmt = close (stmt);
     conn = close (conn);
}

public static Connection close (Connection conn) {
    if (conn != null) {
        try {
            conn.close ();
        }
        catch (SQLException e) {
            e.printStackTrace(); // Log, don't rethrow!!
        }
    }
    return null;
}

此代码将确保所有连接等始终正确关闭,并且关闭期间的任何异常都不会隐藏先前的错误。

于 2009-08-13T14:53:19.053 回答
2

我认为您应该从有关连接池的 Sun教程开始。除此之外,还有许多连接池的实现,一些是开源的,包括来自 Apache 的一个。你真的应该从那里开始,而不是在这里重新发明轮子。

于 2009-08-13T15:02:10.780 回答
1

您只能在任何给定的连接上保持一个声明打开。使用连接池创建多个连接并不是那么困难,尽管要走的路是使用其中一种更常用的连接。

此外,如果您打算使用标准 JDBC,我建议使用 PreparedStatement 而不是 Statement。

我一直在使用 iBatis,开箱即用非常好。还带来了一些其他的东西。

于 2009-08-13T15:04:44.003 回答
0

看看这个(+:

于 2009-08-13T15:27:43.927 回答
0

额外位:

  1. 应用服务器倾向于提供连接池,它可以变得相当聪明。如果您使用的是应用服务器,请在添加您自己的任何内容之前仔细调查开箱即用的内容。

  2. 交易:如果你有

    开始交易

    获取连接工作关闭连接//意味着返回池

    获取连接(具有相同的隔离级别等)
    //您将获得相同的连接,池为您的事务保留它

    工作 // 发生在同一个事务关闭连接中

    提交事务 // 提交所有工作

  3. 连接和错误

池实现可以很聪明。如果池中的任何一个连接遇到某些错误,表明数据库服务器已反弹,则池可以选择丢弃所有池成员。

于 2009-08-13T15:33:28.987 回答