如何结合连接池使用JDBC Swing Worker并实现SQL与业务逻辑分离
问题1:当前实现的效率是否合理?是否更推荐维持数小时长连接?
- 你当前使用连接池+try-with-resources的实现效率是合理的,远优于维持数小时长连接的方案。
- 长连接闲置时会占用数据库服务器连接资源,还容易因网络波动、数据库超时配置被断开,后续使用会抛出连接失效异常,还要额外开发保活逻辑,得不偿失。
- 连接池本身已经做了连接复用优化,每次请求从池里取连接的开销可以忽略不计,用完归还连接也不会真的关闭物理连接,完全不会有性能浪费。
- 只有当你单次查询的结果集特别大、遍历需要几十分钟级别,才需要让连接在遍历期间保持打开,你现在的实现本身就是这么做的,遍历结束才归还连接,完全符合要求。
问题2:如何拆分JDBC逻辑和GUI逻辑,实现分层?
可以用回调函数/函数式接口解耦,完全不需要把JDBC代码和Swing Worker的publish逻辑混在一起:
- 先在
Sql类里定义函数式接口,用来处理每一行的结果:
@FunctionalInterface public interface RowHandler { void handle(Row row); }
- 把JDBC查询逻辑封装到
Sql类的方法里,接收RowHandler作为参数,遍历结果集的时候调用回调:
public void queryForRows(String query, RowHandler handler) throws SQLException { try (Connection conn = getDataSource().getConnection(); PreparedStatement statement = conn.prepareStatement(query); ResultSet rs = statement.executeQuery()) { while (rs.next()) { Row r = new Row(); // 原来的Row映射逻辑放到这里,和JDBC相关的都留在Sql类 handler.handle(r); } } }
- Swing Worker的doInBackground里只需要调用Sql类的方法,把publish方法作为回调传进去就行:
protected List<Row> doInBackground() { try { sql.queryForRows(query, this::publish); } catch (SQLException e) { e.printStackTrace(System.err); } return null; }
这样拆分之后,Sql类完全不知道GUI的存在,只负责JDBC操作和行映射,Worker只负责调度和GUI相关的publish逻辑,完全没有分层耦合的问题,同时连接还是由try-with-resources自动关闭,不会泄漏,也不需要长期持有连接。
问题3:能否让ResultSet遍历结束后自动关闭对应连接?
你现在用的try-with-resources本身就是Java原生提供的自动关闭资源的最佳实现,完全可以满足需求:
- try-with-resources会在代码块执行完成(包括正常结束、抛出异常中断)的时候,按声明的逆序自动关闭所有实现了
AutoCloseable接口的资源,Connection、Statement、ResultSet都实现了这个接口。 - 如果你确实需要把ResultSet返回给外层使用(不推荐,ResultSet是绑定连接的低级别对象),可以用离线的
CachedRowSet把结果集的数据读到内存里,这样就可以关闭底层连接之后再使用数据,但如果你的结果集特别大,会占用过多内存,还是推荐用上面的回调方案更合理。 - 不要依赖GC销毁ResultSet的时候关闭连接,GC的时机是不可控的,很容易造成连接泄漏,必须手动或者用try-with-resources显式管理连接生命周期。
内容的提问来源于stack exchange,提问作者Philip Leifeld
相关产品推荐
相关产品推荐

