与LINQ一样的Scala集合式SQL支持

soc*_*soc 11 c# linq database scala scalaquery

据我所知,LINQ支持的唯一内容是Scala目前不支持其集合库,它是与SQL数据库的集成.

据我了解LINQ可以"收集"各种操作和查询到那里处理它的时候可以给"整"语句到数据库,防止一个简单的SELECT第一张全表到虚拟机的数据结构.

如果我错了,我会很乐意纠正.

如果没有,在Scala中支持相同的内容是什么?

是不是可以编写一个实现集合接口的库,但是没有任何数据结构支持它,但是一个String,它将后续集合组装到所需的Database语句中?

或者我的观察完全错了?

sze*_*ger 13

作为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
}
Run Code Online (Sandbox Code Playgroud)

User.id和User.name现在分别是Column [Int]和Column [String]类型.所有计算都在Query monad中执行(这是一种比必须从中创建的SQL语句更自然的数据库查询表示).请执行以下查询:

val q = for(u <- User if u.id < 5) yield u.name
Run Code Online (Sandbox Code Playgroud)

经过一些隐含的转换和贬低后,这转化为:

val q:Query[String] =
  Query[User.type](User).filter(u => u.id < ConstColumn[Int](5)).map(u => u.name)
Run Code Online (Sandbox Code Playgroud)

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采用了另一种方法,它使用编译器插件直接处理表达式树,确保它们只包含数据库查询中允许的语言子集,并静态构造查询.


ret*_*nym 13

我应该提一下,Scala确实对表达式树有实验支持.如果将匿名函数作为参数传递给期望参数类型的方法scala.reflect.Code[A],则会获得AST.

scala> import scala.reflect.Code      
import scala.reflect.Code 
scala> def codeOf[A](code: Code[A]) = code
codeOf: [A](code:scala.reflect.Code[A])scala.reflect.Code[A]
scala> codeOf((x: Int) => x * x).tree 
res8: scala.reflect.Tree=Function(List(LocalValue(NoSymbol,x,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))),Apply(Select(Ident(LocalValue(NoSymbol,x,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))),Method(scala.Int.$times,MethodType(List(LocalValue(NoSymbol,x$1,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))),PrefixedType(ThisType(Class(scala)),Class(scala.Int))))),List(Ident(LocalValue(NoSymbol,x,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))))))
Run Code Online (Sandbox Code Playgroud)

这已在字节码生成库"口诀",这是被用来呈现在2010斯卡拉天的作者约翰·鲁道夫.


Sti*_*gar 5

使用LINQ,编译器会检查lambda表达式是否已编译为IEnumerable或IQueryable.第一个像Scala集合一样工作.第二个将表达式编译为表达式树(即数据结构).LINQ的强大之处在于编译器本身可以将lambda转换为表达式树.您可以编写一个构建表达式树的库,其界面类似于您对集合的接口,但是如何让编译器从lambdas构建数据结构(而不​​是JVM代码)?

话虽如此,我不确定Scala在这方面提供了什么.也许可以在Scala中使用lambda建立数据结构,但无论如何我相信你需要编译器中的类似功能来构建对数据库的支持.请注意,数据库不是您可以为其构建提供程序的唯一基础数据源.例如,有许多LINQ提供商可以使用Active Directory或Ebay API.

编辑:为什么不能只有一个API?

为了进行查询,您不仅要使用API​​方法(filter,Where等等),还要使用lambda表达式作为这些方法的参数.其中(x => x> 3)(C#LINQ).编译器将lambda转换为字节码.API需要构建数据结构(表达式树),以便您可以将数据结构转换为基础数据源.基本上你需要编译器为你做这个.

免责声明1:可能(只是可能)有一些方法可以创建执行lambdas的代理对象,但会重载运算符以生成数据结构.这将导致性能略差于实际LINQ(运行时与编译时).我不确定这样的库是否可行.也许ScalaQuery库使用类似的方法.

免责声明2:也许Scala语言实际上可以将lambda作为可检查对象提供,以便您可以检索表达式树.这将使Scala中的lambda特性等同于C#中的lambda特性.也许ScalaQuery库使用这个假设的功能.

编辑2:我做了一些挖掘.似乎ScalaQuery使用库方法并重载一堆运算符以在运行时生成树.我不完全确定细节,因为我不熟悉Scala术语,并且很难阅读文章中的复杂Scala代码:http: //szeiger.de/blog/2008/12/21/a-type-safe -database查询-DSL换斯卡拉/

与可以在查询中使用或从查询返回的每个对象一样,表格使用它所代表的值的类型进行参数化.这总是单个列类型的元组,在我们的例子中是Integers和Strings(注意使用java.lang.Integer而不是Int;稍后会详细介绍).在这方面,SQuery(我现在已将其命名)更接近HaskellDB而不是LINQ,因为Scala(与大多数语言一样)不允许您在运行时访问表达式的AST.在LINQ中,您可以使用数据库中值和列的实际类型编写查询,并在运行时将查询表达式的AST转换为SQL.如果没有这个选项,我们必须使用像Table和Column这样的元对象来构建我们自己的AST.

非常酷的图书馆.我希望将来它能得到应有的爱,并成为真正的生产就绪工具.