Advertisement
mitrakov

Slick: insert and return whole row

Aug 29th, 2018
344
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 3.74 KB | None | 0 0
  1. // Question on Slick: how to insert a case class instance to DB and get it back
  2. // with all the autogenerated fields filled?
  3. // Example is based on DB schema described in https://pastebin.com/5qhdsZp2 and https://pastebin.com/vdGqiwQ3
  4.  
  5. // FacultyDao.scala:
  6. package dao
  7.  
  8. import javax.inject.Inject
  9. import scala.concurrent.{ExecutionContext, Future}
  10. import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
  11. import play.db.NamedDatabase
  12. import slick.jdbc.JdbcProfile
  13. import slick.lifted.ProvenShape
  14.  
  15. case class Faculty(facultyId: Option[Long], name: String, isArts: Boolean)
  16.  
  17. class FacultyDao @Inject() (
  18.     @NamedDatabase("students") val dbConfigProvider: DatabaseConfigProvider
  19.   )(implicit val ec: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] {
  20.  
  21.   import profile.api._
  22.  
  23.   private class FacultyTable(tag: Tag) extends Table[Faculty](tag, "faculty") {
  24.     def facultyId: Rep[Long] = column[Long]("faculty_id", O.PrimaryKey, O.AutoInc)
  25.     def name: Rep[String] = column[String]("name")
  26.     def isArts: Rep[Boolean] = column[Boolean]("isArts")
  27.  
  28.     override def * : ProvenShape[Faculty] = (facultyId.?, name, isArts) <> (Faculty.tupled, Faculty.unapply)
  29.   }
  30.  
  31.   private lazy val table = TableQuery[FacultyTable]
  32.  
  33.   def insert(faculty: Faculty): Future[Long] = {
  34.     val query = table.returning(table.map(_.facultyId)) += faculty // "+=" will skip "O.AutoInc" in SQL Query so that DB Engine will take care of that
  35.     println(query.statements.mkString)
  36.     db.run(query)
  37.   }
  38. }
  39.  
  40. // CampusController.scala:
  41. package controllers
  42.  
  43. import javax.inject.Inject
  44. import scala.concurrent.ExecutionContext
  45. import play.api.mvc._
  46. import dao.{Faculty, FacultyDao}
  47.  
  48. /**
  49.  * CampusController
  50.  * @param cc Controller Components
  51.  */
  52. class CampusController @Inject()(
  53.     facultyDao: FacultyDao,
  54.     cc: ControllerComponents
  55.   )(implicit val ec: ExecutionContext) extends AbstractController(cc) {
  56.  
  57.   // don't forget to add in routes: "POST   /test/faculty/:name    controllers.CampusController.insertNewFaculty(name)"
  58.   def insertNewFaculty(name: String): Action[AnyContent] = Action.async {
  59.     val faculty = Faculty(facultyId = None, name, isArts = false) // please note that facultyId = None (auto-generated)
  60.     facultyDao.insert(faculty) map { newId: Long =>
  61.       val newFaculty = faculty.copy(facultyId = Some(newId))      // here we substitute with a new generated value
  62.       Ok(s"New faculty is $newFaculty")
  63.     }
  64.   }
  65. }
  66.  
  67.  
  68.  
  69. // 1. Here we go!
  70. // localhost:9000/test/faculty/phililogy
  71. // Stdout: insert into `faculty` (`name`,`isArts`)  values (?,?)
  72. // Output: New faculty is Faculty(Some(2),phililogy,false)
  73.  
  74.  
  75.  
  76. // 2. Now let's see more interesting example:
  77. // in DAO:
  78. def insert(faculty: Faculty): Future[Faculty] = {
  79.   val query = table.returning(table.map(t => t)) += faculty    // "t => t" will return the whole row!
  80.   println(query.statements.mkString)
  81.   db.run(query)
  82. }
  83.  
  84. // in Controller:
  85. facultyDao.insert(faculty) map { newFaculty: Faculty =>   // newFaculty is already here!
  86.   Ok(s"New faculty is $newFaculty")
  87. }
  88.  
  89. // localhost:9000/test/faculty/history
  90. // Output: [SlickException: This DBMS allows only a single column to be returned from an INSERT, and that column must be an AutoInc column.]
  91. // WTF? MySQL doesn't support it :'(
  92.  
  93.  
  94.  
  95. // 3. OK, MySQL can't return the whole row, so I'd prefer this way instead:
  96. def insert(faculty: Faculty): Future[Faculty] = {
  97.   val query = table.returning(table.map(_.facultyId)) += faculty
  98.   println(query.statements.mkString)
  99.   db.run(query) map {id => faculty.copy(facultyId = Some(id))}
  100. }
  101.  
  102. // localhost:9000/test/faculty/history
  103. // Stdout: insert into `faculty` (`name`,`isArts`)  values (?,?)
  104. // Output: New faculty is Faculty(Some(3),history,false)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement