必要なパッケージ
build.sbtに書く
build.sbt
// slick
libraryDependencies ++= Seq(
"mysql" % "mysql-connector-java" % "5.1.29",
"com.typesafe.slick" %% "slick" % "2.1.0",
"c3p0" % "c3p0" % "0.9.1.2"
)
データベースへの接続方法
DBConfiguration
クラスを作ってそこにまとめるのがいいかも。
ComboPooledDataSource
でコネクションプーリングしておくことで、コネクションが切れても再接続されるらしい。
class DBConfiguration {
import com.mchange.v2.c3p0.ComboPooledDataSource
import scala.slick.driver.MySQLDriver.simple._
val dbType = "mysql"
val dbHost = "127.0.0.1"
val dbPort = 3306
val dbName = "mydb"
val dbUser = "root"
val dbPassword = "mypass"
val dbDriver = "com.mysql.jdbc.Driver"
val jdbcUrl = "jdbc:%s://%s:%d/%s?characterEncoding=UTF-8".format(dbType, dbHost, dbPort, dbName)
lazy val database = {
val ds = new ComboPooledDataSource
ds.setDriverClass(dbDriver)
ds.setJdbcUrl(jdbcUrl)
ds.setUser(dbUser)
ds.setPassword(dbPassword)
ds.setInitialPoolSize(3)
ds.setMaxPoolSize(30)
ds.setTestConnectionOnCheckin(false)
ds.setTestConnectionOnCheckout(true)
Database.forDataSource(ds)
}
}
ドメインオブジェクト
ドメインオブジェクトはcase classで定義しておくだけ。
case class User(username: String, email: String)
ドメインオブジェクトの永続化方法
ドメインオブジェクトの永続化の実装はひとつのクラスにまとめておくといい。いわゆるDataMapperパターン。
Slickのリスト操作風に書けるDSLを使ってもいいが、個人的な好みで素のSQLが書きたいのでここではPlain SQL Queryを使った。
このサンプルコードにはMySQLUsersTable
にはCRUDのすべての操作がある。
import scala.slick.jdbc.{ GetResult, StaticQuery => Q }
class MySQLUsersTable(config: DBConfiguration) {
val db = config.database // alias
// 結果の行とcase classをマッピングするための宣言。findAllとfindByUsernameでこれを使う
implicit val getUserResult = GetResult(r => User(r.<<, r.<<))
def createTable() = db.withSession { implicit session =>
// NA は non arguments の略
Q.updateNA(
"""
|CREATE TABLE IF NOT EXISTS `users` (
| `username` varchar(255) NOT NULL DEFAULT '',
| `email` varchar(255) NOT NULL DEFAULT '',
| PRIMARY KEY (`username`)
|) ENGINE=InnoDB DEFAULT CHARSET=utf8;
""".stripMargin
).execute
}
def truncate() = db.withSession { implicit session =>
Q.updateNA("TRUNCATE TABLE users").execute
}
def add(user: User) = db.withTransaction { implicit session =>
// +? で文字列結合するとエスケープもしてくれる
(Q.u + "INSERT INTO users (username, email) VALUES (" +? user.username + ", " +? user.email + ")").execute
}
def findAll: List[User] = db.withSession { implicit session =>
(Q[User] + "SELECT * FROM users").list
}
def findByUsername(username: String): Option[User] = db.withSession { implicit session =>
(Q[String, User] + "SELECT * FROM users WHERE username = ? LIMIT 1")(username).list.headOption
}
def update(user: User) = db.withTransaction { implicit session =>
(Q.u + "UPDATE users SET email = " +? user.email + " WHERE username = " +? user.username).execute
}
def delete(username: String) = db.withTransaction { implicit session =>
(Q.u + "DELETE FROM users WHERE username = " +? username).execute
}
}
クライアントコード
クライアントコードは次のようになる
val table = new MySQLUsersTable(new DBConfiguration)
table.createTable()
table.truncate()
table.add(User("alice", "alice@example.com"))
table.add(User("bob", "bob@example.com"))
table.add(User("carol", "carol@example.com"))
println(table.findAll)
println(table.findByUsername("alice")) // -> Some(User(...))
table.update(User("alice", "alice2@example.com"))
println(table.findByUsername("alice"))
table.delete("alice")
println(table.findByUsername("alice")) // -> None