猿问

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

我正在开发一个 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上面出现的大部分样板。我怎么能做到这一点?


开满天机
浏览 134回答 1
1回答

当年话下

您可以传入一个策略来告诉该方法如何将行映射到对象上的字段。这就是 spring-jdbc 所做的,它将RowMapper定义为:public interface RowMapper<T> {&nbsp; &nbsp; T mapRow(ResultSet resultSet, int rowNum) throws SQLException;}这就是您可以更改方法的方式,包括合并 rowMapper:public static <T> List<T> queryList(String query, Connection conn, RowMapper<T> rowMapper) throws SQLException {&nbsp; &nbsp; LOGGER.debug("Executing query = {}", query);&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; Statement stmt = conn.createStatement();&nbsp; &nbsp; &nbsp; &nbsp; ResultSet rs = stmt.executeQuery(query);&nbsp; &nbsp; &nbsp; &nbsp; List<> list = new ArrayList<>();&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; while (rs.next()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; list.add(rowMapper.mapRow(rs, list.size() + 1));&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return list;&nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; DBUtil.closeResultSet(rs);&nbsp; &nbsp; &nbsp; &nbsp; DBUtil.closeStatement(stmt);&nbsp; &nbsp; &nbsp; &nbsp; LOGGER.debug("Finished query = {}", query);&nbsp; &nbsp; }}在这里捕获所有异常并不好,因为如果出现问题,您希望能够使用异常来退出当前操作。否则,您将在日志中有多个错误堆栈跟踪,其中一个是发生错误的地方,另一个是下游,当您的代码期望出现不是由于前一个错误导致的结果时,您的代码会失败。当第一个错误发生时快速失败。下一步将参数化您的查询,因此您不必将参数值括在引号中或担心 sql 注入。有关spring-jdbc 如何处理此问题的示例,请参见此答案。我把连接的东西移出了方法;传入连接允许您在同一个 JDBC 本地事务中执行多个 sql 语句。(连接仍然需要关闭,这只是错误的地方。)同样在这里传递这个句柄是违反德墨忒耳法则的:特别是,一个对象应该避免调用另一个方法返回的成员对象的方法。对于许多使用点作为字段标识符的现代面向对象语言,该法则可以简单地表述为“仅使用一个点”。也就是说,代码 abMethod() 违反了 a.Method() 没有的规律。打个比方,想让狗走路,不是直接命令狗腿走路,而是直接让狗走路。取而代之的是命令狗,然后狗命令自己的腿。即使您只做只读的事情,让查询共享事务也可以提高一致性并提高性能。使用 Spring 使用事务不那么痛苦,您可以使用注释以声明方式实现事务,以显示您希望边界在哪里。这里的全局答案是:最好采用一个预先存在的工具(spring-jdbc 或类似的东西),它们已经解决了你甚至还没有考虑过的问题,而不是零碎地重新发明它,这就是你显然要走的路。
随时随地看视频慕课网APP

相关分类

Java
我要回答