You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java SSH隧道连VPN数据库查询结果为空,需验证评论数一致性

解决SSH隧道连接数据库后查询结果为空的问题

我看你遇到了通过SSH隧道连接数据库后,控制台显示SSH已连接但数据库查询结果为空的问题,咱们一步步来排查和解决:

核心问题分析

你的代码里有几个关键配置错误,导致数据库连接实际上没正确建立,所以查询返回空结果:

1. 数据库连接地址配置错误

在connectToDataBase方法中,你把localSSHUrl设置成了远程数据库的IP,但SSH端口转发的作用是把本地端口映射到远程数据库端口,所以应该连接localhost(本地转发端口),而不是直接连远程数据库IP。另外你注释掉了setPortNumber(localPort),必须指定本地转发的端口(8740)才能正确连接。

2. MySQL驱动类适配问题

如果你的MySQL版本是8.0及以上,com.mysql.jdbc.Driver已经被废弃,应该使用com.mysql.cj.jdbc.Driver,而且JDBC 4.0+不需要手动调用Class.forName(...).newInstance()来加载驱动。

3. 资源泄漏与ResultSet遍历时机问题

你的代码中没有正确关闭Statement和ResultSet,而且在executeMyQuery返回ResultSet后,后续方法在finally块中关闭连接,虽然遍历是在关闭前,但如果ResultSet依赖连接存活(默认行为),一旦连接关闭ResultSet就无法访问。这不是当前查询为空的直接原因,但必须修复避免后续问题。

修正后的关键代码

修正connectSSH方法

public static void connectSSH() throws SQLException {
    String sshHost = "my ssh host";
    String sshuser = "my ssh user";
    String SshKeyFilepath = "/Users/mac/.ssh/id_rsa";
    int localPort = 8740;
    String remoteHost = "ip db";
    int remotePort = 3306;

    // MySQL 8+使用新版驱动类
    String driverName = "com.mysql.cj.jdbc.Driver";
    try {
        java.util.Properties config = new java.util.Properties();
        JSch jsch = new JSch();
        session = jsch.getSession(sshuser, sshHost, 22);
        jsch.addIdentity(SshKeyFilepath);
        config.put("StrictHostKeyChecking", "no");
        config.put("ConnectionAttempts", "3");
        session.setConfig(config);
        session.connect();
        System.out.println("SSH Connected");

        // JDBC 4.0+可省略驱动注册,但保留也没问题
        Class.forName(driverName);

        int assigned_port = session.setPortForwardingL(localPort, remoteHost, remotePort);
        System.out.println("localhost:" + assigned_port + " -> " + remoteHost + ":" + remotePort);
        System.out.println("Port Forwarded");
    } catch (Exception e) {
        e.printStackTrace();
        // 抛出异常让上层处理,避免SSH连接失败后继续执行数据库操作
        throw new SQLException("Failed to establish SSH connection", e);
    }
}

修正connectToDataBase方法

public static void connectToDataBase(String dataBaseName) throws SQLException {
    String dbuserName = "username db";
    String dbpassword = "pass db";
    int localPort = 8740;
    // SSH转发后,连接本地localhost,而非远程数据库IP
    String localSSHUrl = "localhost";

    try {
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setServerName(localSSHUrl);
        // 必须指定本地转发的端口
        dataSource.setPortNumber(localPort);
        dataSource.setUser(dbuserName);
        dataSource.setAllowMultiQueries(true);
        dataSource.setPassword(dbpassword);
        dataSource.setDatabaseName(dataBaseName);
        // MySQL 8+必须配置时区,否则可能连接失败
        dataSource.setServerTimezone("UTC");

        connection = dataSource.getConnection();
        System.out.print("Connection to server successful!:" + connection + "\n\n");
    } catch (Exception e) {
        e.printStackTrace();
        throw new SQLException("Failed to connect to database", e);
    }
}

修复executeMyQuery的资源管理

public static List<Map<String, Object>> executeMyQuery(String query, String dataBaseName) {
    List<Map<String, Object>> resultList = new ArrayList<>();
    try (Connection conn = getConnection(dataBaseName);
         Statement stmt = conn.createStatement();
         ResultSet resultSet = stmt.executeQuery(query)) {

        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();

        while (resultSet.next()) {
            Map<String, Object> row = new HashMap<>();
            for (int i = 1; i <= columnCount; i++) {
                row.put(metaData.getColumnName(i), resultSet.getObject(i));
            }
            resultList.add(row);
        }
        System.out.println("Database query executed successfully, returned " + resultList.size() + " rows");
        return resultList;
    } catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException("Query execution failed", e);
    } finally {
        closeConnections();
    }
}

// 重构获取连接的方法,避免重复代码
private static Connection getConnection(String dataBaseName) throws SQLException {
    connectSSH();
    connectToDataBase(dataBaseName);
    return connection;
}

修正getAllDBNames方法(使用新的查询方法)

public static List<String> getAllDBNames() {
    List<Map<String, Object>> queryResult = executeMyQuery("show databases", "DB1");
    List<String> organisationDbNames = new ArrayList<>();
    for (Map<String, Object> row : queryResult) {
        organisationDbNames.add(row.get("Database").toString());
    }
    return organisationDbNames;
}

测试建议

  1. 先确认SSH隧道是否真的生效:用本地MySQL客户端连接localhost:8740,输入数据库用户名和密码,看是否能正常访问数据库。如果客户端也连不上,说明SSH端口转发有问题,检查SSH主机、用户名、密钥路径是否正确。
  2. 运行修正后的代码,查看控制台输出的连接信息,确认数据库连接成功。
  3. 测试getAllDBNames方法,看是否能返回数据库列表。

内容的提问来源于stack exchange,提问作者Prasetyo Budi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:54:01