Circumflex ORM
参考
※ひどいことに、mavenリポジトリにあるもの(2011/10時点)は、おそらくScala2.8用。2.9で動作させるとNoSuchMethodException等が発生するので注意。
評価
良い点
- タイプセーフに(中途半端ではあるが)こだわっている。普通ならどうやってもなりようがないSQLをかなりタイプセーフなものに仕立て上げている。正解かどうかはおいておいても、この点は他のORMと一線を画している。
- コード量が非常に小さく、とりまわしが楽。
- 様々なRDBの方言に対応可能なようだ、コードを書けば。
悪い点
- circumflexというウェブフレームワークを前提としており、「一つのデータベース」しか扱うことができない(この点はPlay Frameworkのanormとも全く同じ)。複数のデータベースを切り替えたり、同時に使用したりすることは不可能。つまり、そこから切り離して「一般的なORM」として使うのはそのままではできず、改造が必要になる。なぜなら、ウェブフレームワークの起動時に与えられたデータベース接続定義をあらゆるところから「静的に」参照しているから。
とにかく動かしてみる
Circumflex ORMはCircumflexというウェブフレームワークの一部だが、ここでは単体で使用する。 DBはH2を用いた。
テーブルの定義とデータベース生成
Country, Cityはサンプルそのまま
import ru.circumflex._
import ru.circumflex.orm._
import ru.circumflex.core._
class Country extends Record[String, Country] {
val code = "code".VARCHAR(2).NOT_NULL.DEFAULT("'ch'")
val name = "name".TEXT.NOT_NULL
def cities = inverseMany(City.country)
def relation = Country
def PRIMARY_KEY = code
}
object Country extends Country with Table[String, Country]
class City extends Record[Long, City] with SequenceGenerator[Long, City] {
val id = "id".BIGINT.NOT_NULL.AUTO_INCREMENT
val name = "name".TEXT
val country = "country_code".TEXT.NOT_NULL
.REFERENCES(Country)
.ON_DELETE(CASCADE)
.ON_UPDATE(CASCADE)
def relation = City
def PRIMARY_KEY = id
}
object City extends City with Table[Long, City]
object Creation {
def main(args: Array[String]) {
val cx = Circumflex
cx("orm.connection.driver") = "org.h2.Driver"
cx("orm.connection.url") = "jdbc:h2:sample"
cx("orm.connection.username") = "sa"
cx("orm.connection.password") = ""
val unit = new DDLUnit(Country, City)
unit.CREATE()
}
}どんなDDLが生成されているのか?
println(Country.sqlCreate) println(City.sqlCreate)
レコードの挿入
val country = new Country
country.code := "jp"
country.name := "japan"
try {
country.INSERT_!()
COMMIT()
} catch {
case e:Exception => { e.printStackTrace(); }
}
バグ
git上では修正されているが、バイナリリリースはされていない(2011/10/24)
dialect.scalaの207行目付近
def columnDefinition[R <: Record[_, R]](field: Field[_, R]): String = {
var result = field.name + " " + field.sqlType
if (field.notNull_?) result += " NOT NULL"
if (field.unique_?) result += " UNIQUE" // <-- これが抜けてる
result += defaultExpression(field)
return result
}
