您应该在需要时从连接池中获取连接,并尽快关闭连接。重用连接是连接池的工作,而不是你的。
换句话说:在您的消费者捆绑包被停用之前,不要让连接保持活动状态。
连接池通常实现 DataSource 接口,您应该通过它使用连接池。在这种情况下,您可以轻松替换池实现而无需更改代码。例如:
@Component
public class MyComponent {
// Connection pool based DataSource
@Reference
DataSource dataSource;
public void myFunction() {
try (Connection c = dataSource.getConnection()) {
// Database operations
} catch (SQLException e) {
// TODO
}
}
}
当您发现自己多次重复相同的代码(获取连接、捕获 SQLException)时,您可以编写一个接受函数式接口的简单组件。例如:
@Component
@Service
public class SQLHelper {
@Reference // This is a connection pool DataSource
private DataSource dataSource;
public <R> R execute(Callback<R> callback) {
try (Connection c = dataSource.getConnection()) {
return callback.call(c);
} catch (SQLException e) {
throw new UncheckedSQLException(e);
}
}
}
您的功能界面如下所示:
public interface Callback<R> {
R call(Connection connection);
}
你会这样使用它:
sqlHelper.execute((Connection c) -> {
// Do some stuff with the connection
});
使用交易
如果你想使用原子事务,我建议你应该使用 org.apache.derby.jdbc.EmbeddedXADataSource 和 org.apache.commons.dbcp.managed.BasicManagedDataSource 来自 commons-dbcp。之后就可以通过JTA处理事务了。
很难直接使用 JTA API。您应该选择一个可以帮助您传播交易的库。
基于声明式服务的小指南:
- 将 derby jar 安装到您的 OSGi 容器中
- 同时安装pax-derby 包!通过这样做,您将拥有一个 DataSourceFactory OSGi 服务
- 安装 everit-dsf-bundle 及其依赖项!您将看到两个新的 DS 组件。通过 web 控制台为名为 XADataSource 的配置创建一个配置!所有配置选项都有说明。
- 在 OSGi 容器中安装 JTA 事务管理器!你有几个选择。我
- 安装 everit-commons-dbcp-component 及其依赖项!您将看到两个新的 DS 组件。在 webconsole 中配置 Managed 并将之前创建的 XADataSource 设置为目标!如果您在同一事务的范围内请求并关闭连接,事务池将负责提供相同的连接。
通常使用嵌入 Geronimo TM 的 Aries 事务管理器。
- 将everit-transaction-helper 安装到您的OSGi 容器中!您将看到一个带有 TransactionHelper 接口的新 OSGi 服务(由可配置的 DS 组件提供)。
现在您可以编写代码了。您的组件类似于以下内容:
@Component
@Service
public class MyComponent {
@Reference
private DataSource dataSource;
@Reference
private TransactionHelper th;
public void myFunction() {
th.required(() -> {
try (Connection c = dataSource.getConnection()) {
// My SQL statements
} catch (SQLException e) {
// TODO
}
}
}
}
如果您不需要事务处理,您可以:
- 使用标准 EmbeddedDataSource
- 使用任何非事务性连接池
- 跳过安装 TransactionManager 和 TransactionHelper 包
- 从代码中跳过 TransactionHelper 的使用
http://cookbook.everit.org/persistence/index.html 上提供了更复杂的指南(还负责创建模式并使用基于 OO 的查询)。
更新
您不必为每个 SQL 语句都建立一个连接。您应该获得一个连接,在“瞬间”内执行尽可能多的 SQL 语句,然后在连接上调用 close。
- 如果你必须同时运行三个 SQL 语句,你应该请求一个连接,执行三个 SQL 语句,然后在连接上调用 close
- 如果您在从池中请求的同一函数中关闭请求的连接,则您可能做对了。您可以调用将连接作为参数传递的其他函数,但它们应该只使用它来运行 SQL 语句而不是返回。
- 您不应该保持连接并等待另一个用户操作。这就是连接池的工作。当您对池提供的连接调用 close 时,该连接不会物理关闭,而只会检索到池中。
- 您应该将连接对象保存在局部变量中。如果你为你的连接对象使用一个成员变量,你应该怀疑你的代码有问题(唯一的例外是如果你将连接传递给一个存在很短时间的对象并且该对象将连接保存在一个成员中变量以获得更清晰的代码)。
- 请注意,如果您使用 Java 6 或更早版本,则应在 finally 块中关闭连接以避免未关闭的连接。
MiniConnectionPoolManager 可能是嵌入式设备的绝佳解决方案,因为它确实是“迷你”的。唯一的问题是它没有实现 DataSource 接口,因此您的业务代码应该直接使用 MiniCPM 类。通过这样做,如果您发现错误或以后需要更复杂的池,则切换到其他连接池将变得更加困难。
如果您决定使用 MiniCPM,我建议您应该编写一个实现 DataSource 并将 getConnection() 函数委托给 MiniCPM 实例的组件。例如:
@Component
@Service
public class MiniCPMDataSourceComponent implements DataSource {
@Reference
protected ConnectionPoolDataSource cpDataSource;
private MiniConnectionPoolManager wrapped;
@Activate
public void activate() {
this.wrapped = new MiniConnectionPoolManager(cpDataSource);
}
@Override
public Connection getConnection() {
return wrapped.getConnection();
}
@Override
public Connection getConnection(String user, String password) {
throw new UnsupportedOperationException();
}
@Deactivate
public void deactivate() {
wrapped.dispose();
}
}
您可以使用最大连接数和超时(MiniCPM 支持)等配置可能性来装饰此组件。如果您使用该组件提供的服务,您将能够在不更改业务代码的情况下切换连接池。此外,您的业务捆绑包不会直接连接到 MiniCPM。