为了账号安全,请及时绑定邮箱和手机立即绑定

当我需要在关闭 ResultSet 之前从 ResultSet 填充时,如何抽象出 JDBC 代码?

当我需要在关闭 ResultSet 之前从 ResultSet 填充时,如何抽象出 JDBC 代码?

开满天机 2022-04-28 16:48:28
我正在开发一个 Java 应用程序,该应用程序对数据库进行大量 JDBC 查询,每个查询都在一个单独的方法中指定,格式大致如下。public static void sampleJDBCQuery(String query, DBUtil dbUtil, DataStructure dataStructure) {    ResultSet rs = null;    Handle handle = null;    Connection conn = null;    Statement stmt = null;    LOGGER.debug("Executing query = {}", query);    try {        handle = dbUtil.getConnectionHandle();        conn = handle.getConnection();        if (conn != null) {            stmt = conn.createStatement();            if (stmt != null) {                rs = stmt.executeQuery(query);                if (rs != null) {                    while (rs.next()) {                        // populate dataStructure using rs                    }                }            }        }    } catch (Exception e) {        Metrics.markMeter("vertica.read.error");        LOGGER.error(e.getMessage(), e);    } finally {        DBUtil.closeResultSet(rs);        DBUtil.closeStatement(stmt);        DBUtil.close(handle);        LOGGER.debug("Finished query = {}", query);    }}使用上述格式的许多不同示例查询(和数据结构类),我的代码库一直在大幅增长。我的目标是拥有一个帮助方法,为我抽象出大部分 JDBC 逻辑。我的第一个想法是有一个具有以下签名的方法。public static ResultSet executeJDBCQuery(String query, DBUtil dbUtil)然后我可以遍历 的行并为每一行ResultSet填充相关内容。DataStructure问题是我仍然必须关闭返回ResultSet并ResultSet以不同的方法关闭似乎是糟糕的设计。我想我正在寻找可能类似于 Python 的函数装饰器概念的东西,这样我就可以“装饰”一个 JDBC 查询来处理sampleJDBCQuery上面出现的大部分样板。我怎么能做到这一点?
查看完整描述

1 回答

?
当年话下

TA贡献1890条经验 获得超9个赞

您可以传入一个策略来告诉该方法如何将行映射到对象上的字段。


这就是 spring-jdbc 所做的,它将RowMapper定义为:


public interface RowMapper<T> {

    T mapRow(ResultSet resultSet, int rowNum) throws SQLException;

}

这就是您可以更改方法的方式,包括合并 rowMapper:


public static <T> List<T> queryList(String query, Connection conn, RowMapper<T> rowMapper) throws SQLException {

    LOGGER.debug("Executing query = {}", query);

    try {

        Statement stmt = conn.createStatement();

        ResultSet rs = stmt.executeQuery(query);

        List<> list = new ArrayList<>();    

        while (rs.next()) {

            list.add(rowMapper.mapRow(rs, list.size() + 1)); 

        }

        return list;

    } finally {

        DBUtil.closeResultSet(rs);

        DBUtil.closeStatement(stmt);

        LOGGER.debug("Finished query = {}", query);

    }

}

在这里捕获所有异常并不好,因为如果出现问题,您希望能够使用异常来退出当前操作。否则,您将在日志中有多个错误堆栈跟踪,其中一个是发生错误的地方,另一个是下游,当您的代码期望出现不是由于前一个错误导致的结果时,您的代码会失败。当第一个错误发生时快速失败。


下一步将参数化您的查询,因此您不必将参数值括在引号中或担心 sql 注入。有关spring-jdbc 如何处理此问题的示例,请参见此答案。


我把连接的东西移出了方法;传入连接允许您在同一个 JDBC 本地事务中执行多个 sql 语句。(连接仍然需要关闭,这只是错误的地方。)


同样在这里传递这个句柄是违反德墨忒耳法则的:


特别是,一个对象应该避免调用另一个方法返回的成员对象的方法。对于许多使用点作为字段标识符的现代面向对象语言,该法则可以简单地表述为“仅使用一个点”。也就是说,代码 abMethod() 违反了 a.Method() 没有的规律。打个比方,想让狗走路,不是直接命令狗腿走路,而是直接让狗走路。取而代之的是命令狗,然后狗命令自己的腿。


即使您只做只读的事情,让查询共享事务也可以提高一致性并提高性能。使用 Spring 使用事务不那么痛苦,您可以使用注释以声明方式实现事务,以显示您希望边界在哪里。


这里的全局答案是:最好采用一个预先存在的工具(spring-jdbc 或类似的东西),它们已经解决了你甚至还没有考虑过的问题,而不是零碎地重新发明它,这就是你显然要走的路。


查看完整回答
反对 回复 2022-04-28
  • 1 回答
  • 0 关注
  • 133 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信