【问题标题】:Scala type resolution of inner class内部类的Scala类型解析
【发布时间】:2013-09-18 00:42:27
【问题描述】:

以下代码显示了一个包含两个 Slick 表定义的模块(以 trait 的形式),第二个具有对第一个的 fk 引用。每个表对象定义一个称为 Id 的内部案例类,用作其主键。这一切都可以编译并正常工作。

trait SlickModule {
  val driver = slick.driver.BasicDriver

  import driver.Table

  case class A(id: TableA.Id, name: String)
  case class B(id: TableB.Id, aId: TableA.Id)

  import scala.slick.lifted.MappedTypeMapper
  implicit val aIdType = MappedTypeMapper.base[TableA.Id, Long](_.id, new TableA.Id(_))
  implicit val bIdType = MappedTypeMapper.base[TableB.Id, Long](_.id, new TableB.Id(_))

  object TableA extends Table[A]("table_a") {
    case class Id(id: Long)

    def id = column[TableA.Id]("id", O.PrimaryKey, O.AutoInc)

    def name = column[String]("name", O.NotNull)

    def * = id ~ name <> (A.apply _, A.unapply _)
  }

  object TableB extends Table[B]("table_b") {
    case class Id(id: Long)

    def id = column[Id]("id", O.PrimaryKey, O.AutoInc)

    def aId = column[TableA.Id]("fk_a", O.NotNull)
    def fkA = foreignKey("fk_a", aId, TableA)(_.id)

    def * = id  ~ aId <> (B.apply _, B.unapply _)
  }
}

但是,如果我将 TableA 中 id 的列定义从

def id = column[TableA.Id]("id", O.PrimaryKey, O.AutoInc)

为此,通过删除 Id 的显式路径

def id = column[Id]("id", O.PrimaryKey, O.AutoInc)

我收到以下编译错误:

type mismatch;
  found   : SlickModule.this.TableA.type => scala.slick.lifted.Column[x$5.Id] forSome { val x$5: SlickModule.this.TableA.type }
  required: SlickModule.this.TableA.type =>  scala.slick.lifted.Column[SlickModule.this.TableA.Id]
  Error occurred in an application involving default arguments.
    def fkA = foreignKey("fk_a", aId, TableA)(_.id)
                                   ^

因此,aId 列的类型参数是沿着现在包含 TableA.type 的路径找到的,而该参数应该是 TableA.Id。谁能解释为什么会出现这种差异以及我如何在不需要显式引用 TableA 对象的情况下解决它?我正在尝试将我的主键列的定义抽象为一个特征,而这个问题阻止了我这样做。

我正在使用 Scala 2.10.2 编译器。

【问题讨论】:

  • 如果你让你的表类而不是单例对象会发生什么?
  • 它只是添加了更多代码,因为我仍然需要从表实例的类扩展的对象,但不能解决问题。

标签: scala types slick


【解决方案1】:

我不完全确定您的代码究竟为什么会出现编译错误,但以下内容似乎可以实现您的目标:

trait TableModule {
  import scala.slick.lifted.{MappedTypeMapper, BaseTypeMapper}
  val driver = slick.driver.BasicDriver
  case class Id(id: Long)
  type Row
  abstract class Table(name: String) extends driver.Table[Row](name) {
    def id = column[Id]("id", O.PrimaryKey, O.AutoInc)
    import driver.Implicit._
    def findById(id: Id) = (for (e <- this if (e.id === id)) yield e)
  }
  implicit def idTypeMapper : BaseTypeMapper[Id] = MappedTypeMapper.base[Id, Long](_.id, new Id(_))
}

trait Schema {
  object ModuleA extends TableModule {
    case class Row(id: Id, name: String)
    object table extends Table("table_a") {
      def name = column[String]("name", O.NotNull)
      def * = id ~ name <> (Row.apply _, Row.unapply _)
    }
  }

  object ModuleB extends TableModule {
    case class Row(id: Id, aId: ModuleA.Id)
    object table extends Table("table_b") {
      def name = column[String]("name", O.NotNull)
      def aId = column[ ModuleA.Id]("fk_a", O.NotNull)
      def fkA = foreignKey("fk_a", aId, ModuleA.table)(_.id)
      def * = id  ~ aId <> (Row.apply _, Row.unapply _)
    }
  }
}

object schema extends Schema {
  def main(args: Array[String]): Unit = {
    val ddl = ModuleA.table.ddl ++ ModuleB.table.ddl
    println("Create:")
    ddl.createStatements.foreach(println)
    println("Delete:")
    ddl.dropStatements.foreach(println)
  }
}

特别是与不同表关联的Id 类是不同的,因此

val aid = ModuleA.Id(1)
val bid : ModuleB.Id = aid

编译失败

[error]  found   : Schema.ModuleA.Id
[error]  required: Schema.ModuleB.Id
[error]     val bid : ModuleB.Id = aid

【讨论】:

  • 非常有趣。这不是我所需要的,因为我正在尝试使用蛋糕模式来将多个表模块折叠到数据库对象中,但是如果我将 Schema 更改为特征而不是对象,那么我将接近什么我想要,而且我认为一些调整会给我一个可用的解决方案。非常感谢您提供的创新方法。
  • 麻烦。如果我将 ModuleA.table 更改为 object table extends Table("table_a") { def name = column[String]("name", O.NotNull) def * = id ~ name &lt;&gt; (Row.apply _, Row.unapply _) import driver.Implicit._ def findById(id: Id): Option[Row] = (for (e &lt;- this if (e.id === id)) yield e) } 的样子,我会收到错误 value === is not a member of scala.slick.lifted.Column[Schema.ModuleA.Id。不知何故,没有找到引入 === 的隐含。
  • 抱歉,cmets 中不允许换行,所以注释有点难以阅读。
  • 我看不出是什么导致了错误,但也许这个问题有解决方案stackoverflow.com/questions/18147396/…
  • 事实上,完全关闭 idTypeMapper 的类型声明,让编译器推断它,也可以。那太棒了。就我所推的而言,这似乎是有效的,因此应该是扩展我需要的一个很好的起点。非常感谢您在这方面的帮助。
猜你喜欢
  • 2014-06-30
  • 2020-08-05
  • 2017-05-15
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
相关资源
最近更新 更多