подключение к удаленной базе данных mysql через ssh с помощью java

Как я могу подключиться к удаленной базе данных MySQL через SSH из приложения java. Небольшой пример кода полезен для меня, и я был бы признателен за это.

5 ответов


Я понимаю, что вы хотите получить доступ к серверу mysql, работающему на удаленной машине и прослушивающему, скажем, порт 3306 через SSH-туннель.

чтобы создать такой туннель от порта 1234 на локальном компьютере до порта 3306 на удаленном компьютере с помощью ssh-клиента командной строки, введите следующую команду с локального компьютера:

ssh -L 1234:localhost:3306 mysql.server.remote

чтобы сделать то же самое с Java, вы можете использовать JSch, реализация Java SSH2. От его сайт:

JSch позволяет подключиться к серверу sshd и использовать переадресацию портов, переадресацию X11, передачу файлов и т. д., и вы можете интегрировать свою функциональность в свои собственные программы Java. Jsch лицензируется под лицензией BSD style.

например, посмотрите на PortForwardingL.java. После подключения сеанса создайте соединение JDBC с MySQL, используя что-то вроде jdbc:mysql://localhost:1234/[database] в качестве URL-адреса подключения.


мой код ниже:

package mypackage;
import java.sql.*;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

public class UpdateMySqlDatabase {
    static int lport;
    static String rhost;
    static int rport;
    public static void go(){
        String user = "ripon";
        String password = "wasim";
        String host = "myhost.ripon.wasim";
        int port=22;
        try
            {
            JSch jsch = new JSch();
            Session session = jsch.getSession(user, host, port);
            lport = 4321;
            rhost = "localhost";
            rport = 3306;
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            System.out.println("Establishing Connection...");
            session.connect();
            int assinged_port=session.setPortForwardingL(lport, rhost, rport);
            System.out.println("localhost:"+assinged_port+" -> "+rhost+":"+rport);
            }
        catch(Exception e){System.err.print(e);}
    }
    public static void main(String[] args) {
        try{
            go();
        } catch(Exception ex){
            ex.printStackTrace();
        }
          System.out.println("An example for updating a Row from Mysql Database!");
          Connection con = null;
          String driver = "com.mysql.jdbc.Driver";
          String url = "jdbc:mysql://" + rhost +":" + lport + "/";
          String db = "testDB";
          String dbUser = "wasim";
          String dbPasswd = "riponalwasim123";
          try{
          Class.forName(driver);
          con = DriverManager.getConnection(url+db, dbUser, dbPasswd);
          try{
          Statement st = con.createStatement();
          String sql = "UPDATE MyTableName " +
                  "SET email = 'ripon.wasim@smile.com' WHERE email='peace@happy.com'";

          int update = st.executeUpdate(sql);
          if(update >= 1){
          System.out.println("Row is updated.");
          }
          else{
          System.out.println("Row is not updated.");
          }
          }
          catch (SQLException s){
          System.out.println("SQL statement is not executed!");
          }
          }
          catch (Exception e){
          e.printStackTrace();
          }
          }
        }

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

public class CTestDriver {
    private static void doSshTunnel(String strSshUser, String strSshPassword, String strSshHost, int nSshPort,
            String strRemoteHost, int nLocalPort, int nRemotePort) throws JSchException {
        final JSch jsch = new JSch();
        Session session = jsch.getSession(strSshUser, strSshHost, 22);
        session.setPassword(strSshPassword);

        final Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);

        session.connect();
        session.setPortForwardingL(nLocalPort, strRemoteHost, nRemotePort);
    }

    public static void main(String[] args) {
        try {
            String strSshUser = "ssh_user_name"; // SSH loging username
            String strSshPassword = "abcd1234"; // SSH login password
            String strSshHost = "your.ssh.hostname.com"; // hostname or ip or
                                                            // SSH server
            int nSshPort = 22; // remote SSH host port number
            String strRemoteHost = "your.database.hostname.com"; // hostname or
                                                                    // ip of
                                                                    // your
                                                                    // database
                                                                    // server
            int nLocalPort = 3366; // local port number use to bind SSH tunnel
            int nRemotePort = 3306; // remote port number of your database
            String strDbUser = "db_user_name"; // database loging username
            String strDbPassword = "4321dcba"; // database login password

            CTestDriver.doSshTunnel(strSshUser, strSshPassword, strSshHost, nSshPort, strRemoteHost, nLocalPort,
                    nRemotePort);

            Class.forName("com.mysql.jdbc.Driver");
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost:" + nLocalPort, strDbUser,
                    strDbPassword);
            con.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.exit(0);
        }
    }
}

package framework.restapi.utils;

import java.sql.*;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

public class SQLConnection {
    private static Connection connection = null;
    private static Session session = null;

    private static void connectToServer(String dataBaseName) throws SQLException {
        connectSSH();
        connectToDataBase(dataBaseName);
    }

    private static void connectSSH() throws SQLException {
        String sshHost = "";
        String sshuser = "";
        String dbuserName = "";
        String dbpassword = "";
        String SshKeyFilepath = "/Users/XXXXXX/.ssh/id_rsa";

        int localPort = 8740; // any free port can be used
        String remoteHost = "127.0.0.1";
        int remotePort = 3306;
        String localSSHUrl = "localhost";
        /***************/
        String driverName = "com.mysql.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");

            Class.forName(driverName).newInstance();

            int assinged_port = session.setPortForwardingL(localPort, remoteHost, remotePort);

            System.out.println("localhost:" + assinged_port + " -> " + remoteHost + ":" + remotePort);
            System.out.println("Port Forwarded");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void connectToDataBase(String dataBaseName) throws SQLException {
        String dbuserName = "sf2_showpad_biz";
        String dbpassword = "lOAWEnL3K";
        int localPort = 8740; // any free port can be used
        String localSSHUrl = "localhost";
        try {

            //mysql database connectivity
            MysqlDataSource dataSource = new MysqlDataSource();
            dataSource.setServerName(localSSHUrl);
            dataSource.setPortNumber(localPort);
            dataSource.setUser(dbuserName);
            dataSource.setAllowMultiQueries(true);

            dataSource.setPassword(dbpassword);
            dataSource.setDatabaseName(dataBaseName);

            connection = dataSource.getConnection();

            System.out.print("Connection to server successful!:" + connection + "\n\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private static void closeConnections() {
        CloseDataBaseConnection();
        CloseSSHConnection();
    }

    private static void CloseDataBaseConnection() {
        try {
            if (connection != null && !connection.isClosed()) {
                System.out.println("Closing Database Connection");
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    private static void CloseSSHConnection() {
        if (session != null && session.isConnected()) {
            System.out.println("Closing SSH Connection");
            session.disconnect();
        }
    }


    // works ONLY FOR  single query (one SELECT or one DELETE etc)
    private static ResultSet executeMyQuery(String query, String dataBaseName) {
        ResultSet resultSet = null;

        try {
            connectToServer(dataBaseName);
            Statement stmt = connection.createStatement();
            resultSet = stmt.executeQuery(query);
            System.out.println("Database connection success");
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return resultSet;
    }

    public static void DeleteOrganisationReferencesFromDB(String organisationsLike) {
        try {
            connectToServer("ServerName");
            Statement stmt = connection.createStatement();

            ResultSet resultSet = stmt.executeQuery("select * from DB1");

            String organisationsToDelete = "";
            List<String> organisationsIds = new ArrayList<String>();

            // create string with id`s values to delete organisations references
            while (resultSet.next()) {
                String actualValue = resultSet.getString("id");
                organisationsIds.add(actualValue);
            }

            for (int i = 0; i < organisationsIds.size(); i++) {
                organisationsToDelete = " " + organisationsToDelete + organisationsIds.get(i);
                if (i != organisationsIds.size() - 1) {
                    organisationsToDelete = organisationsToDelete + ", ";
                }
            }

            stmt.executeUpdate(" DELETE FROM `DB1`.`table1` WHERE `DB1`.`table1`.`organisation_id` in ( " + organisationsToDelete + " );");


        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeConnections();
        }
    }

    public static List<String> getOrganisationsDBNamesBySubdomain(String organisationsLike) {
        List<String> organisationDbNames = new ArrayList<String>();
        ResultSet resultSet = executeMyQuery("select `DB`.organisation.dbname from `DB1`.organisation where subdomain like '" + organisationsLike + "%'", "DB1");
        try {
            while (resultSet.next()) {
                String actualValue = resultSet.getString("dbname");
                organisationDbNames.add(actualValue);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeConnections();
        }
        return organisationDbNames;
    }

     public static List<String> getAllDBNames() {
        // get all live db names incentral DB
        List<String> organisationDbNames = new ArrayList<String>();
        ResultSet resultSet = executeMyQuery("show databases", "DB1");
        try {
            while (resultSet.next()) {
                String actualValue = resultSet.getString("Database");
                organisationDbNames.add(actualValue);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeConnections();
        }
        return organisationDbNames;
    }

      public static void deleteDataBasesByName(List<String> DataBasesNamesList) {
        try {
            connectSSH();
            int dataBasesAmount = DataBasesNamesList.size();
            for (int i = 0; i < dataBasesAmount; i++) {
                connectToDataBase(DataBasesNamesList.get(i));

                Statement stmt = connection.createStatement();
                stmt.executeUpdate("DROP database `" + DataBasesNamesList.get(i) + "`");

            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            CloseDataBaseConnection();
            closeConnections();
        }
    }
}

прежде всего, спасибо отлично работает!

хотя, интересно, должен ли я повторно использовать этот сеанс для каждого (потенциально одновременного) соединения SQL, или если я должен создавать новый сеанс каждый раз и только обновлять его, если по какой-то причине он истек.

В настоящее время я бы каждый раз, когда я делаю соединение, делаю новый экземпляр этого контроллера здесь, а затем выполняю SQL-запросы с подключением, которое я получил от него, а затем закрываю его вручную.

также будет хорошо, если бы я мог сделать класс полезным с помощью try-with-resource и он закрывается. Разберусь с этим. Потому что я не хочу пропустить закрытие.

вот как это выглядит, я получаю соединения DB прямо сейчас.

public class ConnectionManager {

private Connection con = null;
private Session session = null;

public Connection getConnection() {
    Connection con = null;

    var settings = new DbSettingsController();

    boolean useSSH = settings.getSetting(SettingKey.UseSSH).equals("true");
    String sshPort = settings.getSetting(SettingKey.SSHPort);
    String sqlIp = settings.getSetting(SettingKey.MySqlIP);
    String sqlPort = settings.getSetting(SettingKey.MySqlPort);

    if(useSSH) {
        JSch jSch = new JSch();
        try {
            this.session = jSch.getSession(settings.getSetting(SettingKey.SSHUser),
                    settings.getSetting(SettingKey.SSHHost),
                    Integer.valueOf(sshPort));
            this.session.setPassword(settings.getSetting(SettingKey.SSHPassword));
            this.session.setConfig("StrictHostKeyChecking", "no");
            this.session.connect();
            this.session.setPortForwardingL(Integer.parseInt(sshPort), sqlIp, Integer.parseInt(sqlPort));
        } catch (JSchException e) {
            e.printStackTrace();
        }
    }

    var connectionString = String.format("jdbc:mysql://%s:%s/%s?autoReconnect=true&useSSL=false",
            sqlIp, useSSH ? sshPort : sqlPort,
            settings.getSetting(SettingKey.MySqlShema));

    var user = settings.getSetting(SettingKey.MySqlUser);
    var password = settings.getSetting(SettingKey.MySqlPassword);

    try {
        con = DriverManager.getConnection(connectionString, user, password);
    } catch (SQLException e) {
        e.printStackTrace();
    }

    return con;
}

public void close() {
    if(this.con != null) {
        try {
            this.con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if(this.session != null) {
        this.session.disconnect();
    }
}

Если вам интересно, что DbSettingsController Ive тоже сделал, просто помещает настройки в текстовый столбец в локальную БД SQLite с назначенным ему ключом (который перечисляет значение int). Просто скопировал код вставки, который я повторно использовал из другого проекта, так что это было просто и быстро сделать это таким образом.