Azure Database for MySQL(5.7)にJDBC経由で接続し文字コードutf8mb4を使う

## 概要
サロゲートペア文字を入力すべく、
AzureのMySQLで使用可能な文字コードをutf8からutf8mb4に変更をしました。

下記3つを設定する必要がありました。
[MySQL側の設定](#mysql側の設定)
[JDBC側の設定](#jdbc側の設定)
[Connector/J のバージョン](#connectorj-のバージョン)

## MySQL側の設定

### サーバパラメータの設定
```
// サーバの文字コードを設定
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci

// 接続レベルの文字コードを設定
init_connect=
SET character_set_client=utf8mb4;SET character_set_connection=utf8mb4;SET character_set_results=utf8mb4;SET character_set_database=utf8mb4;
```
https://docs.microsoft.com/ja-jp/azure/mysql/howto-server-parameters#setting-parameters-not-listed

設定後、mysqlサーバの再起動

### テーブルの設定変更
ALTER TABLE {tableName} CONVERT TO CHARACTER SET utf8mb4;

## JDBC側の設定
### 接続文字列
接続文字列には、characterEncoding=utf8を設定する必要があります。

Azureのサポートの方に下記の回答をいただきました。

>ゲートウェイは共用のコンポーネントのため、お客様毎に個別の文字コードを設定することができません。
ゲートウェイと Connector/J 間でハンドシェイクを行う際に、
サーバーサイド（実際はゲートウェイ）で utf8 が設定されていると Connector/J が認識してしまい、Connector/J が SET NAME utf8 を実行してしまっておりました。
よって、サーバーサイドだけでなく、クライアントサイド（Connector/J サイド）でもutf8mb4 を使用することを明示的に指定する必要があり、接続文字列で characterEncoding=utf8 を指定する必要がございます。

![image1](https://docs.microsoft.com/ja-jp/azure/mysql/media/concepts-connectivity-architecture/connectivity-architecture-overview-proxy.png)

回答いただいた通り、characterEncoding=utf8を指定しないとAzureのMySQLでは正常にutf8mb4にならない模様です。


## Connector/J のバージョン
Connector/J 5.1.**47** 以降のバージョンである必要があります。
Connector/J 5.1.**47** 以前のバージョンを指定すると、characterEncoding=utf8を指定したとしても、characterEncoding=utf8mb3が利用されてしまう問題があるようです。
https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-charsets.html

## JDBC経由でAzure MySQLに接続
MySQL側の設定、JDBC側の設定、Connector/J のバージョンを変更したら、
JDBC経由で下記を実行して、正しく文字コードが設定されて接続できているのかを確認します

```sql
SELECT * FROM performance_schema.session_variables WHERE variable_name LIKE 'character_set%';
```

### テストコード
テストコードは[こちらの記事](https://qiita.com/norikiyo777/items/0bc3bf28b94ae4922b9a)を引用にさせていただいております。(ありがとうございます！)


```java:Test.java
package jp.co.tdc.asp.core.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Test {
	public static void main( String args[] ) throws Exception {

    /*接続するデータベース名*/
    String databasename   = "XXXXXXXXXXXXXXXXXXXX";

    /*データベースの接続に用いるユーザ名*/
    String user = "XXXXXXXXXXXXXXXXXXXX";

    /*データベースの接続に用いるユーザのパスワードを指定している*/
    String password = "XXXXXXXXXXXXXXXXXXXX";

    /*データベースをあらわすURLを設定している*/
    String url =  "jdbc:mysql://XXXXXXXXXXXXXXXXXXXX.mysql.database.azure.com/" + databasename + "?characterEncoding=utf8";


    /*接続を表すConnectionオブジェクトを初期化*/
    Connection con = null;

    try{

        /*クラスローダによりJDBCドライバを読み込んでいることを示している。
        引数は、データベースにアクセスするためのJDBCドライバのクラス名である。*/
        Class.forName( "com.mysql.jdbc.Driver" );

        /*DriverManagerクラスのgetConnectionメソッドを使ってデータベースに接続する。*/
        con = DriverManager.getConnection( url, user, password );

        System.out.println( "Connected...." );

        /*データベースの接続後に、sql文をデータベースに直接渡すのではなく、
        sqlコンテナの役割を果たすオブジェクトに渡すためのStatementオブジェクトを作成する。*/
        Statement st = con.createStatement();

        /*SQL文を作成する*/
        String sqlStr = "SELECT * FROM performance_schema.session_variables WHERE variable_name LIKE 'character_set%';";

        /*SQL文を実行した結果セットをResultSetオブジェクトに格納している*/
        ResultSet result = st.executeQuery( sqlStr );

        /*クエリ結果を1レコードずつ出力していく*/
        while( result.next() )
        {
            /*getString()メソッドは、引数に指定されたフィールド名(列)の値をStringとして取得する*/
            String str1 = result.getString( "VARIABLE_NAME" );
            String str2 = result.getString( "VARIABLE_VALUE" );


            System.out.println( str1 + "=" + str2 );

        }

        /*ResultSetオブジェクトを閉じる*/
        result.close();

        /*Statementオブジェクトを閉じる*/
        st.close();

        /*Connectionオブジェクトを閉じる*/
        con.close();
    }
    catch( SQLException e ){

        /*エラーメッセージ出力*/
        System.out.println( "Connection Failed. : " + e.toString() );

        /*例外を投げちゃうぞ*/
        throw new Exception();

    }catch (ClassNotFoundException e){

        /*エラーメッセージ出力*/
        System.out.println("ドライバを読み込めませんでした " + e);
    }
    finally{
        try{
            if( con != null ){
                con.close();
            }
        }
        catch(Exception e){

            /*エラーメッセージ出力*/
            System.out.println( "Exception2! :" + e.toString() );

            /*例外を投げちゃうぞ*/
            throw new Exception();
        }
    }
}
}
```
テストコードの結果が下記のようになれば成功です。

```:result
Connected....
character_set_client=utf8mb4
character_set_connection=utf8mb4
character_set_database=utf8mb4
character_set_filesystem=binary
character_set_results=
character_set_server=utf8mb4
character_set_system=utf8
character_sets_dir=c:\mysql\share\charsets\
```

サロゲートペア文字の入力が可能になっているはずです。

## おわり
接続の際にうまくいかず、サポートの方に対応してもらったので、備忘のためにメモ