JDBC 基本概念、池化和线程化
Posted
技术标签:
【中文标题】JDBC 基本概念、池化和线程化【英文标题】:JDBC fundamental concepts, Pooling and Threading 【发布时间】:2010-11-19 08:05:18 【问题描述】:我一直在单线程环境的 JavaSE 中使用 JDBC。但是现在我需要使用一个连接池并让许多线程与数据库(MSSQL 和 Oracle)进行交互,我很难做到这一点,因为我似乎对 api 缺乏一些基本的理解。
连接并记录Connection
后的AFAIK 表示与数据库的物理tcp/ip 连接。它创建Statement
(s),可以看作是通过Connection
与数据库进行SQL交互。
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() 将获得健康连接?非常感谢,请原谅我的英语不好。
【问题讨论】:
【参考方案1】:事务发生在连接级别。
没有。通常,JDBC 驱动程序会确保您不能在同一连接上执行第二条语句,而另一个语句处于活动状态。
如果您需要连接池,请尝试DBCP framework。它提供了相当不错的故障处理(比如注意到过时的连接和客户端代码没有返回的连接)。
至于您的代码:始终将代码包装在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;
此代码将确保所有连接等始终正确关闭,并且关闭期间的任何异常都不会隐藏以前的错误。
【讨论】:
天哪,我喜欢 java,一切都有一个框架 :D 感谢 Aaron 的回答。但是,关于框架对陈旧连接的作用,难道不是任何池实现(oracle 或 mssql 驱动程序)都应该默认做的吗? “应该”!=“确实”!=“做得很好”。您可以在 DBCP 中设置一个超时,上面写着“如果一个小时后没有返回连接,则客户端中存在错误,所以让我们重用它”。至于“陈旧”,您需要提供自定义 SQL 来检查连接是否已死。如果 JDBC 会定义它会很好,但它没有,并且大多数 DB 供应商甚至不费心支持很多 JDBC 规范。例如,在 Oracle 中,TIMESTAMP 不是 java.sql.Timestamp。【参考方案2】:我认为您应该从连接池的 Sun tutorial 开始。除此之外,还有很多连接池的实现,一些开源的,包括one from Apache。您应该真正从那里开始,而不是在这里重新发明***。
【讨论】:
Em,对不起,我不是想重新发明一些东西。本教程建议使用连接包装器 JDCConnectionManager。但是我使用的驱动程序已经有了它们的池化数据源实现。 您使用的是什么驱动程序?另外,你看过 Apache 的实现吗?如果您需要的不仅仅是基本的 JDBC,那可能会为您解决问题。 ojdbc14.jar 和 jtds-1.2.2.jar。如果驱动程序已经提供了它,我为什么要尝试 apache 的池实现? 好吧,您有两个不同的数据库正在连接,Apache 实现(或可能的其他实现)将使您能够拥有一个与其中任何一个交互的池。但我不再清楚你的问题。起初,您似乎正在编写自己的池实现(如 Sun 教程)。现在您似乎想使用现有的。我不知道 Oracle 有什么,但 jTDS 只是给你一个适合池的连接,它没有给你实际的池实现。【参考方案3】:连接池使用自己的包装器实现来装饰 Connection 和 Statement 实例。当您在连接上调用 close 时,您实际上只是将其释放回池中。当您在准备好的语句上调用 close 时,您实际上只是将其释放回连接的语句缓存。当您准备语句时,您可能只是从连接中获取缓存的语句实例。所有这些都隐藏在视图之外,因此您不必担心。
当一个连接被提供给一个客户端时,它不再可供任何其他客户端使用,直到该连接被释放回池中。您通常只在需要时获取连接,然后在完成后立即返回它们。由于连接在池中保持打开状态,因此获取和释放连接的开销很小。
您应该像使用单个 JBDC 连接一样使用池中的连接,并遵循有关关闭资源的最佳实践,以免泄漏任何连接或语句。请参阅其他一些答案中的 try/catch/finally 示例。
池可以管理连接资源并在将它们分发给客户端之前对其进行测试,以确保它们不会过时。此外,池将根据需要创建和销毁连接。
【讨论】:
哇!,感谢teabot 的另一件事,一旦你有一个连接。为什么 api 将要在数据库中进行的操作拆分为 Statement 对象而不是仅使用连接对象?对于准备好的陈述,我理解这一点。但是对于正常的语句,是否只是为了让开发者说N个可能执行或不执行的操作,或者可能执行多次,只是为了这个? 只是猜测,但是:正如您显然意识到的那样,PreparedStatements 必须与 Connections 分开,因为每个 Connection 可能有多个 PreparedStatement。所以我想 Java 人把 Statement 分开来保持它们与 PreparedStatements 平行。否则,您将在 Connection 中有一大堆“语句函数”,然后在 PreparedStatement 中有一大堆。那会弄乱 Statement 和 PreparedStatement 的继承树。正如我所说,只是猜测。如果有人对此有权威消息来源,我会很高兴听到它。【参考方案4】:您只能在任何给定的连接上保持一个语句打开。使用连接池创建多个连接并没有那么困难,尽管要走的路是使用最常用的连接之一。
另外,如果你打算使用标准 JDBC,我建议使用 PreparedStatement 而不是 Statement。
我一直在使用 iBatis,开箱即用非常好。还带来了一些其他的东西。
【讨论】:
【参考方案5】:看看this (+:
【讨论】:
【参考方案6】:额外的位:
应用服务器倾向于提供连接池,它可以变得相当聪明。如果您使用的是应用服务器,请在添加自己的任何内容之前仔细调查开箱即用的内容。
交易:如果你有
开始交易
获取连接 工作 关闭连接 // 意味着返回池
获取连接(具有相同的隔离级别等) // 您将获得 SAME 连接,池为您的交易保留它
work // 发生在同一个事务中 关闭连接
提交事务 // 提交所有工作
连接和错误
池实现可以很聪明。如果池中的任何一个连接遇到某些错误,表明数据库服务器已反弹,则池可以选择丢弃所有池成员。
【讨论】:
【参考方案7】:如果您已经掌握了单线程 JDBC,那么使用多线程和连接池应该没什么大不了的。您需要做的不同是: 1. 当您需要连接时,从池中获取它,而不是直接获取。 2. 每个线程都应该有自己的连接。
澄清第 2 点:如果您获得一个连接,然后将其传递给多个线程,则可能有两个线程尝试同时针对同一个连接执行查询。 Java 将对此抛出异常。每个连接只能有一个活动语句,每个语句只能有一个活动查询(即结果集)。如果两个线程都持有同一个 Connection 对象,它们很可能会立即违反此规则。
另一个警告:使用连接池时,请务必非常小心,在完成后始终关闭连接。池管理器没有明确的方法可以知道您何时完成连接,因此如果您未能关闭连接,它会在那里悬空很长时间,可能永远取决于池管理器。我总是总是在每个“getConnection”后面加上一个 try 块,并在 finally 块中关闭连接。然后我知道我已经在函数退出之前关闭了它。
除此之外,一切都应该和你习惯的一样。
【讨论】:
不知道是不是版本问题,“Java会在这个上面抛出异常”在最近的JDBC中不是真的。 @yuxh 对不起,我想我的陈述模棱两可。我不是说,如果你尝试这样做,Java 会立即自动抛出异常。我的意思是,你有可能创造一种会导致异常的情况。您可能会在任何给定应用程序的任何给定运行中找到一种方法。以上是关于JDBC 基本概念、池化和线程化的主要内容,如果未能解决你的问题,请参考以下文章