- この記事は 2016年04月14日 にQiitaへ投稿した内容を転記したものです。
- 本記事は執筆から1年以上が経過しています。
前書き
職場の新人氏が質問してて「調べてもおまじないとしか書かれてなくて…」とのことだったので書きました。
で、なんなん?
DB.withConnection()
は引数にConnection => A
型の関数を取るメソッドなので、(c: Connection) => A
の無名関数を渡しています。
ただし、引数はimplicit引数にしています。
// 省略していない式 DB.withConnection( (implicit c: Connection) => Some("value") ) // メソッド定義でcの引数の型(Connection)はわかっているので省略可能 DB.withConnection( (implicit c) => Some("value") ) // 無名関数の引数が1つなので、丸括弧は省略可能 DB.withConnection( implicit c => Some("value") ) // 引数の関数を囲ってる () は {} にできる DB.withConnection { implicit c => Some("value") }
なんでimplicit引数にしてるの?
ここでSQL操作を行うと思うのですが、例えばAnormのSqlResult.as()
等のimplicit引数へConnectionを暗黙的に渡すためです。
DB.withConnection { implicit c => SQL(""" |SELECT * |FROM User |WHERE logged_in BETWEEN {from} AND {to}""".stripMargin ).on( 'from -> from, 'to -> to ).as(userParser *) }
/** * Converts this query result as `T`, using parser. */ def as[T](parser: ResultSetParser[T])(implicit connection: Connection): T = Sql.asTry(parser, resultSet(connection), resultSetOnFirstRow).get
なので、一応、下記のように書くこともできます。
DB.withConnection { c => implicit val conn = c SQL(""" |SELECT * |FROM User |WHERE logged_in BETWEEN {from} AND {to}""".stripMargin ).on( 'from -> from, 'to -> to ).as(userParser *) }
または
DB.withConnection { c => SQL(""" |SELECT * |FROM User |WHERE logged_in BETWEEN {from} AND {to}""".stripMargin ).on( 'from -> from, 'to -> to ).as(userParser *)(c) }
このようなimplicit引数の動作は、REPL等でも確認することができます。
def foo(implicit f: Int): Int = f * 10 def bar(implicit b: Int): Int = { // implicit引数の b が暗黙的に使用される foo } def baz(b: Int): Int = { implicit val i = b // i が暗黙的に使用される foo } println(foo(5)) // 50 println(bar(6)) // 60 println(baz(7)) // 70
実際に関数へConnectionを渡している所は?
例えば、DefaultDatabase.withConnection()とか。
def withConnection[A](block: Connection => A): A = { withConnection(autocommit = true)(block) } def withConnection[A](autocommit: Boolean)(block: Connection => A): A = { val connection = getConnection(autocommit) try { block(connection) } finally { connection.close() } }