什么ORM与Scala一起使用?

时间:2009-07-16 21:33:07

标签: database orm scala

我即将编写一个依赖于MySQL数据库的Scala命令行应用程序。我一直在寻找ORM,并且找不到合适的ORM。

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

我不是来自Java的Scala,所以我不知道所有选项。有没有人使用过Scala的ORM,如果有的话,你使用了什么以及它的效果如何?

6 个答案:

答案 0 :(得分:34)

有几个原因可以解释为什么面向JPA的框架(例如Hibernate)不能很好地适应惯用的Scala应用程序:

  • 没有嵌套注释作为Scala 2.8 Preview的状态 - 这意味着您不能将注释用作复杂应用程序的映射元数据(即使是最简单的注释也经常使用@JoinTable - > @JoinColumn );
  • Scala和Java集合之间的不一致使开发人员转换集合;还有一些情况是,如果不实现底层框架的复杂接口(例如,Hibernate的PersistentCollections),就无法将Scala集合映射到关联;
  • 一些非常常见的功能,例如域模型验证,需要持久化类的JavaBeans约定 - 这些东西并不是“Scala方式”的做法;
  • 当然,互操作问题(如原始类型或代理)引入了一个无法轻易绕过的全新问题。

我确信还有更多原因。这就是我们开始Circumflex ORM project的原因。这个纯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中;查询本身非常灵活,您可以在变量中存储查询对象,谓词,投影,子查询和关系别名,以便您可以重复使用它们,甚至可以从现有查询中进行批量更新操作(insert-select for例子);
  • 关联之间的透明导航(一对一,多对一,一对多和多对多 - 通过中间关系)可以通过懒惰或急切的提取策略来实现;在这两种情况下,关联都建立在潜在关系的外键之上;
  • 验证是框架的一部分;
  • 还有一个Maven2插件,允许生成模式并从方便的XML格式文件导入初始数据。

Circumflex ORM缺乏的唯一内容是:

  • 多列主键(尽管可以创建由多列唯一约束支持的多列外键,但它仅用于数据完整性);
  • 完整的文档(尽管我们正在积极开展工作);
  • 以Circumflex ORM为核心技术的价值数十亿美元的生产系统的成功故事。

P.S。我希望这篇文章不会被视为广告。实际上并非如此 - 我试图尽可能客观。

答案 1 :(得分:9)

我尝试了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 = _
}

答案 2 :(得分:4)

我很高兴宣布为Scala推出新的ORM库。 MapperDao将域类映射到数据库表。它目前支持mysql,postgresql(即将推出的oracle驱动程序),一对一,多对一,一对多,多对多关系,自动生成的密钥,事务,并可选择与spring完美集成框架。它允许自由设计不受持久性细节影响的域类,鼓励不变性并且类型安全。该库不是基于反射,而是基于良好的Scala设计原则,并包含用于查询数据的DSL,其非常类似于选择查询。它不需要实现equals()或hashCode()方法,这对于持久化实体可能会有问题。使用类型安全的Scala代码完成映射。

详细信息和使用说明可在mapperdao的网站上找到:

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

答案 3 :(得分:2)

这里的@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

}

答案 4 :(得分:2)

Slick是功能性世界的完美搭档。传统的ORM不适合Scala。光滑的构图很好,并使用模仿Scala集合类和理解的DSL。

答案 5 :(得分:1)

当然,任何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/