概要
Micronaut フレームワークを利用して、MySQL サーバへの接続設定を行う。データベースへの接続は、コネクションプールを利用する。
コネクションプールには、HikariCP ライブラリを利用する。
コネクションプール設定は、複数定義する。
設定は、環境変数やシステムプロパティなどで、上書きできるようにする。
環境
Java 1.8.0_242Micronaut 1.3.3
Micronaut CLI 1.3.3
MySQL Connector/J 8.0.19
MySQL Server 5.7.29
Micronaut とは、フルスタックのマイクロサービスフレームワークのこと。
MySQL Server のタイムゾーンは、UTC で設定している。
前提
MySQL サーバ、Java および、Micronaut CLI は、インストール済みとする。MySQL サーバは、ローカルで構築し、ユーザは root 、パスワード無しとする。
手順
プロジェクト作成
Micronaut CLI でプロジェクトを作成する。Gradle プロジェクトとなる。
言語指定が、Kotlin となっているが、変更しても設定に影響はない。
mn create-app --lang=kotlin micronaut-db-sample
ライブラリ追加
「build.gradle」の「dependencies」に以下の設定を追加HikariCP および、MySQL Connector/J が利用可能になる。
HikariCP などのライブラリは、依存関係で一緒に導入される。
runtimeOnly platform("io.micronaut:micronaut-bom:$micronautVersion")
runtimeOnly "io.micronaut.configuration:micronaut-jdbc-hikari"
runtimeOnly "mysql:mysql-connector-java:8.0.19"
接続設定の追加
Micronaut の設定ファイルにコネクションプールの設定を追加する。追加した設定は、ソース中でインジェクションを行い利用する。
「src/main/resources/application.yml」に以下の設定を追加する。
設定中にある「serverTimezone=UTC」は、必須。当方の MySQL サーバは、UTC で設定してある。SYSTEM 設定(デフォルト)の場合は、JST などを指定する。
datasources:
# デフォルトのコネクションプール設定
default:
# ${...} は、プレースホルダーであり、環境変数または、システムプロパティで設定可能
# プレースホルダー中のコロンより後ろは、デフォルト値
# デフォルト値は、バッククォート「`」で囲んだ値を指定する。
# デフォルト値を指定せず(password のようにコロンの後を空にする)、
# 環境変数などで設定しない場合 null 扱いとなる。エラーとはならない。
# デフォルト値を指定しない(コロンを付けない)場合は、
# 環境変数などで設定をしないと起動時にエラーとなる。
# 環境変数で設定する場合「DB_DEFAULT_URL」で指定すると url が指定できる。
# 環境変数の「_」は、プレースホルダーキーの「.-」と見なされる。
# システムプロパティの場合は、キャメルケースも「-」で分割して一致する。
# 設定とキーの一致法則については、以下の「Table 1. Property Value Normalization」を参照。
# https://docs.micronaut.io/latest/guide/index.html#propertySource
url: ${db.default.url:`jdbc:mysql://localhost:3306?serverTimezone=UTC`}
username: ${db.default.username:`root`}
# 【注意】デフォルト値に「``」と設定すると、パスワードありで空のパスワードを指定することになる。
# パスワード無しの設定にするには、デフォルト値には何も設定しないこと
password: ${db.default.password:}
# デフォルトでは、古いクラス指定になってしまうため、指定が必須となる
driverClassName: com.mysql.cj.jdbc.Driver
# デフォルトは、自動コミットになる
autoCommit: false
# データベース指定
catalog: ${db.database:`lab`}
# コネクション時のタイムアウト(ms)
connectionTimeout: 1000
# 未使用(アイドル状態)コネクションの切断時間(ms)
idleTimeout: ${db.idle.timeout:`10000`}
# 最大コネクション保持数
maximumPoolSize: ${db.maximum.pool.size:`10`}
# この設定時間を超えて、コネクションを継続利用すると切断される(ms)
# バッチ処理など長時間稼働する処理の場合は、設定値を増やす必要がある。
maxLifetime: ${db.max.lifetime:`1800000`}
# 最小コネクション保持数
minimumIdle: ${db.minimum.idle:`0`}
# ログなどに出力されるプール名
# デフォルトでは、「HikariPool-1」のような名称となる
poolName: Pool-RW
# 読み込み専用設定
readOnly: false
# トランザクション分離レベル
# MySQL のデフォルトは、「TRANSACTION_REPEATABLE_READ」
# デフォルト値の場合、同一処理内では、default 側で Commit した内容を readonly 側では参照できない。
transactionIsolation: TRANSACTION_READ_COMMITTED
# MySQL Connector/J のプロパティを設定
# HikariDataSource の dataSourceProperties に格納され、最終的に MySQL Connector/J に伝播する。
# この設定は、構造化せずに以下のようにピリオドでキーを結合して値を定義することもできる。
# data-source-properties.cachePropStmts: true
# 設定の内容は、HikariCP が推奨している設定項目となる。
# 詳細は、以下を参照。
# https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
# MySQL Connector/J に設定できるプロパティは、以下を参照
# https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
data-source-properties:
cachePropStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
# ログに SQL のプロファイル情報を出力
profileSQL: true
# 任意の名称で設定可能。
# @Named アノテーションでこの名称を指定する。
readonly:
url: ${db.default.url:`jdbc:mysql://localhost:3306?serverTimezone=UTC`}
username: ${db.default.username:`root`}
password: ${db.default.password:}
driverClassName: com.mysql.cj.jdbc.Driver
autoCommit: false
catalog: ${db.database:`lab`}
poolName: Pool-R
readOnly: true
transactionIsolation: TRANSACTION_READ_COMMITTED
DB 接続例
データベースの準備
スキーマとテーブルを準備CREATE DATABASE IF NOT EXISTS `lab` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `lab`;
SET NAMES utf8 ;
DROP TABLE IF EXISTS `books`;
SET character_set_client = utf8mb4;
CREATE TABLE `books` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '名称',
`price` int(11) NOT NULL COMMENT '価格',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本';
データベースアクセスの実装
データベースにアクセスする場合、以下のように実装する。「src/main/kotlin/micronaut/db/sample」ディレクトリに以下のクラスを作成
「DbAccessController.kt」
package micronaut.db.sample
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import java.sql.Connection
import javax.inject.Inject
import javax.inject.Named
import javax.sql.DataSource
@Controller("/db")
class DbAccessController(
@Inject private val dataSource: DataSource,
@Inject @Named("readonly") private val dataSourceReadonly: DataSource
) {
@Get(produces = ["${MediaType.TEXT_PLAIN};${MediaType.CHARSET_PARAMETER}=utf-8"])
fun index(): String {
val response = StringBuilder()
// 読み書き用 Connection を生成
// use は、リソースを自動クローズする構文
dataSource.connection.use { con ->
// 読み込み専用 Connection を生成
dataSourceReadonly.connection.use { conR ->
// 読み書き用 Statement を生成
con.createStatement().use { stmt ->
// Books テーブルに Insert
stmt.execute("INSERT INTO books (id, name, price) VALUES (0, 'foo', 1000)")
// Books テーブルを全件取得
// コミット前なので 0 件
response.append("最初の SELECT: ")
response.append(select(conR))
// コミット
con.commit()
// Books テーブルを全件取得
// コミット後なので 1 件
response.append("Commit 後の SELECT: ")
response.append(select(conR))
// レコードを削除
stmt.execute("DELETE FROM books")
con.commit()
}
}
}
return response.toString()
}
}
fun select(con: Connection): String = con.createStatement().use { stmt ->
stmt.executeQuery("SELECT * FROM books").use { rset ->
val response = StringBuilder()
while (rset.next()) {
response.appendln("id:${rset.getInt("id")}" +
", name:${rset.getString("name")}" +
", price:${rset.getInt("price")}"
)
}
rset.last()
return "${rset.row} 件\n" + response.toString()
}
}
動作確認
以下のコマンドで起動する。
gradlew run
起動後、以下の URL にブラウザでアクセスする。
http://localhost:8080/db
ブラウザ上に以下のような表示がされる。
id は、自動採番のため、実行する度に変わる。
最初の SELECT: 0 件
Commit 後の SELECT: 1 件
id:0, name:foo, price:1000