Slick code generation for only a single schema

自古美人都是妖i 提交于 2020-02-03 22:51:28

问题


Is there a way to have Slick's code generation generate code for only a single schema? Say, public? I have extensions that create a whole ton of tables (eg postgis, pg_jobman) that make the code that slick generates gigantic.


回答1:


Use this code with appropriate values and schema name,

object CodeGenerator {

  def outputDir :String =""
  def pkg:String =""
  def schemaList:String = "schema1, schema2"
  def url:String  = "dburl"
  def fileName:String =""
  val user = "dbUsername"
  val password = "dbPassword"
  val slickDriver="scala.slick.driver.PostgresDriver"
  val JdbcDriver = "org.postgresql.Driver"
  val container = "Tables"


  def generate() = {    

    val driver: JdbcProfile = buildJdbcProfile

    val schemas = createSchemaList

    var model = createModel(driver,schemas)

    val codegen = new SourceCodeGenerator(model){
      // customize Scala table name (table class, table values, ...)
      override def tableName = dbTableName => dbTableName match {
        case _ => dbTableName+"Table"
      }
      override def code = {
        //imports is copied right out of
        //scala.slick.model.codegen.AbstractSourceCodeGenerator
        val imports = {
          "import scala.slick.model.ForeignKeyAction\n" +
            (if (tables.exists(_.hlistEnabled)) {
              "import scala.slick.collection.heterogenous._\n" +
                "import scala.slick.collection.heterogenous.syntax._\n"
            } else ""
              ) +
            (if (tables.exists(_.PlainSqlMapper.enabled)) {
              "import scala.slick.jdbc.{GetResult => GR}\n" +
                "// NOTE: GetResult mappers for plain SQL are only generated for tables where Slick knows how to map the types of all columns.\n"
            } else ""
              ) + "\n\n" //+ tables.map(t => s"implicit val ${t.model.name.table}Format = Json.format[${t.model.name.table}]").mkString("\n")+"\n\n"
        }


        val bySchema = tables.groupBy(t => {
          t.model.name.schema
        })
        val schemaFor = (schema: Option[String]) => {
          bySchema(schema).sortBy(_.model.name.table).map(
            _.code.mkString("\n")
          ).mkString("\n\n")
        }
      }

      val joins =   tables.flatMap( _.foreignKeys.map{  foreignKey  =>
        import foreignKey._
        val fkt =   referencingTable.TableClass.name
        val pkt =   referencedTable.TableClass.name
        val columns =   referencingColumns.map(_.name)  zip
          referencedColumns.map(_.name)
        s"implicit def autojoin${fkt + name.toString} = (left:${fkt} ,right:${pkt}) =>  "   +
          columns.map{
            case    (lcol,rcol) =>
              "left."+lcol  +   "   === "   +   "right."+rcol
          }.mkString("  &&  ")
      })

      override def entityName = dbTableName => dbTableName match {
        case _ => dbTableName
      }

      override def Table = new Table(_) {
        table =>
        // customize table value (TableQuery) name (uses tableName as a basis)
        override def TableValue = new TableValue {
          override def rawName = super.rawName.uncapitalize
        }
        // override generator responsible for columns
        override def Column = new Column(_){
          // customize Scala column names
          override def rawName = (table.model.name.table,this.model.name) match {

            case _ => super.rawName
          }
        }
      }

    }

    println(outputDir+"\\"+fileName)
    (new File(outputDir)).mkdirs()
    val fw = new FileWriter(outputDir+File.separator+fileName)
    fw.write(codegen.packageCode(slickDriver, pkg, container))
    fw.close()

  }

  def createModel(driver: JdbcProfile, schemas:Set[Option[String]]): Model = {
    driver.simple.Database
      .forURL(url, user = user, password = password, driver = JdbcDriver)
      .withSession { implicit session =>
      val filteredTables = driver.defaultTables.filter(
        (t: MTable) => schemas.contains(t.name.schema)
      )
      PostgresDriver.createModel(Some(filteredTables))
    }
  }

  def createSchemaList: Set[Option[String]] = {
    schemaList.split(",").map({
      case "" => None
      case (name: String) => Some(name)
    }).toSet
  }

  def buildJdbcProfile: JdbcProfile = {

    val module = currentMirror.staticModule(slickDriver)
    val reflectedModule = currentMirror.reflectModule(module)
    val driver = reflectedModule.instance.asInstanceOf[JdbcProfile]
    driver

  }
}



回答2:


I encountered the same problem and I found this question. The answer by S.Karthik sent me in the right direction. However, the code in the answer is slightly outdated. And I think a bit over-complicated. So I crafted my own solution:

import slick.codegen.SourceCodeGenerator
import slick.driver.JdbcProfile
import slick.model.Model

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext}

val slickDriver = "slick.driver.PostgresDriver"
val jdbcDriver = "org.postgresql.Driver"
val url = "jdbc:postgresql://localhost:5432/mydb"
val outputFolder = "/path/to/src/test/scala"
val pkg = "com.mycompany"
val user = "user"
val password = "password"


object MySourceCodeGenerator {

  def run(slickDriver: String, jdbcDriver: String, url: String, outputDir: String,
          pkg: String, user: Option[String], password: Option[String]): Unit = {

    val driver: JdbcProfile =
      Class.forName(slickDriver + "$").getField("MODULE$").get(null).asInstanceOf[JdbcProfile]
    val dbFactory = driver.api.Database
    val db = dbFactory.forURL(url, driver = jdbcDriver, user = user.orNull,
                              password = password.orNull, keepAliveConnection = true)
    try {
      // **1**
      val allSchemas = Await.result(db.run(
        driver.createModel(None, ignoreInvalidDefaults = false)(ExecutionContext.global).withPinnedSession), Duration.Inf)
      // **2**
      val publicSchema = new Model(allSchemas.tables.filter(_.name.schema.isEmpty), allSchemas.options)
      // **3**
      new SourceCodeGenerator(publicSchema).writeToFile(slickDriver, outputDir, pkg)
    } finally db.close
  }
}

MySourceCodeGenerator.run(slickDriver, jdbcDriver, url, outputFolder, pkg, Some(user), Some(password))

I'll explain what's going on here:

  • I copied the run function from the SourceCodeGenerator class that's in the slick-codegen library. (I used version slick-codegen_2.10-3.1.1.)
  • // **1**: In the origninal code, the generated Model was referenced in a val called m. I renamed that to allSchemas.
  • // **2**: I created a new Model (publicSchema), using the options from the original model, and using a filtered version of the tables set from the original model. It turns out tables from the public schema don't get a schema name in the model. Hence the isEmpty. Should you need tables from one or more other schemas, you can easily create a different filter expression.
  • // **3**: I create a SourceCodeGenerator with the created publicSchema model.

Of course, it would even be better if the Slick codegenerator could incorporate an option to select one or more schemas.



来源:https://stackoverflow.com/questions/28285129/slick-code-generation-for-only-a-single-schema

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!