Nablarch:MySQL＆TiDB用のDialectを作成する

## 概要

Nablarchには、MySQL用のDialectがない。
このようなケースにおいてMySQL用のDialectを設定する方法を整理する

## やることのまとめ

### JDBCドライバの登録

https://nablarch.github.io/docs/LATEST/doc/application_framework/application_framework/blank_project/CustomizeDB.html

### ダイアレクトを追加

https://nablarch.github.io/docs/5u24/doc/application_framework/application_framework/libraries/database/database.html#database-add-dialect

## 実装リファレンスの整理

### javadoc

https://nablarch.github.io/docs/6u1/javadoc/nablarch/core/db/dialect/PostgreSQLDialect.html

### dialect側

### postgres(nablarch-core-jdbc)

https://github.com/nablarch/nablarch-core-jdbc/blob/master/src/main/java/nablarch/core/db/dialect/PostgreSQLDialect.java

### mysql(hibarnate)

https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java

### アプリケーション側

https://qiita.com/charon/items/83a0c7901bcf733fc108

## その他：大文字小文字関連の取り扱い

### collation（照合順序）

https://dev.mysql.com/doc/refman/8.4/en/charset-configuration.html

https://qiita.com/subun33/items/9df8ca30e282c691dd54

## 決めなければならないこと

※前提：DefaultDialectと同一実装の場合は、オーバーライド不要

https://github.com/nablarch/nablarch-core-jdbc/blob/v5-main/src/main/java/nablarch/core/db/dialect/DefaultDialect.java

| No | 項目 | 影響を受けるメソッド | Oracleの場合 | MySQLの場合 | 
| --- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | ------------ | ----------- | 
| 1   | シーケンスオブジェクトの次の値を取得するSQL文                      | buildSequenceGeneratorSql(java.lang.String&nbsp;sequenceName)                                                              |   オーバーライド(NEXTVAL)  |    オーバーライド(?)、ただし、TiDBの場合NEXTVALでよい   | 
| 2   | レコード数取得用のSQL文                                            | convertCountSql(java.lang.String&nbsp;sql)                                                                                 |   デフォルト   |    デフォルト         | 
| 3   | SQLIDからレコード数取得用のSQL文                                   | convertCountSql(java.lang.String&nbsp;sqlId, java.lang.Object&nbsp;condition,&nbsp;StatementFactory&nbsp;statementFactory) |    デフォルト    |       デフォルト       | 
| 4   | SQL文をページング用のSQL文に変換するSQL文                          | convertPaginationSql(java.lang.String&nbsp;sql,&nbsp;SelectOption&nbsp;selectOption)                                       |    オーバーライド(ROWNUM)   |    オーバーライド(LIMIT,OFFSET)  | 
| 5   | ping用SQL文                                                        | getPingSql()                                                                                                               | `select 1 from dual`  | `select 1` | 
| 6   | ResultSetから値を取得するための変換クラス                          | getResultSetConvertor()                                                                                                    |  オーバーライド   |  (実地テストで確認)  | 
| 7   | 一意制約違反を表す例外のエラーコード                               | isDuplicateException(java.sql.SQLException&nbsp;sqlException)                                                              | 1            |    1062    | 
| 8   | Query Timeアウト時に発生する例外のエラーコード                     | isTransactionTimeoutError(java.sql.SQLException&nbsp;sqlException)                                                         | 1013         |    3024(TiDBも同様)     | 
| 9   | IDENTITY(オートインクリメントカラム)が使用できるか                 | supportsIdentity()                                                                                                         |    true    |     true     | 
| 10  | batch insert時にIDENTITY(オートインクリメントカラム)が使用できるか | supportsIdentityWithBatchInsert()                                                                                          |     true     |    true    | 
| 11  | SQL文でのオフセット指定が使用できるか                              | supportsOffset()                                                                                                           |     true    |     true   | 
| 12  | シーケンスオブジェクトが使用できるか                               | supportsSequence()                                                                                                         |     true    |      false(TiDBはtrue)       | 
|     | 

## オーバーライドが必要そうな機能のチェックポイント

### buildSequenceGeneratorSql

- デフォルト実装は`UnsupportedOperationException`
- よってシーケンスオブジェクトが使用可能な場合、オーバーライド必須。
- `NEXTVAL`を使用できる場合は、NEXTVALを使用する。
  - `NEXTVAL`を使用できない場合、代用実装とする。

### convertCountSql&convertCountSql

- デフォルト実装は`SELECT COUNT(*) COUNT_ FROM ('引数のSQL') SUB_`
- デフォルト実装で事足りるため、基本的にはオーバーライド不要。全ての既存Dialectはオーバーライドをしていない。
  - 単にCOUNT文が宣言できれば良い

### convertPaginationSql

- デフォルト実装はSQL文を変換せずに返すので、**ページングされない**
- よって可能な限り**オーバーライドすべき**。
- offsetとlimitを使用できる場合、offsetとlimitを使用するSQL文に変換
  - offsetとlimitを使用できない場合、ROWNUMなどで代用する。

### getResultSetConvertor

https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java

- デフォルト実装はSQL文を変換せずに返す。
- 利用する型に関して、利用に支障がないならばオーバーライドは不要。
- 主に、ResultSet#getObject(int)を使用した場合にアプリケーションで処理する際に不都合なデータ型が返却される場合に必要となる。詳細は下記参照。

https://nablarch.github.io/docs/5u25/javadoc/nablarch/core/db/statement/ResultSetConvertor.html

### lower_case_table_names(識別子の大文字と小文字の区別)

https://dev.mysql.com/doc/refman/8.0/ja/identifier-case-sensitivity.html

https://blog.clairvoyance.co.jp/2024/06/10/%e3%80%90mysql8%e3%80%91lower_case_table_names%e3%81%af%e7%b0%a1%e5%8d%98%e3%81%ab%e3%81%af%e5%a4%89%e3%81%88%e3%82%89%e3%82%8c%e3%81%aa%e3%81%84/

```公式
lower_case_table_names=1 を全システムで使用してください。 
これの主な欠点は、SHOW TABLES または SHOW DATABASES を使用したときに、  
元の大文字または小文字で名前が表示されないことです。  

Unix 上では lower_case_table_names=0 を、Windows 上では lower_case_table_names=2 を使用してください。   
これでデータベース名とテーブル名の大文字と小文字の区別が保持されます。   
この欠点は、ユーザーのステートメントが、Windows 上で正しい大文字または小文字でデータベース名およびテーブル名を常に参照していることを確認する必要があることです。   
大文字と小文字が区別される Unix にステートメントを転送する場合、大文字と小文字が正しくなければこのステートメントは機能しません。
```


## エラーリファレンス

### 一意制約違反(ERROR 1062)

https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_dup_entry

### クエリタイムアウト(ERROR 3024)

https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_query_timeout

https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_lock_wait_timeout

## MYSQLには、シーケンスオブジェクトのサポ－トがない

### hibarnate-mysql

https://github.com/hibernate/hibernate-orm/blob/417eb893696fcece73c2482ae42b6b60aaae80e9/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java#L905

### 補足：TiDB,MariaDBの場合はシーケンスオブジェクトを使用できる

https://docs.pingcap.com/ja/tidb/stable/sql-statement-create-sequence#mysql-compatibility

### TiDBのタイムアウトSQLは、以前は差異があったが現在は同じ

https://github.com/pingcap/tidb/pull/43041

### TiDBのベース実装は、基本的にMysqlベースでよい(参考：Hibarnate)

https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java

### TiDBは、NEXTVALが使える

https://docs.pingcap.com/ja/tidb/stable/sql-statement-create-sequence