サクサク年表君というソフトを趣味でやっている.これは年表をみんなで作れるようにするというプロジェクトで,プロジェクトといいつつ自分しかやっていないのだけれど,一人でチマチマ作っている.今回はScalikeJDBCでDBにつなげるようにしたので,備忘録的な立ち位置の記事.
バージョン
- Java SE
8
- sbt
1.4.5
- Scala
2.13.4
- ScalikeJDBC
3.5.0
- MySQL
8.0.22
前回はこちら
ScalikeJDBCとは
ScalikeJDBCとは,Scala向けの小さなDBアクセスライブラリだ.ORMは含まれておらず,素朴にSQLを書いて実行するとList[A]
がもらえるサックリした世界観.
ちなみに仕事で今いるチームでもORMはあまり使われていなくて,開発者にとって一番馴染みがあるからSQLで良いし,細やかな動作の様子を把握できるし,つねに同じようなSQLが発行されてSlow Queryを探しやすくなるので良い,という理由で直にSQLを書いている.自動クエリ組み立てみたいなものをあまり信用していないという感じ.
ScalikeJDBCでは,こういう雰囲気でSQLを発行できる(公式ページ*1より引用).
// insert initial data Seq("Alice", "Bob", "Chris") foreach { name => sql"insert into members (name, created_at) values (${name}, current_timestamp)".update.apply() } // for now, retrieves all data as Map value val entities: List[Map[String, Any]] = sql"select * from members".map(_.toMap).list.apply()
ScalikeJDBCは,MySQLの他にPostgreSQLやH2などに接続できる.ドライバは各社が作っているので,これをあらかじめsbtで依存性として導入しておけば勝手に良い感じに接続してくれる. 今回はdocker-composeで起動したMySQLサーバに接続する.
ScalikeJDBCのセットアップ
まずはScalikeJDBCがMySQLに接続するための準備をしよう.基本的に公式ページの手順に従いつつ,MySQLに接続する上で不要な箇所を削り,また見落しがちなポイントを追加した.
依存性の導入
ScalikeJDBCは,他のScala/Javaパッケージと同様に,Mavenリポジトリで提供されている.
各ライブラリの最新バージョンは以下のページで確認しよう:
https://mvnrepository.com/artifact/mysql/mysql-connector-java
# build.properties sbt.version=1.4.5
build.sbt
でパッケージの依存性を記述した.
// build.sbt scalaVersion := "2.13.4" /* 略 */ libraryDependencies ++= Seq( /* 略 */ // DB // MySQLコネクタ.これがないとscalikeJDBCはMySQLに接続できない "mysql" % "mysql-connector-java" % "8.0.22", // ScalikeJDBC本体 "org.scalikejdbc" %% "scalikejdbc" % "3.5.0", // テスト用にh2 DBを使えるようにしておく(この記事では使わない) "com.h2database" % "h2" % "1.4.200", // for test purpose // ScalikeJDBCが要求するログフレームワーク "ch.qos.logback" % "logback-classic" % "1.2.3", // テスト時にいろいろ助けてくれるらしいパッケージ "org.scalikejdbc" %% "scalikejdbc-test" % "3.5.0" % "test", // 今回はapplication.confに接続・コネクションプールの設定を記述する. // それを読み取るためのパッケージ "org.scalikejdbc" %% "scalikejdbc-config" % "3.5.0" )
ここまで記述できたら,sbt
を起動して依存性が解決されるか確認すると良いだろう.
application.conf
に接続設定・コネクションプール設定を記述する
ScalikeJDBC自体のセットアップが終わったので,接続やコネクションプールに関する設定を記述していく.今回はsrc/main/resources/application.conf
にこの設定を記述できるようにしてあるので,そこに記述する.
# application.conf # JDBC settings db.default.driver="com.mysql.jdbc.Driver" db.default.url="jdbc:mysql://db/データベース名" db.default.user="ユーザ名" db.default.password="パスワード" # Connection Pool settings db.default.poolInitialSize=5 db.default.poolMaxSize=7 # poolConnectionTimeoutMillis defines the amount of time a query will wait to acquire a connection # before throwing an exception. This used to be called `connectionTimeoutMillis`. db.default.poolConnectionTimeoutMillis=1000 db.default.poolValidationQuery="select 1 as one" db.default.poolFactoryName="commons-dbcp2"
コネクションプールはあまり興味がなかったので公式ページの記述に従った.今はdriver, url, user, passwordがきちんと書けていればよい.
コンフィグラファイルを使わない場合は以下のページを参考に設定しよう:
MySQLの設定
この項はdocker-composeを開発に使い,コンテナとしてMySQLをセットアップするという前提で書かれているので,既にMySQLをセットアップできているなら読み飛ばしましょう.
ここではdocker-composeがMySQLコンテナを起動できるようにし,ScalikeJDBCが接続できるようになるまでのブートストラップができるようにする. おおまかに,「コンテナ自体の設定」「初期化スクリプトの用意」を行う.
コンテナの設定
今回はMySQL 8.0.22
を使う.docker-compose.yml
に以下のように記述する.
version: '3' services: # ...略... db: image: mysql:8.0.22 volumes: - ./tmp/db:/var/lib/mysql - ./db:/db ports: - 3306:3306 environment: - MYSQL_ROOT_PASSWORD=my-secret-pw command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
ここでは以下の設定を行っている.
- MySQL 8.0.22イメージを使うこと
- MySQLがデータを保存するディレクトリをBind mountして永続化すること
- MySQLは
/var/lib/mysql
にデータを保存するので,これをホストの./tmp/db
と同期させる.
- MySQLは
- 初期化用SQLスキーマのあるディレクトリをBind mountして読み込めるようにすること
- スキーマは後述する.スキーマは
./db/
に保存する.
- スキーマは後述する.スキーマは
- ポートを解放すること
- MySQLの
root
ユーザのパスワード- ローカルで開発用に使うので適当に設定
- デフォルトのパスワード認証の方法の指定(標準の
caching_sha2_password
だとたまに対応していないライブラリがあるので・・・) - エンコーディングまわりの設定
- 最もアンパイな設定
ここまで書けたら,前もって./db/
と./tmp/db/
とをmkdir
で作成しておき,docker-compose up db
を実行してDBが立ち上がるか確認しよう.
初期化スクリプトの用意
さて,開発の準備は整ったのですが,スクリプト一発でスキーマが反映されていると嬉しいですね.手でスキーマを導入していくのは苦行です.
そこでDB初期化用のスクリプトをscript/setup_db.sh
に用意します.
#!/bin/bash set -ex MYSQL_HOST=db MYSQL_USER=root # cf. docker-compose.yml MYSQL_PASS=my-secret-pw # cf. docker-compose.yml MYSQL_SCHEMA_PATH=/db/schema.sql docker-compose run --rm db bash -c "mysql -h $MYSQL_HOST -u $MYSQL_USER --password=$MYSQL_PASS < $MYSQL_SCHEMA_PATH"
やっていることはあまり大したことはありません.MySQLへの接続を確立し,スキーマファイルを流し込むだけです.
スクリプトができたら,スクリプトが読み込むためのSQLスキーマファイルをdb/schema.sql
に用意します.
-- Initial Sequence DROP DATABASE IF EXISTS データベース名; CREATE DATABASE データベース名 CHARACTER SET utf8mb4 COLLATE = utf8mb4_general_ci; -- In mysql 8 or higher, creating user and granting should be separated. -- Drop user DROP USER IF EXISTS 'readwrite'@'%'; DROP USER IF EXISTS 'readwrite'@'localhost'; -- Prepare user CREATE USER 'readwrite'@'%' IDENTIFIED BY 'readwrite'; CREATE USER 'readwrite'@'localhost' IDENTIFIED BY 'readwrite'; GRANT SELECT, UPDATE, INSERT, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON データベース名.* TO readwrite@'%'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON データベース名.* TO readwrite@'localhost'; FLUSH PRIVILEGES; -- Table Definition SET sql_mode='TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY'; -- ここにテーブル定義を書いていく
このスキーマでは以下のことをやっています.
- DBが存在していれば破壊し,新たにDBを作成する
- ユーザ
readwrite
がいれば破壊し,新たにユーザを作成する- 開発用なので,
readwrite
というユーザをreadwrite
というパスワードで作成しています.
- 開発用なので,
- ユーザ
readwrite
に権限を付与する- 通常のアプリケーションの動作で必要な権限は網羅しているはずです.
- 各種設定を行う
- いわゆるkamipo traditionalです
- session開始時にも同様の設定をやったほうがよいが,ScalikeJDBCでどうやったらセッション開始時にSQLを発行できるようになるか分からなかったのでスキップ
DBセットアップを実行する
DBコンテナとセットアップスクリプトの設定ができた.さっそく実行してみよう.
まずDBコンテナを起動しておく.
$ docker-compose up db
そのターミナルは開きっぱなしにして,別ターミナルからセットアップスクリプトを実行する.
$ ./script/setup_db.sh
するとDBコンテナのDBがいったん削除され,スキーマファイルの通りにDBがセットアップされます.テーブルが増えるたびにこれを実行すればよいです.
ScalikeJDBCから接続してみる
せっかくなのでScalikeJDBCから接続できることを確認してみましょう.テーブルはまだ作成していないので,テーブルがなくても動くクエリを使います.
import scalikejdbc._ // Setup connection-pool regards to application.conf. // cf. application.conf. scalikejdbc.config.DBs.setupAll() // test val value = DB readOnly { implicit session => sql"select 1 as one".map(_.long(1)).list.apply() } println(s"DB Connection has been established: $value")
このコードを実行すると,うまくMySQLに接続できたならば,DB Connection has been established: 1
と表示されるはずです.おめでとう!
自分はCake Patternを採用したScalatraプロジェクトでDBに接続したかったので,このコードをDataBase
traitとして,Cake Patternのコンポーネントとしてmix-inしました.
class App extends Database { /* ... */ }
おわり
今回は次のことについて説明しました:
- ScalikeJDBCとは
- ScalikeJDBCの導入
- 手元のローカル開発環境用のMySQL環境のセットアップ
- 初期化スクリプトの用意
わからなかったこともありました:
- セッション開始時に自動的にkamipo traditionalを設定する方法