scalikejdbcでDBMS独自構文を書く
例えばMySQLのon duplicate key update
など
まず、sql"..."
で書けば何でもいけるのはいいとして
def upsertUser1(id: String, email: String)(implicit session: DBSession) = sql""" insert into `user` (`id`, `email`) values ($id, $email) on duplicate key update `email` = values(`email`) """.update().apply()
QueryDSLでinsert文を書いてsqls"..."
をappend
するもよし
def upsertUser2(id: String, email: String)(implicit session: DBSession) = applyUpdate { val c = User.column insert.into(User).namedValues( c.id -> id, c.email -> email ).append( sqls"on duplicate key update ${c.email} = values(${c.email})" ) }
しかしtypoするとメンドクサイし、自分はIDEの補完に頼って生きていきたい。
そういうことでInsertSQLBuilderと、ついでにSQLSyntaxオブジェクトに対してimplicit conversionを定義します。
object MySQLSyntaxSupport { implicit class RichInsertSQLBuilder(val self: InsertSQLBuilder) extends AnyVal { def onDuplicateKeyUpdate(columnsAndValues: (SQLSyntax, Any)*): InsertSQLBuilder = { val cvs = columnsAndValues map { case (c, v) => sqls"$c = $v" } self.append(sqls"on duplicate key update ${sqls.csv(cvs:_*)}") } } implicit class RichSQLSyntax(val self: sqls.type) extends AnyVal { def values(column: SQLSyntax): SQLSyntax = sqls"values($column)" } }
これでこのように書けます。
import MySQLSyntaxSupport._ def upsertUser3(id: String, email: String)(implicit session: DBSession) = applyUpdate { val c = User.column insert.into(User).namedValues( c.id -> id, c.email -> email ).onDuplicateKeyUpdate( c.email -> sqls.values(c.email) ) }
まとめ
implicit conversionによってinsertにon duplicate key updateのサポートを追加しました。
同様にselect/update/deleteやwhereなどの後にも文脈に沿ったメソッドを追加することができます。
version
scalaVersion := "2.10.3" libraryDependencies ++= Seq( "org.scalikejdbc" %% "scalikejdbc" % "1.7.4", "org.scalikejdbc" %% "scalikejdbc-interpolation" % "1.7.4", "mysql" % "mysql-connector-java" % "5.1.28" )