scala - 使用 Slick 实现蛋糕模式的“非法继承”
问题描述
我正在为 DB 层使用 Slick (3.3) 实现 Web 服务。我试图使 Slick 实现尽可能通用,希望能够实现与 DB 无关的以及尽可能抽象服务模型的通用表、表查询和 DAO 类。我正在尝试结合几种技术来做到这一点:
- 从公共基本特征扩展的模型层次结构
- 表层次结构,提取模型特征的常用列(灵感来自http://gavinschulz.com/posts/2016-01-30-common-model-fields-with-slick-3-part-i.html)
- DB-agnosticism,依赖于 JdbcProfile 特征而不是任何特定于 DB 的配置文件实现(如此处所述:https ://stackoverflow.com/a/31128239/4234254 和 slick multi-db docs)
- 依赖注入的蛋糕模式
但是,我在对某些模式元素进行分层时遇到了麻烦,而且我不是 Scala 类型专家,我无法自己找出解决方案。我已经创建了这个问题的复制品,试图尽可能地最小化它,并使用一个模拟的光滑库。完整的代码可以在这里找到:https ://github.com/anqit/slick_cake_minimal_error_repro/blob/master/src/main/scala/com/anqit/repro/Repro.scala但我会在下面详细介绍。
我的“光滑”库和模型类:
abstract class Table[E]
type TableQuery[W <: Table[_]] = List[W] // not actually a list, but need a concrete type constructor to demonstrate the issue
object TableQuery {
def apply[W <: Table[_]]: TableQuery[W] = List[W]()
}
trait BaseEntity
case class SubEntityA() extends BaseEntity
case class SubEntityB() extends BaseEntity
将上面列表中的技术 2 与蛋糕模式相结合,我正在为每个模型创建包装模式元素的特征。基本模式包括实体表共有的列(例如 id),实体表继承自:
trait BaseSchema[E <: BaseEntity] {
// provides common functionality
abstract class BaseTableImpl[E] extends Table[E]
def wrapper: TableQuery[_ <: BaseTableImpl[E]]
}
// functionality specific to SubEntityA
trait SchemaA extends BaseSchema[SubEntityA] {
class TableA extends BaseTableImpl[SubEntityA]
// this definition compiles fine without a type annotation
val queryA = TableQuery[TableA]
def wrapper = queryA
}
// functionality specific to SubEntityB that depends on SchemaA
trait SchemaB extends BaseSchema[SubEntityB] { self: SchemaA =>
class TableB extends BaseTableImpl[SubEntityB] {
// uses SchemaA's queryA to make a FK
}
/*
attempting to define wrapper here without a type annotation results in the following compilation error (unlike defining wrapper for SchemaA above):
def wrapper = Wrapper[WrappedB]
type mismatch;
[error] found : Repro.this.Wrapper[SubB.this.WrappedB]
[error] (which expands to) List[SubB.this.WrappedB]
[error] required: Repro.this.Wrapper[_ <: SubB.this.BaseWrapMeImpl[_1]]
[error] (which expands to) List[_ <: SubB.this.BaseWrapMeImpl[_1]]
[error] def wrapper = Wrapper[WrappedB]
[error] ^
it does, however, compile if defined with an explicit type annotation as below
*/
val queryB = TableQuery[TableB]
def wrapper: TableQuery[TableB] = queryB
}
这是我遇到的第一个错误,类型不匹配,我目前使用显式类型注释解决了这个问题,但我怀疑它与主要错误有关,敬请期待。
一个基本的 DAO,它将提供常见的查询方法:
trait BaseDao[E <: BaseEntity] { self: BaseSchema[E] => }
最后,将所有蛋糕层放在一起:
// now, the actual injection of the traits
class DaoA extends SchemaA
with BaseDao[SubEntityA]
// so far so good...
class DaoB extends SchemaA
with SchemaB
with BaseDao[SubEntityB] // blargh! failure! :
/*
illegal inheritance;
[error] self-type Repro.this.DaoB does not conform to Repro.this.BaseDao[Repro.this.SubEntityB]'s selftype Repro.this.BaseDao[Repro.this.SubEntityB] with Repro.this.BaseSchema[Repro.this.SubEntityB]
[error] with BaseDao[SubEntityB]
[error] ^
*/
第一个错误(SchemaB 中的类型不匹配),我完全不知所措。我的包中的几个技巧之一是当我在 Scala 中遇到与类型相关的错误时添加显式类型注释,这是我尝试这样做并让它编译的唯一原因。我很想解释为什么会发生这种情况,并且我怀疑修复我的代码以便我可以在没有类型的情况下编写它可能会帮助我解决第二个错误。这让我想到了……第二个错误。对我来说,看起来我已经包含了满足自我类型树的所有必要特征,但我想没有。我的猜测是SchemaB
,在扩展的同时BaseSchema[SubEntityB]
,不知何故不被认可为BaseSchema[SubEntityB]
? 我没有正确设置我的层次结构吗?或者也许我需要使用边界而不是严格的类型引用?
解决方案
推荐阅读
- java - 如何使用 querydsl 获取子字段?
- apache-spark - Spark Streaming 窗口使用
- python - gmpy2中如何准确判断一个mpfr数是否为整数?
- django - 我无法在 django 视图中作为组织登录
- javascript - 如何在 AJAX 函数完全执行之前修复 jQuery Promise .then() 被调用?
- sharepoint - 文档级别的共享点权限?可能是个愚蠢的问题
- python - 读一个“。” 分隔文件并使用 python 创建 SQL 查询
- python - 列值不在 padas 数据框中的索引中
- arrays - 使用指针数组将输入和输出输入文件
- node.js - 设置秘密时,express-jwt 秘密应设置错误