Slick Напишите простую функцию создания таблицы

Эмм... Я пробую Slick с Play 2. Процесс создания таблицы стал очень разочаровывающим, потому что, в отличие от других ORM (например, ebean), Slick не определяет, создана ли база данных, если таблица уже существует, он сообщит об исключении. Я просто не хочу бросать и создавать каждый раз, когда я перезагружаю сервер, поэтому я решил написать небольшую функцию, которая мне поможет:

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

Что это делает, так это принимает некоторые объекты, подобные этому:

  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
  }

И используйте метод MTable из scala.slick.jdbc.meta.MTable, чтобы узнать, существует таблица или нет. Затем я столкнулся с простой проблемой отражения Java. Если метод databaseCreate принимает строки, я могу вызвать .ddl.create. Поэтому я решил передать объекты и использовать reffection: table.getClass.getName. Единственная проблема заключается в несоответствии типов: (из моей IDE)

Ожидаемый: MySQLDriver.simple.type#Table, фактический: BlogData.Tag.type

BlogData — это большой объект, который я использовал для хранения всех меньших объектов таблицы. Как решить эту проблему несоответствия?? Использовать целую кучу asInstanceOf? Это сделало бы команду невыносимо длинной и уродливой...


Исправлено:

Несоответствие типов является ложной тревогой, которая исходит от IDE, а не от компилятора консоли typesafe. Настоящая проблема:

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

Затем я последовал совету и изменил код:

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

Затем я получил эту ошибку:

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

Мой импорт здесь:

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

person windweller    schedule 20.12.2013    source источник
comment
также опубликовано здесь: groups.google.com/d/msg/scalaquery/ Nh79acqDnoY/J-lcPPAT_3sJ   -  person cvogt    schedule 20.12.2013


Ответы (4)


Не уверен, почему вы получаете это сообщение об ошибке. Мне нужно было бы увидеть ваш импорт и место, где вы вызываете databaseCreate, но неправильно то, что def databaseCreate(tables: Table*) должно быть def databaseCreate(tables: Table[_]*) и, возможно, также принимать второй список аргументов def databaseCreate(tables: Table[_]*)(implicit session: Session) = ....

Также вместо table.getClass.getName можно использовать table.tableName.

person cvogt    schedule 20.12.2013
comment
Ах!! Спасибо, по крайней мере теперь первая ошибка (со стороны компилятора, а не глупой IDE) решена. Теперь все, что он говорит, это то, что session неоднозначно. - person windweller; 21.12.2013
comment
Удалите импорт threadLocalSession и используйте db.withSession{ session:Session =› ... } там, где вам нужен сеанс. ИЛИ (но я советую первое) удалить список аргументов (неявный сеанс: сеанс), который я посоветовал вам добавить, поскольку не ожидал, что вы будете использовать threadLocalSession. - person cvogt; 21.12.2013
comment
Есть ли причина не импортировать threadLocalSession или предпочтительнее всегда использовать {session:Session => ...}? Я вроде как использовал threadLocalSession в другом месте.. - person windweller; 21.12.2013
comment
Использование db.withSession{ session:Session => ... } гарантирует, что во время компиляции у вас есть сеанс. Когда вы импортируете threadLocalSession, вы можете забыть о вызове db.withSession{ ... }, и компилятор Scala не увидит его. - person cvogt; 21.12.2013
comment
Вау! Спасибо! Наконец-то обошел эту проблему. Несоответствие типов произошло из-за того, что я импортировал два разных источника, содержащих один и тот же класс, и вы помогли мне (большое время) с сеансом! Спасибо! Я, вероятно, больше не буду использовать threadLocalSession - person windweller; 21.12.2013

В Slick 2: для полноты картины см. это и этот связанный поток.

Я использую следующий метод:

def createIfNotExists(tables: TableQuery[_ <: Table[_]]*)(implicit session: Session) {
  tables foreach {table => if(MTable.getTables(table.baseTableRow.tableName).list.isEmpty) table.ddl.create}
}

Затем вы можете просто создать свои таблицы с неявным сеансом:

db withSession {
  implicit session =>
    createIfNotExists(table1, table2, ..., tablen)
}
person Michael Jess    schedule 20.09.2014

Я предполагаю, что вы используете SLICK v1, поскольку у вас есть объекты Table. У меня есть рабочий код для v1. Эта версия удалит все существующие таблицы перед их воссозданием (но не удалит таблицы, которые вы не хотите воссоздавать). Он также объединит DDL в один перед созданием, чтобы получить правильную последовательность создания:

def create() = database withSession {
  import scala.slick.jdbc.{StaticQuery => Q}
  val tables = Seq(TableA, TableB, TableC)
  def existingTables = MTable.getTables.list().map(_.name.name)
  tables.filter(t => existingTables.contains(t.tableName)).foreach{t =>
    Q.updateNA(s"drop table ${t.tableName} cascade").execute
  }
  val tablesToCreate = tables.filterNot(t => existingTables.contains(t.tableName))
  val ddl: Option[DDL] = tablesToCreate.foldLeft(None: Option[DDL]){(ddl, table) =>
    ddl match {
      case Some(d) => Some(d ++ table.ddl)
      case _ => Some(table.ddl)
    }
  }
  ddl.foreach{_.create}
}
person Synesso    schedule 03.01.2014

Вы должны использовать asTry с таблицей create DBIOAction . Он проверит, существует ли таблица... если не существует, DBIOAction будет работать для создания таблицы.

на домашней страницеDAO

   val createHomePageTableAction: DBIOAction[Int, NoStream, Effect.Schema with Effect.Write] = homePageTable.schema.create >>(homePageTable+=Homepage("No Data","No Data",0l))

в ExplorePageDAO

  val SoftwareTableCreateAction: FixedSqlAction[Unit, NoStream, Effect.Schema] = softwareTable.schema.create

И в createTablesController

package controllers

import javax.inject.{Inject, Singleton}
import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
import services.dbServices.{ExplorePageDAO, HomePageDAO}

import scala.concurrent.ExecutionContext

@Singleton
class CreateTablesController @Inject()(cc: ControllerComponents)(implicit assetsFinder: AssetsFinder, executionContext: ExecutionContext)
  extends AbstractController(cc) {

  import services.dbServices.dbSandBox._
  def createTable: Action[AnyContent] = Action.async {

    dbAccess.run(HomePageDAO.createHomePageTableAction .asTry >> ExplorePageDAO.SoftwareTableCreateAction.asTry).map(_=> Ok("Table Created"))
  }


}
person John    schedule 22.11.2018