Slick 2.0.0 documentation - 05 Schema code generation
Permalink to Schema Code Generation — Slick 2.0.0 documentation
Slickコードジェネレータは既存のデータベーススキーマをそのまま動かす上で便利なツールとなっている。スタンドアローン形式で動かしたり、sbtのbuildに対し統合したり出来る。
デフォルトでコードジェネレータは、TableQueryの値に対応するTableクラスを生成する。これらの値は、個々は行の値を包括するケースクラスとなり、全体としてコレクション操作関数が呼び出せるようなものになっている。もしScalaのタプルの限界数である22個より多いカラムが存在していたのなら、自動的にSlickの実験的な実装であるHListを用いた実装を出力する。(ちなみに、25カラムより多い場合には非常にコンパイルに時間がかかる事が分かっており、可能な限り早く修正する予定だ)
実装は実用的なものになってはいるが、コードジェネレータはSlick 2.0における新しい機能となっており、依然として実験的なものも含んでいる。必要なものを摘出し、必要のない機能を取り除いていく予定だ。将来的なバージョンにおけるコードジェネレータに対する修正は小さくする予定だ。もし必要ならば、Slickの他の部分から独立した実装にしても良い。我々はこの機能を用いた人々の挑戦に対する声に非常に関心がある。
ジェネレータについて、talk at Scala eXchange2013で軽く説明も行っている。
Slickのコードジェネレータは以下のようにして手軽に動かすことが出来る。
scala.slick.model.codegen.SourceCodeGenerator.main(
Array(slickDriver, jdbcDriver, url, outputFolder, pkg)
)
必要な引数は以下の通りである
コードジェネレータは指定されたパッケージ名に一致するサブフォルダを、指定された出力先フォルダの中に作成し、そこの“Tables.scala“というファイルへ結果を出力する。そのファイルには“Tables“オブジェクトが生成される。引数に与えたSlickドライバーと同じものが用いられているかを確認して欲しい。このファイルには同様に“Tables“トレイトがふくまれ、これはCakeパターンに用いられたものになっている。
コードジェネレータをコンパイル毎に事前に実行することも出来るし、手動で実行することも出来る。実際に使ってみた例がこちらにあるので見て欲しい。
コードジェネレータはモデルデータに基づきコードを自動生成する関数をオーバーライドする事で、柔軟にカスタマイズ出来る。小さなカスタマイズであっても大きなカスタマイズであっても、このようなモデルドリブンなコードジェネレーションが同じように扱われる。例えば、とあるフレームワークにおけるバインディングや、その他のデータに関連するアプリケーションの繰り返しセクションにおいて用いられる。
この例ではカスタマイズされたコードジェネレータを用いており、メインリソースをコンパイルする前にコードジェネレータを走らせるマルチプロジェクトのsbtビルドに対しどのように設定を行うのかを示している。
コードジェネレータの実装は構造化されており、いくつかの階層化されたサブジェネレータに責務を委譲している。つまり完全なる出力を出す際に、部分化した結果を各ジェネレータにおいて出力している。各サブジェネレータの実装は、対応するファクトリメソッドをオーバーライドすることで、カスタマイズしたものへ変更する事が出来る。SourceCodeGenerator
はファクトリメソッドであるTable
を持っており、これは各テーブルのためのサブジェネレータを生成するために用いられるものである。サブジェネレータであるTable
は、Table
クラス、エンティティケースクラス、カラム、キー、インデックス、といった情報のための、別個複数のサブジェネレータを持っている。
Slickに部分的に関連するサブジェネレータにおいて、データモデルはコード生成のために用いられる。
カスタマイズする際にオーバーライドする関数については、APIドキュメントを是非見てもらいたい。
コードジェネレータをカスタマイズする例として、以下のようなものがある。
import scala.slick.jdbc.meta.createModel
import scala.slick.model.codegen.SourceCodeGenerator
// fetch data model
val model = db.withSession{ implicit session =>
createModel(H2Driver.getTables.list,H2Driver) // you can filter specific tables here
}
// customize code generator
val codegen = new SourceCodeGenerator(model){
// override mapped table and class name
override def entityName =
dbTableName => dbTableName.dropRight(1).toLowerCase.toCamelCase
override def tableName =
dbTableName => dbTableName.toLowerCase.toCamelCase
// add some custom import
override def code = "import foo.{MyCustomType,MyCustomTypeMapper}" + "\n" + super.code
// override table generator
override def Table = new Table(_){
// disable entity class generation and mapping
override def EntityType = new EntityType{
override def classEnabled = false
}
// override contained column generator
override def Column = new Column(_){
// use the data model member of this column to change the Scala type, e.g. to a custom enum or anything else
override def rawType =
if(model.name == "SOME_SPECIAL_COLUMN_NAME") "MyCustomType" else super.rawType
}
}
}
codegen.writeToFile(
"scala.slick.driver.H2Driver","some/folder/","some.packag","Tables","Tables.scala"
)