LoginSignup
3
3

More than 5 years have passed since last update.

Play 2.3 Anorm の NamedParameter

Posted at

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 によってはランタイムエラーが起こってしまうようです。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3