session => // SQL Interpolation val name = sql”select name from company where id = ${id}” .map(rs => rs.string(“name”)).single.apply() // Query DSL val name = withSQL { select(c.name).from(Company as c).where.eq(c.id, id) }.map(rs => rs.string(c.resultName.name)).single.apply() } SQL Interpolation; not a simple String interpolation. sql”…” generates a PreparedStatement object internally. Thus, SQL injection vulnerability never happens as far as you use this feature. QueryDSL; Internal DSL to build SQL Interpolation Using the DSL to build SQL statements is more type-safe than using SQL Interpolation directly.
compatibility - Multiple data sources support - Detailed logging for debugging - Unit testing support - Handy - Play Framework module - Source code generator
user, password) = (“jdbc:h2:mem:db”, “sa”, “”) ConnectionPool.singleton(url, user, password) val conn: java.sql.Connection = ConnectionPool.borrow() val names: Seq[String] = DB.readOnly { implicit session => sql”select name from company”.map(_.string(“name”).list.apply() } // named connection pool val poolSettings = new ConnectionPoolSettings(maxSize = 50) ConnectionPool.add(‘secondary, url, user, password, poolSettings) NamedDB(‘secondary).readOnly { implicit session => Setting up the default connection pool Setting up a named connection pool For more information: http://scalikejdbc.org/documentation/ connection-pool.html
the DBSession directly val name: Option[String] = DB readOnly { session: DBSession => session.single(“select name from company where id = ?”, id) { rs => rs.string(“name”) } } // Running SQL object requires implicit DBSession val name: Option[String] = DB readOnly { implicit session => sql”select name from company where id = ${id}” .map(_.string(“name”)).single.apply() } 1) SQL#single#apply() internally runs DBSession#single method. 2) If no implicit DBSession is available here, the code results in compilation error! DBSession’s APIs are inspired by github.com/twitter/querulous at the beginning.
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() // AutoSession provides a new session DB localTx { implicit session => addCompany(“Typesafe”) // within the transaction getAllNames() // within the transaction, “Typesafe” will be contained in the result }
will be embedded as-is val 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 piece of SQL which can be embedded as-is into SQL objects.
case class Member(id: Long, fullName: Option[String] = None) // companion object for entity object Member extends SQLSyntaxSupport[Member] { def apply(m: ResultName[Member])(rs: WrappedResultSet) = new Member( id = rs.get(m.id), name = rs.get(m.fullName) ) ) val m = Member.syntax(“m”) val id = 123 val member: Option[Member] = DB readOnly { implicit s => sql”select ${m.result.*} from ${Member as m} where ${m.id} = ${id}” .map(Member(m.resultName)).single.apply() }
== “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
= 123 val 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 ^
c = Company.syntax(“c”) val id = 123 val 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.column update(Company).set(column.name -> “Oracle”).where.eq(column.id, 123) delete.from(Company).where.eq(column.id, 123) The DSL looks very similar to SQL. It should be intuitive for everybody!
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() A programmer can have n skills.
select id, name from users where email = '[email protected]'; (3 ms) [Stack Trace] ... models.User$.findByEmail(User.scala:26) controllers.Projects$$anonfun$index$1$$anonfun$apply$1$$anonfun$apply$2.apply(Projects.scala:20) controllers.Projects$$anonfun$index$1$$anonfun$apply$1$$anonfun$apply$2.apply(Projects.scala:19) controllers.Secured$$anonfun$IsAuthenticated$3$$anonfun$apply$3.apply(Application.scala:88) Issued SQL statement Stack trace dump to figure out where the query was issued in the source code Execution time
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.count Member.create(123, “Brian”) Member.count should equal(before + 1) } } The prepared rows by the fixture are also rolled back when the tests complete. All the operations are surely rolled back when the tests completed regardless of the test results.
MemberSpec extends Specification { def is = “Member should create a new record” ! autoRollback().create end } 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.count Member.create(123, “Brian”) Member.count should equal(before + 1) } } The prepared rows by the fixture are also rolled back when the tests complete.
"2.6.0-scalikejdbc-3.2" // conf/application.conf play.modules.enabled += "scalikejdbc.PlayDBApiAdapterModule" scalikejdbc-play-dbapi-adapter is the easiest way to set up ScalikeJDBC in Play applications.
- Object-oriented > Functional - Convenient for 80% use cases - Flexible enough for the rest - e.g., Querying no-PK tables - The last resort for corner cases: Directly using ScalikeJDBC
= None, score: Double ) object Member extends SkinnyCRUDMapper[Member] { override lazy val defaultAlias = createAlias("m") override def extract(rs: WrappedResultSet, n: ResultName[Member]) = autoConstruct(rs, rn) } data class representing a table row (plain Scala class) Although defining as the companion is preferred, it works even if it’s not the companion but whatever else. Possible other base traits: SkinnyMapper (read-only), SkinnyNoIdMapper, SkinnyMapperWithId[Id, Entity], etc.
company: Option[Company] = None) object Member extends SkinnyCRUDMapper[Member] { lazy val companyRef = belongsTo[Company](Company, (m, c) => m.copy(company = c)) override lazy val defaultAlias = createAlias("m") override def extract(rs: WrappedResultSet, n: ResultName[Member]) = autoConstruct(rs, rn) } val m = Member.defaultAlias val nameIsAlice = sqls.eq(m.name, “Alice”) Member.joins(Member.companyRef).findAllBy(nameIsAlice) Member.joins(Member.companyRef).where(nameIsAlice).apply() If the field (companyId here) is class name + “Id”, that’s the simplest (just use belongsTo in mapper). If you have a different name, use belongsToWithFk/WithJoinCondition instead.