Search code examples
scalaplayframeworkslick

Slick Write a Simple Table Creation Function


Emm...I'm trying out Slick with Play 2. The table creation process has become very frustrating because unlike other ORM (like ebean), Slick doesn't detect if the database is created, if there is already a table existing, it will report an exception. I simply just don't want to drop and create every time I restart the server, so I decide to write a small function to help me:

  def databaseCreate(tables: Table*) = {
     for (table <- tables) {
       if (MTable.getTables(table.getClass.getName).list.isEmpty) table.ddl.create
     }
  }

What this does is to take in some objects like this one:

  object Tag extends Table [(Option[Int], String)]("Tags") {
    def id = column[Int]("TAG_ID", O.PrimaryKey, O.AutoInc)
    def tag_name = column[String]("TAG_NAME")

    def * = id.? ~ tag_name
  }

And use MTable method from scala.slick.jdbc.meta.MTable to know if the table exists or not. Then I kinda run into a simple Java reflection problem. If databaseCreate method takes Strings, then I can invoke .ddl.create. So I decide to pass in objects and use relfection: table.getClass.getName. The only problem is there is a type mismatch: (from my IDE)

Expected: MySQLDriver.simple.type#Table, actual: BlogData.Tag.type

BlogData is the big object I used to store all smaller table objects. How do I solve this mismatch problem?? Use a whole bunch of asInstanceOf? It would make the command unbearably long and ugly...


Corrected:

The type mismatch is a false alarm, which comes from the IDE, not from the typesafe console complier. The real problem is:

type Table takes type parameters
def databaseCreate(tables: Table*) = {
^
one error found

Then I followed the advice and changed the code:

def databaseCreate(tables: Table[_]*)(implicit session: Session) = {
         for (table <- tables) {
           if (MTable.getTables(table.tableName).list.isEmpty) table.ddl.create
         }
      }

Then I got this error:

ambiguous implicit values: both value session of type slick.driver.MySQLDriver.simple.Session and method threadLocalSession in object Database of type => scala.slick.session.Session match expected type scala.slick.session.Session
if (MTable.getTables(table.tableName).list.isEmpty) table.ddl.create
^
one error found

My imports are here:

import play.api.GlobalSettings
import play.api.Application
import models.BlogData._
import scala.slick.driver.MySQLDriver.simple._
import Database.threadLocalSession
import play.api.Play.current
import scala.slick.jdbc.meta.MTable

Solution

  • Not sure why you get this error message. I would need to see your imports and the place where you call databaseCreate, but what is wrong is def databaseCreate(tables: Table*) should be def databaseCreate(tables: Table[_]*) and probably take a second argument list as well def databaseCreate(tables: Table[_]*)(implicit session: Session) = ....

    Also, instead of table.getClass.getName, you can use table.tableName.