Schema code generation

The Slick code generator is a convenient tool for working with an existing or evolving database schema. It can be run as stand-alone or integrated into you sbt build for creating all code Slick needs to work with it.

Overview

By default the code generator generates Table classes, corresponding TableQuery values, which can be used in a collection-like manner as well as case classes for holding complete rows of values. For Tables with more than 22 columns the generator automatically switches to Slick’s experimental HList implementation for overcoming Scala’s tuple size limit. (Note that compilation times currently get extremely long for more than 25 columns. We are hoping to fix this as soon as possible).

The implementation is ready for practical use, but since it is new in Slick 2.0 we consider it experimental and reserve the right to remove features without a deprecation cycle if we think that it is necessary. It would be only a small effort to run an old generator against a future version of Slick though, if necessary, as it’s implementation is rather isolated from the rest of Slick. We are interested in hearing about people’s experiences with using it in practice.

Parts of the generator are also explained in our talk at Scala eXchange 2013.

Run from the command line or Java/Scala

Slick’s code generator comes with a default runner. You can simply execute

scala.slick.model.codegen.SourceCodeGenerator.main(
  Array(slickDriver, jdbcDriver, url, outputFolder, pkg)
)

and provide the following values

  • slickDriver Fully qualified name of Slick driver class, e.g. “scala.slick.driver.H2Driver”
  • jdbcDriver Fully qualified name of jdbc driver class, e.g. “org.h2.Driver”
  • url jdbc url, e.g. “jdbc:postgresql://localhost/test”
  • outputFolder Place where the package folder structure should be put
  • pkg Scala package the generated code should be places in

The code generator places a file “Tables.scala” in the given folder in a subfolder corresponding to the package. The file contains an object “Tables” from which the code can be imported for use right away. Make sure you use the same Slick driver. The file also contains a trait “Tables” which can be used in the cake pattern.

Integrated into sbt

The code generator can be run before every compilation or manually. An example project showing both can be found here.

Customization

The generator can be flexibly customized by overriding methods to programmatically generate any code based on the data model. This can be used for minor customizations as well as heavy, model driven code generation, e.g. for framework bindings (Play,...), other data-related, repetetive sections of applications, etc.

This example shows a customized code-generator and how to setup up a multi-project sbt build, which compiles and runs it before compiling the main sources.

The implementation of the code generator is structured into a small hierarchy of sub-generators responsible for different fragments of the complete output. The implementation of each sub-generator can be swapped out for a customized one by overriding the corresponding factory method. SourceCodeGenerator contains a factory method Table, which it uses to generate a sub-generator for each table. The sub-generator Table in turn contains sub-generators for Table classes, entity case classes, columns, key, indices, etc. Custom sub-generators can easily be added as well.

Within the sub-generators the relevant part of the Slick data model can be accessed to drive the code generation.

Please see the api documentation for info on all of the methods that can be overridden for customization.

Here is an example for customizing the generator:
import scala.slick.jdbc.meta.createModel
import scala.slick.model.codegen.SourceCodeGenerator
// fetch data model
val model = db.withSession{ implicit session =>
  createModel(H2Driver.getTables.list,H2Driver) // you can filter specific tables here
}
// customize code generator
val codegen = new SourceCodeGenerator(model){
  // override mapped table and class name
  override def entityName =
    dbTableName => dbTableName.dropRight(1).toLowerCase.toCamelCase
  override def tableName =
    dbTableName => dbTableName.toLowerCase.toCamelCase

  // add some custom import
  override def code = "import foo.{MyCustomType,MyCustomTypeMapper}" + "\n" + super.code

  // override table generator
  override def Table = new Table(_){
    // disable entity class generation and mapping
    override def EntityType = new EntityType{
      override def classEnabled = false
    }

     // override contained column generator
    override def Column = new Column(_){
      // use the data model member of this column to change the Scala type, e.g. to a custom enum or anything else
      override def rawType =
        if(model.name == "SOME_SPECIAL_COLUMN_NAME") "MyCustomType" else super.rawType
    }
  }
}
codegen.writeToFile(
  "scala.slick.driver.H2Driver","some/folder/","some.packag","Tables","Tables.scala"
)