Springから仮想サーバーのMySQLに繋ぐには(未解決）

##概要
SpringBoot＋Thymeleaf+Javaで組み込みDBのH2databaseを使用していたが、仮想サーバーにMySQLを置いて繋ぐべきと指示を受けたのでDBを置き換えようと奮闘中。
かれこれ１週間以上大量のエラーログに吐き出され、ハマっていて~~まだ未解決~~なので、備忘録＋頭の中を整理する為、ここまでで正しそうなところをメモしておく。
詳しい方、アドバイスください。

2019/6/14追記:なんとかテスト接続に成功しました。まだ色々あやふやではあります。
とりあえずこれで動きますが、RepositoryクラスとEntityクラスで組み込みサーバーを使っていた場合、このやり方に変えるのにはほぼ全部書き換える必要がありそうです。
特にControllerは全滅。htmlはそのまま流用できるかも？

##必要なクラス(多分）

|クラス名　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|主な機能説明|
|-------|------|
|JSch|SSHユーザー、SSHパスワード、SSHポート等を引数にしてSessionインスタンスを生成する|
|Session|仮想サーバーに接続する。インスタンスはnewでなくJSchのgetSession(args)メソッドで生成する。この接続が生きている間しかクエリーは発行できない（はず）|
|Properties|Sessionのインスタンスに設定を追加するのに使う（多分）拡張子.propertiesのファイルを読み込むのに一般的に使用するもので、特にサーバーサイドやDBの為だけに使うものではないらしい。|
|JdbcTemplate|このインスタンスがSQLクエリーを発行する。つまりこれのインスタンスをControllerから使えるようになる事がゴール|

##build.gradle
dependenciesに以下を追記（元からある部分はそれでOK）
追記したらgradleをリフレッシュする。

```gradle:build.gradle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	compile 'org.springframework.boot:spring-boot-starter'
    compile 'org.springframework.boot:spring-boot-starter-jdbc'
    compile 'mysql:mysql-connector-java:5.1.35'
    compile 'com.jcraft:jsch:0.1.53'
}
```

##application.properties
**※うまく設定できていない状態でこれを記述するとプロジェクトが起動しなくなるので、直前にGitCommitしておかないと大変なことになる。**

```properties:application.properties
	spring.datasource.url=jdbc:mysql://127.0.0.1:3306/仮想サーバーにあるMySQLに作ったデータベース名
	spring.datasource.username=MySQLのユーザー名
	spring.datasource.password=MySQLのパスワード
	spring.datasource.driver-class-name=com.mysql.jdbc.Driver

```

##SSHConnectionクラスを自作する。
ここに仮想サーバーのSSHパスワードやポート番号を書く。
MySQLのポート番号は3306の固定だと思う。
SSHリモートポート番号も2222で固定のようだが、解説サイトによっては22になっている。あるいは全然違う番号にするのが安全と書いてあったりする。
ドットインストールを参考にVagrantを使用してCentOSを入れた場合、ユーザー名とパスワードはvagrantがデフォルトになっていると思うが、自分で変えた場合はそれになるだろう。
なお、鍵認証する場合はコメントアウトしている部分を導入しなければいけないが、このままコメントアウトしていても動作するようだ。

```java:SSHConnection.java

/**
 * @author tabuchikenta
 * 
 */
public class SSHConnection {
	private final static String S_PATH_FILE_PRIVATE_KEY = "/Users/Macのユーザー名/.ssh/id_rsa"; 
	private final static String S_PATH_FILE_KNOWN_HOSTS = "/Users/Macのユーザー名/.ssh/known_hosts";
	private final static String S_PASS_PHRASE = "vagrant";
	private final static int LOCAl_PORT = 3306; 
	private final static int REMOTE_PORT = 3306; 
	private final static int SSH_REMOTE_PORT = 2222; 
	private final static String SSH_USER = "vagrant";
	private final static String SSH_REMOTE_SERVER = "127.0.0.1";
	private final static String MYSQL_REMOTE_SERVER = "127.0.0.1";

	private Session session; //represents each ssh session
	
	public void closeSSH ()
	{
	    session.disconnect();
	}

	public SSHConnection () throws Throwable
	{
	    JSch jsch = null;

	        jsch = new JSch();
	       // jsch.setKnownHosts(S_PATH_FILE_KNOWN_HOSTS);
	       // jsch.addIdentity(S_PATH_FILE_PRIVATE_KEY, S_PASS_PHRASE.getBytes());
	       
	        session = jsch.getSession(SSH_USER, SSH_REMOTE_SERVER, SSH_REMOTE_PORT);
	        session.setPassword(S_PASS_PHRASE);
	        final Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
	        session.connect();          
	        session.setPortForwardingL(LOCAl_PORT, MYSQL_REMOTE_SERVER, REMOTE_PORT); 

	}
}

```

##Applicationクラス static void main (String[] args) 特に何もしなくてもいいかも
your.pacageは消して自分のパッケージ名を書いてください（普通自動的にインポートしてるはず）。
他サイトを見るとここに全部詰め込んでいたり、色々やってたりするので、そうするのが推奨なのかもしれないけど一応他に切り出しても動いたので、多分こんなところに起動以外の処理は書かない方がいいと思います。コンテナに入れてる方がいてそれはその方がいいらしいですが、とりあえず接続に関係なさそうなところは極力排除したかったので消しました。
普通にIDEが吐き出したままで動きそう。（間違ってたらごめんなさい）

```java:MySQLTestApplication.java
package com.your.package;//これは自分に合わせて変える
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MySqlTest2Application {
    public static void main(String[] args) {
        	SpringApplication.run(MySqlTest2Application.class, args);
        }
    }

```

##DAOクラスの追加
private JdbcTemplate jdbc;のところに直接@AutowiredアノテーションをつけたらNullPointerException例外で動かなかった。
JdbcTemplateのインスタンスはApplicationクラスかControllerクラスでしか@Autowiredが機能しないらしい（やり方が間違ってるだけかもしれない）
そこで、Controllerクラスで生成し、このDAOクラスのコンストラクタで引数に受け取って使用することにした。
まだ試作中ですが、このクラスのインスタンスは一つでいいと思うのでSingletonパターンにして外部からnewさせないようにした方がいい気がします。
また、現状Listにレコードセットをそのまま代入していますが、実際にはEntityクラスを作ってそのインスタンスをListに入れるべきだと思います。

```java:MyDao.java

package com.your.packages;

import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.JdbcTemplate;

/*実際にはシングルトンパターンにした方が良さそう？。*/
public class MyDao {

    private JdbcTemplate jdbc;
    
    public MyDao(JdbcTemplate jdbc) {
    	this.jdbc = jdbc;
    	try {
			SSHConnection sshcon = new SSHConnection();//ここで仮想サーバーのMySQLに接続する。
		} catch (Throwable e) {
			e.printStackTrace();
		}
    }
    /*SQLクエリーを発行してレコードセットを返す*/
    public List<Map<String,Object>> findAll(){
    	List list = this.jdbc.queryForList("SELECT * FROM M_student");//SQLの発行
    	list.forEach(System.out::println);//ログの確認用、動作確認取れたら消していい。
    	return list;
    }
    	
}

```

##Controllerクラスを作る
コメントをつけている行は自分のページに合わせて処理を変える。

```MyController.java

package com.kenta.tabuchi.test;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {

    @Autowired
    private JdbcTemplate jdbc;
    
	@RequestMapping(value= "/",method=RequestMethod.GET)
	public ModelAndView indexGet(ModelAndView mav) {
		mav.setViewName("index");
		MyDao dao = new MyDao(jdbc);
		List<Map<String,Object>> recordset = dao.findAll();//これでレコードセットを取り出して
		mav.addObject("msg",recordset.get(0));//HTMLテンプレートへ流し込む。
		
		return mav;
		
	}
}

```

##index.html を作成
上のaddObjectで指定した"msg"変数に結果を流し込んでいる。
上のコントローラーに合わせて変数名などは変更する。

```html:index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>MySQLテスト</title>
</head>
<body>
	<p>Hello MySQLTest</p>
	<p th:text="${msg}"></p>
</body>
</html>

```