什么ORM与Scala一起工作良好?

我即将编写一个依赖于MySQL数据库的Scala命令行应用程序。 我一直在四处寻找ORM,并且很难find一个能够正常工作的。

Lift ORM看起来不错,但我不确定它可以从整个Lift Web框架中分离出来。 ActiveObjects也看起来不错,但作者说,它可能不适用于Scala。

我不是从Java来的Scala,所以我不知道所有的select。 有没有人使用Scala的ORM,如果是的话,你使用了什么,它有多好?

面向JPA的框架(例如Hibernate)不能很好地适应惯用的Scala应用程序,有几个原因:

  • Scala 2.8预览没有嵌套的注释 – 这意味着你不能使用注释作为复杂应用程序的映射元数据(即使是最简单的经常使用@JoinTable – > @JoinColumn )。
  • Scala和Java集合之间的不一致使开发人员转换集合; 还有一些情况是,如果不能实现底层框架的复杂接口(例如Hibernate的PersistentCollections ,就不可能将Scala集合映射到关联;
  • 一些非常常见的特性,例如域模型validation,需要JavaBean在持久化类上的约定 – 这些东西并不完全是“Scala方式”的处理;
  • 当然,互操作问题(如原始types或代理)引入了一个全新的难以轻松解决的问题。

我敢肯定,还有更多的原因。 这就是为什么我们启动了Circumflex ORM项目 。 这个纯粹的Scala ORM试图消除经典的Java ORM的噩梦。 具体来说,你可以用很多的方式定义你的实体,你可以用传统的DDL语句来实现:

 class User extends Record[User] { val name = "name".TEXT.NOT_NULL val admin = "admin".BOOLEAN.NOT_NULL.DEFAULT('false') } object User extends Table[User] { def byName(n: String): Seq[User] = criteria.add(this.name LIKE n).list } // example with foreign keys: class Account extends Record[Account] { val accountNumber = "acc_number".BIGINT.NOT_NULL val user = "user_id".REFERENCES(User).ON_DELETE(CASCADE) val amount = "amount".NUMERIC(10,2).NOT_NULL } object Account extends Table[Account] 

正如您所看到的,这些声明比传统的JPA POJO稍微冗长些。 但实际上有几个概念汇集在一起​​:

  • 用于生成模式的精确DDL(您可以轻松地以相同的DSL样式添加索引,外键和其他内容);
  • 所有的查询都可以在“表格对象”里面组装,而不是散落在DAO中; 查询本身非常灵活,可以在variables中存储查询对象,谓词,投影,子查询和关系别名,以便可以重用它们,甚至可以从现有查询(例如insert-select)中进行批量更新操作。
  • 协会之间的透明导航(一对一,多对一,一对多和多对多通过中间关系)可以通过懒惰或渴望获取策略来实现; 在这两种情况下,协会都build立在基础关系的外键之上;
  • validation是框架的一部分;
  • 还有一个Maven2插件,允许生成模式和从便捷的XML格式文件导入初始数据。

Circumflex ORM唯一的缺点是:

  • 多列主键(尽pipe可以创build由多列唯一约束支持的多列外键,但仅用于数据完整性)。
  • 全面的文件(尽pipe我们正在积极努力);
  • 具有Circumflex ORM技术的100亿美元生产系统的成功案例是其核心技术。

PS我希望这个post不会被认为是一个广告。 事实并非如此,真的 – 我试图尽可能客观。

我尝试了EclipseLink JPA ,基本操作对我来说工作得很好。 JPA是一个Java标准,还有其他的实现也可以工作( OpenJPA等)。 下面是一个在Scala中的JPA类的例子:

 import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity { val name = "Users" } class User { @Id @GeneratedValue var userid:Long = _ var login:String = _ var password:String = _ var firstName:String = _ var lastName:String = _ } 

我很高兴地宣布Scala的新ORM库的第一个版本。 MapperDao将域类映射到数据库表。 它目前支持mysql,postgresql(即将推出的oracle驱动程序),一对一,多对一,一对多,多对多关系,自动生成的关键字,事务处理以及可选地与弹簧很好地集成框架。 它允许在不受持久性细节影响的领域类的devise上自由,鼓励不变性和types安全。 该库不是基于reflection,而是基于良好的Scaladevise原则,并且包含一个DSL来查询数据,这与查询非常类似。 它不需要执行equals()或hashCode()方法,这对于持久实体可能是有问题的。 映射是使用types安全的Scala代码完成的。

详细信息和使用说明可以在mapperdao的网站find:

http://code.google.com/p/mapperdao/

该库可以在上述网站上下载,也可以作为maven依赖(文档包含如何通过maven使用它的细节)

例子可以在:

https://code.google.com/p/mapperdao-examples/

通过代码示例简单介绍库:

 class Product(val name: String, val attributes: Set[Attribute]) class Attribute(val name: String, val value: String) ... val product = new Product("blue jean", Set(new Attribute("colour", "blue"), new Attribute("size", "medium"))) val inserted = mapperDao.insert(ProductEntity, product) // the persisted entity has an id property: println("%d : %s".format(inserted.id,inserted)) 

查询非常熟悉:

 val o=OrderEntity import Query._ val orders = query(select from o where o.totalAmount >= 20.0 and o.totalAmount <= 30.0) println(orders) // a list of orders 

我鼓励大家使用图书馆并给予反馈。 该文档目前相当广泛,有安装和使用说明。 请随时发表评论,并在googlemail dot com的kostas dot kougios与我联系。

谢谢,

Kostantinos Kougios

这里基本上是@Column注解的例子:

  /* Corresponding table: CREATE TABLE `users` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255) default NULL, `admin` tinyint(1) default '0', PRIMARY KEY (`id`) ) */ import _root_.javax.persistence._ @Entity @Table{val name="users"} class User { @Id @Column{val name="id"} var id: Long = _ @Column{val name="name"} var name: String = _ @Column{val name="admin"} var isAdmin: Boolean = _ override def toString = "UserId: " + id + " isAdmin: " + isAdmin + " Name: " + name } 

油滑是function世界的完美搭配。 传统的ORM不适合Scala。 Slick组合得很好,使用模仿Scala集合类和理解的DSL。

当然,任何Java数据库访问框架也可以在Scala中工作,并且可能会遇到一些常见的问题,例如集合转换等。例如,jOOQ已经被观察到在Scala中运行良好。 在手册中给出了Scala中的jOOQ代码示例:

 object Test { def main(args: Array[String]): Unit = { val c = DriverManager.getConnection("jdbc:h2:~/test", "sa", ""); val f = new Factory(c, SQLDialect.H2); val x = T_AUTHOR as "x" for (r <- f select ( T_BOOK.ID * T_BOOK.AUTHOR_ID, T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4, T_BOOK.TITLE || " abc" || " xy" ) from T_BOOK leftOuterJoin ( f select (x.ID, x.YEAR_OF_BIRTH) from x limit 1 asTable x.getName() ) on T_BOOK.AUTHOR_ID === x.ID where (T_BOOK.ID <> 2) or (T_BOOK.TITLE in ("O Alquimista", "Brida")) fetch ) { println(r) } } } 

取自http://www.jooq.org/doc/2.6/manual/getting-started/jooq-and-scala/