很多JDBC的教程和博文还在用较旧的方式来访问数据库。
利用 Java 6 的 JDBC 4.0 API 和 Java 7 的 try-with-resources 语法可以大大简化冗长的代码。

Java 7 以前的写法

可以看到有很多资源清理的操作,也要手动装载 JDBC 驱动

    private static final String URL = "jdbc:mysql://localhost";
    private static final String USER = "root";
    private static final String PASSWD = "secret";
    private static final String STATEMENT = "SHOW DATABASES";
    
    public static void priorJava7JDBC() {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");  // 注册驱动

            conn = DriverManager.getConnection(URL, USER, PASSWD); // 打开连接

            stmt = conn.createStatement();
            rs = stmt.executeQuery(STATEMENT);  // 执行语句

            // 处理结果
            while (rs.next()) {
                System.out.println(rs.getString("Database"));
            }
            // 资源清理
            rs.close();
            stmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeQuietly(rs);
            closeQuietly(stmt);
            closeQuietly(conn);
        }
    }

    public static void closeQuietly(AutoCloseable closeable) {
        try {
            if(closeable != null) {
                closeable.close();
            }
        } catch (Exception e) {
        }
    }

Java 7 之后的写法

通过try-with-resources 省略了资源清理的代码,类似与 Python 的 with 语法,也无需注册驱动

    public static void afterJava7JDBC() {
        try (Connection connection = DriverManager.getConnection(URL, USER, PASSWD);
             PreparedStatement stmt = connection.prepareStatement(STATEMENT)) {
            try (ResultSet rs = stmt.executeQuery()) {

                while(rs.next()) {
                    System.out.println(rs.getString("Database"));
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

try-with-resources 异常处理方式

try-with-resources 可以理解为自动调用 close 方法的语法糖,但异常处理有差异,看下面的例子

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

readFirstLineFromFileWithFinallyBlock方法如果 readLine 和 close 同时抛出异常,方法的异常会是 finally 语句块的异常。try 语句块的异常被suppressed。 相比之下,readFirstLineFromFile方法如果 readLine 和 try-with-resources 调 close 方法都抛出异常,方法的异常会是 try 语句块的异常,即 readLine 的异常, try-with-resources 的异常被suppressed。可以通过抛出的异常调用 Throwable.getSuppressed 方法获得 try-with-resources 的异常。

原文描述如下:

However, in this example, if the methods readLine and close both throw exceptions, then the method readFirstLineFromFileWithFinallyBlock throws the exception thrown from the finally block; the exception thrown from the try block is suppressed. In contrast, in the example readFirstLineFromFile, if exceptions are thrown from both the try block and the try-with-resources statement, then the method readFirstLineFromFile throws the exception thrown from the try block; the exception thrown from the try-with-resources block is suppressed. In Java SE 7 and later, you can retrieve suppressed exceptions; see the section Suppressed Exceptions for more information.

还可以更简便吗

上面的代码虽然有一定程度简化,但还是需要打开连接、执行语句、遍历结果集等操作, 通过 Spring 的 JdbcTemplate 配合 Java 8 的流式api以及lambda表达式可以让代码变得更加简洁,接近One-Liners。例如:

jdbcTemplate.query(
        "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[] { "Josh" },
        (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
).forEach(customer -> log.info(customer.toString()));

具体可看教程

See Also