Play 2.1 から Play 2.3 にアップデートした際の Anorm 関連のメモです。
Play 2.1 Anorm
Play 2.1 の Anorm では、SimpleSql#on の可変長引数に、変数で渡したい時は、値を ParameterValue に変換しておく必要がありました。
// NG: Seq[(Symbol, Any)]
val params1 = Seq(
'email -> "foo@example.net",
'id -> 1
)
SQL("UPDATE users SET email = {email} WHERE id = {id}")
.on(params1: _*).executeUpdate()
// [error] found : Seq[(Symbol, Any)]
// [error] required: Seq[(Any, anorm.ParameterValue[_])]
// OK: Seq[(Symbol, ParameterValue[_ >: String with Int])]
val params2 = Seq(
'email -> toParameterValue("bar@example.net"),
'id -> toParameterValue(2)
)
SQL("UPDATE users SET email = {email} WHERE id = {id}")
.on(params2: _*).executeUpdate()
Play 2.3 Anorm
Play 2.3 の Anorm では SimpleSql#on の引数が NamedParameter に変更されていました。これまでの (Any, ParameterValue[_])
の別名ではなく、以下のように新たにクラス定義されているので、先述の Play 2.1 の方法は使えなくなります。
case class NamedParameter(name: String, value: ParameterValue)
以下のように Seq
の型パラメータに NamedParameter
を指定して、暗黙変換するように修正する必要があります。
val params = Seq[NamedParameter](
'email -> "foo@example.net",
'id -> 1
)
SQL("UPDATE users SET email = {email} WHERE id = {id}")
.on(params: _*).executeUpdate()
// 事前に値を ParameterValue に暗黙変換しておく方法
val values = Seq[ParameterValue]("bar@example.net", 2)
SQL("UPDATE users SET email = {email} WHERE id = {id}")
.on('email -> values(0), 'id -> values(1)).executeUpdate()
注意すべき点として ParameterValue
自体も変更されていて Any
からの暗黙変換を受け付けなくなっています。
val id: Any = 1
val row = SQL("SELECT * FROM users WHERE id = {id}")
.on('id -> id).apply().head
// [error] No implicit view available from Any => anorm.ParameterValue
上記のように明示的に Any
で渡すことはないと思いますが、パラメータを Seq
に入れておいて map
でまとめて変換しようと考えてしまうと、値の型が混在して Any
になった時にコンパイルが通りません。
// NG: Seq[(Symbol, Any)]
val params = Seq('id -> 1, 'name -> "foo") map { t =>
val np: NamedParameter = t; np
}
// NG: Seq[Any]
val values = Seq(123, "abc") map { v =>
val pv: ParameterValue = v; pv
}
anorm.features.anyToStatement
をインポートすることで Any
も暗黙変換されるようになりますが、非推奨です。
anorm.ToStatement.dateToStatement を見ると、パラメータ値が java.util.Date
の場合、PreparedStatement 値として java.sql.Timestamp
へ暗黙変換を行なっています。パラメータ値の型が Any
だと java.util.Date
のまま java.sql.PreparedStatement#setObject
でセットされるので、JDBC によってはランタイムエラーが起こってしまうようです。