ScalikeJDBC Tutorial for Beginners

4,926 views

Published on

ScalikeJDBC Tutorial for Beginners

http://git.io/scalikejdbc

Published in: Technology, Education

ScalikeJDBC Tutorial for Beginners

  1. 1. ScalikeJDBCTutorialfor Beginners@seratchKazuhiro Sera
  2. 2. Table of contentsWhat’s this?ConnectionPoolImplicit SessionSQL SyntaxQuery DSLMore FeaturesGet Started Now
  3. 3. What’s this?
  4. 4. ScalikeJDBChttp://git.io/scalikejdbcA tidy SQL-based DB access libraryfor Scala developers....SQL-based?
  5. 5. SQL-basedimport scalikejdbc._, SQLInterpolation._val id = 123// Anorm-like API for Scala 2.9val name = SQL(“select name from company where id = {id}”).bindByName(‘id -> id).map(rs => rs.string(“name”)).single.apply()// SQL Interpolation since 2.10val name = sql”select name from company where id = ${id}”.map(rs => rs.string(“name”)).single.apply()// Query DSLval name = withSQL {select(c.name).from(Company as c).where.eq(c.id, id)}.map(rs => rs.string(c.resultName.name)).single.apply()
  6. 6. Everybody knows SQLWe don’t need something new anddifferent from SQL.Maybe, you can write the code on theprevious slide immediately, right?Because you already know SQL.
  7. 7. ConnectionPool
  8. 8. Easy-to-use CPDB block such as DB.readOnly { ... }borrows a connection fromConnectionPool.Since ConnectionPool is a singletonobject, you can easily borrowanywhere.
  9. 9. ConnectionPoolimport scalikejdbc._Class.forName(“org.h2.Driver”)// default connection poolval (url, user, password) = (“jdbc:h2:mem:db”, “sa”, “”)ConnectionPool.singleton(url, user, password)val conn: java.sql.Connection = ConnectionPool.borrow()val names: List[String] = DB readOnly { implicit session =>sql”select name from company”.map(_.string(“name”).list.apply()}// named connection poolval poolSettings = new ConnectionPoolSettings(maxSize = 50)ConnectionPool.add(‘secondary, url, user, passsword, poolSettings)NamedDB(‘secondary) readOnly { implicit session =>
  10. 10. DBCP by defaultBy default, only Commons DBCPimplementation is provided.It’s also possible to implement yourpreferred connection pool.(e.g. C3P0, BoneCP)
  11. 11. Implicit Session
  12. 12. Go implicitlyDBSession representsjava.sql.Connectionwith a transaction(if exists).#apply() requires DBSessionimplicitly.
  13. 13. Implicit DBSessionimport scalikejdbc._, SQLInterpolation._val id = 123// DBSession usageval name: Option[String] = DB readOnly { session: DBSession =>session.single(“select name from company where id = ?”, id) { rs =>rs.string(“name”)}}// SQL API requires implicit DBSessionval name: Option[String] = DB readOnly { implicit session =>sql”select name from company where id = ${id}”.map(_.string(“name”)).single.apply()}
  14. 14. AutomaticAutoSession creates & closes an ad-hoc session if absent.query: read-only sessionupdate: auto-commit session
  15. 15. Flexible Transactionimport scalikejdbc._, SQLInterpolation._implicit val session = AutoSessionsql”select name from company”.map(_.string(“name”)).list.apply()def addCompany(name: String)(implicit s: DBSession = AutoSession) {sql”insert into company values (${name})”.update.apply()}def getAllNames()(implicit s: DBSession = AutoSession): List[String] = {sql”select name from company”.map(_.string(“name”)).list.apply()}val names: List[String] = getAllNames() // read-only sessionDB localTx { implicit session =>addCompany(“Typesafe”) // within a transactiongetAllNames() // within a transaction, includes “Typesafe”}
  16. 16. SQL Syntax
  17. 17. SQLSyntax?// SQLSyntax = a part of SQL object, will be embedded as-isval c: SQLSyntax = sqls”count(*)”val bobby: String = “Bob%”val query = sql”select ${c} from members where name like ${bobby}”// -> “select count(*) from members where name like ?”A part of SQL object which will beembedded as-is.
  18. 18. SQLSyntaxSupportSQLSyntaxSupport provides DRY andtype safe SQL.- CoC but configurable- Write complex join queries- Using type-dynamic(similar toRuby’s method-missing) but havecompile-time check with macro
  19. 19. CoC Rules// Scala objectcase class GroupMember(id: Long, fullName: Option[String] = None)obejct GroupMember extends SQLSyntaxSupport[GroupMember]// DDLcreate table group_member {id bigint not null primary key,full_name varchar(255)}Simply snake_case’d name
  20. 20. Customize it// Scala objectcase class GroupMember(id: Long, fullName: Option[String] = None)obejct GroupMember extends SQLSyntaxSupport[GroupMember] {override val tableName = “group_members”override val nameConverters = Map(“fullName” -> “fullname”)}// DDLcreate table group_members {id bigint not null primary key,fullname varchar(255)}
  21. 21. Query & Extractingimport scalikejdbc._, SQLInterpolation._// entity class (Plain Old Scala Object)case class Member(id: Long, fullName: Option[String] = None)// companion object for entityobejct Member extends SQLSyntaxSupport[Member] {def apply(m: ResultName[Member])(rs: WrappedResultSet) = new Member(id = rs.long(m.id), name = rs.stringOpt(m.fullName)))val m = Member.syntax(“m”)val id = 123val memebr: Option[Member] = DB readOnly { implicit s =>sql”select ${m.result.*} from ${Member as m} where ${m.id} = ${id}”.map(Member(m.resultName)).single.apply()}
  22. 22. result? resultName?val m = Member.syntax(“m”)m.fullName == “m.full_name”m.result.fullName == “m.full_name as fn_on_m”m.resultName.fullName == “fn_on_m”Scala:sql”select ${m.result.fullName} from ${Member as m} where ${m.id} = 123”SQL:“select m.full_name as fn_on_m from member m where m.id = 123”ResultSet extractor:val name = sql”...”.map(rs => rs.string(m.resultName.fullName)).single.apply()// extracting “fn_on_m” from ResultSet
  23. 23. Type safe dynamicimport scalikejdbc._, SQLInterpolation._val m = Member.syntax(“m”)val id = 123val memebr: Option[Member] = DB readOnly { implicit s =>sql”select ${m.result.fullNamee} from ${Member as m} where ${m.id} = ${id}”.map(Member(m.resultName)).single.apply()}<console>:28: error: Member#fullNamee not found. Expected fields are #id,#fullName, #createdAt, #deletedAt.m.fullNamee^
  24. 24. Query DSL
  25. 25. Uniqueness- DBSession inspired by Querulous- SQL(String) inspired by Anorm- sql”...” inspired by SlickQuery DSL is surelyScalikeJDBC’s originalway!
  26. 26. What’s Query DSL- Just appends SQL parts- Type safe and pragmatic- Easy to understand- Not composable but reusable- Parts from sqls object- Append sqls”...” when needed
  27. 27. Query DSL examplesimport scalikejdbc._, SQLInterpolation._implicit val session = AutoSessionval c = Company.syntax(“c”)val id = 123val company: Option[Company] = withSQL {select.from(Company as c).where.eq(c.id, id)}.map(Company(c.resultName)).single.apply()insert.into(Company).values(123, “Typesafe”)val column = Company.columnupdate(Company).set(column.name -> “Oracle”).where.eq(column.id, 123)delete.from(Company).where.eq(column.id, 123)
  28. 28. Joins, one-to-x APIval programmerWithSkills = withSQL {select.from(Programmer as p).leftJoin(Company as c).on(p.companyId, c.id).leftJoin(ProgrammerSkill as ps).on(ps.programmerId, p.id).leftJoin(Skill as s).(ps.skillId, s.id).where.eq(p.id, id).and.isNull(p.deletedAt)}.one(Programmer(p, c)).toMany(SKill.opt(s)).map { (pg, skills) => pg.copy(skills = skills) }.single.apply()
  29. 29. Sub query, Pagingval noSkillProgrammers = withSQL {select.from(Programmer as p).leftJoin(Company as c).on(p.companyId, c.id).where.notIn(p.id,select(sqls.distinct(ps.programmerId)).from(ProgrammerSkill as ps)).isNull(p.deletedAt).limit(10).offset(0).orderBy(p.id).desc}.map(Programmer(p, c)).list.apply()
  30. 30. sqls.xxx, sqls”...”val userId =val orderCount: Long = withSQL {select(sqls.count(sqls.distinct(o.id))) // sqls object.from(Order as o).innerJoin(Product as p).on(p.id,o.productId).where.append(sqls”${o.userId} = ${userId}”) // direct SQL embedding}.map(rs => rs.long(1)).single.apply().get
  31. 31. More and More- insert select- groupBy, having- in, exists, like, between- union, unionAll- withRoundBracket { ... }- dynamic(And|Or)Conditions { ... }In detail:QueryDSLFeature.scalaQueryInterfaceSpec.scala
  32. 32. More Features
  33. 33. Code Generator- sbt plugin- Code from existing tables- project/scalikejdbc.properties- Play2 style models and testsIn detail:Wiki Pagesbt “scalikejdbc-gen [table-name]”
  34. 34. Testing with ScalaTestimport scalikejdbc._, scalatest.AutoRollbackimport org.scalatest.fixture.FlatSpecclass MemberSpec extends FlatSpec with AutoRollback {override def fixture(implicit s: DBSession) {sql”delete from members”.update.apply()Member.create(1, “Alice”)}behavior of “Member”it should “create a new record” in { implicit s =>val beforeCount = Member.countMember.create(123, “Brian”)Member.count should equal(before + 1)}}
  35. 35. Testing with specs2 (1)import scalikejdbc._, specs2.mutable.AutoRollbackimport org.specs2.mutable.Specificationobject MemberSpec extends Specification {“Member should create a new record” in new MyAutoRollback {val beforeCount = Member.countMember.create(123, “Brian”)Member.count should equal(before + 1)}}trait MyAutoRollback extends AutoRollback {override def fixture(implicit s: DBSession) {sql”delete from members”.update.apply()Member.create(1, “Alice”)}}
  36. 36. Testing with specs2 (2)import scalikejdbc._, specs2.AutoRollbackimport org.specs2.Specificationobject MemberSpec extends Specification { def is =“Member should create a new record” ! autoRollback().createend}case class autoRollback() extends AutoRollback {override def fixture(implicit s: DBSession) {sql”delete from members”.update.apply()Member.create(1, “Alice”)}def create = this {val beforeCount = Member.countMember.create(123, “Brian”)Member.count should equal(before + 1)}}
  37. 37. Play2 Support- Integrates with Play2 seamlessly- Read conf/application.conf- Add plugin to conf/play.plugins- FixturePlugin is also available- Great contribution by @tototoshiIn detail:Wiki Page
  38. 38. Get Started Now
  39. 39. More infoExamplesdevteam-app exampleQueryInterfaceSpec.scalaReferenceWiki PagesUsers GroupScalikeJDBC Users GroupScalikeJDBC Users Group Japan
  40. 40. Enjoy!Just write SQLand get things done!git.io/scalikejdbc

×