Java 连接 MySQL 8:驱动、TLS 与资源管理

505 字
3 分钟
Java 连接 MySQL 8:驱动、TLS 与资源管理

目标#

从 Java 应用连接 MySQL 8,执行一条参数化查询,并保证连接、语句和结果集被可靠关闭。

添加驱动#

Maven 项目添加 MySQL Connector/J。版本应以 Maven Central 和 MySQL 官方文档中的当前稳定版为准:

<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
  <version>当前稳定版</version>
</dependency>

JDBC 4 以后通常不需要手动调用 Class.forName(),驱动会通过 Service Provider 自动注册。

配置连接#

不要把用户名和密码写入源码。示例从环境变量读取:

export DB_URL='jdbc:mysql://db.example.com:3306/app?sslMode=VERIFY_IDENTITY&serverTimezone=UTC'
export DB_USER='app_user'
export DB_PASSWORD='replace-with-secret'

生产环境应使用密钥管理服务或受权限保护的运行时配置。不要为解决证书问题设置 useSSL=false;应配置可信 CA,并使用 VERIFY_CA 或 VERIFY_IDENTITY。

最小查询示例#

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        String url = requireEnvironment("DB_URL");
        String user = requireEnvironment("DB_USER");
        String password = requireEnvironment("DB_PASSWORD");
        String sql = "SELECT id, name FROM users WHERE status = ?";

        try (
            Connection connection = DriverManager.getConnection(url, user, password);
            PreparedStatement statement = connection.prepareStatement(sql)
        ) {
            statement.setString(1, "active");
            try (ResultSet results = statement.executeQuery()) {
                while (results.next()) {
                    System.out.printf(
                        "%d %s%n",
                        results.getLong("id"),
                        results.getString("name")
                    );
                }
            }
        } catch (SQLException error) {
            System.err.printf(
                "Database operation failed, SQLState=%s, code=%d%n",
                error.getSQLState(),
                error.getErrorCode()
            );
        }
    }

    private static String requireEnvironment(String name) {
        String value = System.getenv(name);
        if (value == null || value.isBlank()) {
            throw new IllegalStateException(name + " is required");
        }
        return value;
    }
}

参数化查询把数据与 SQL 结构分开,可以避免由字符串拼接引入的 SQL 注入。try-with-resources 按相反顺序自动关闭资源。

连接池与事务#

Web 服务不应为每个业务调用手动创建长期连接,应使用 HikariCP 等连接池。多条写操作必须作为一个原子操作时,关闭自动提交并显式提交;异常时回滚。

验证#

  1. 使用最小权限数据库账号,只授予应用需要的库表操作。
  2. 正确凭据能查询,错误凭据只输出诊断信息,不泄露密码。
  3. 证书主机名不匹配时连接应失败。
  4. 应用关闭后,数据库侧没有遗留空闲连接。

参考#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java 连接 MySQL 8:驱动、TLS 与资源管理
https://blog.mintalix.com/posts/study-link-mysql/
作者
Mint
发布于
2021-07-12
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Mint
软件开发、工程实践与技术思考。
分类
标签