作为 ScalaQuery 的作者,我对 Stilgar 的解释没有什么要补充的。Scala 中缺少的 LINQ 部分确实是表达式树。这就是为什么 ScalaQuery 对 Column 和 Table 类型而不是这些实体的基本类型执行所有计算的原因。
您将表声明为具有列的投影(元组)的 Table 对象,例如:
class User extends Table[(Int, String)] {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = id ~ name
}
User.id 和 User.name 现在分别是 Column[Int] 和 Column[String] 类型。所有计算都在 Query monad 中执行(这是一种比必须从它创建的 SQL 语句更自然的数据库查询表示)。进行以下查询:
val q = for(u <- User if u.id < 5) yield u.name
经过一些隐式转换和脱糖后,这将转化为:
val q:Query[String] =
Query[User.type](User).filter(u => u.id < ConstColumn[Int](5)).map(u => u.name)
filter 和 map 方法不必检查它们的参数作为表达式树来构建查询,它们只是运行它们。从类型中可以看出,表面上看起来像“u.id:Int < 5:Int”实际上是“u.id:Column[Int] < u.id:Column[Int]”。运行此表达式会生成查询 AST,例如 Operator.Relational("<", NamedColumn("user", "id"), ConstColumn(5))。类似地,Query monad 的“filter”和“map”方法实际上并不执行过滤和映射,而是建立一个描述这些操作的 AST。
QueryBuilder 然后使用这个 AST 为数据库构造实际的 SQL 语句(使用 DBMS 特定的语法)。
ScalaQL采用了另一种方法,它使用编译器插件直接处理表达式树,确保它们只包含数据库查询中允许的语言子集,并静态构造查询。