`
zeallf
  • 浏览: 29900 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Scala 各种数据库访问框架简介

阅读更多
目前有关 Scala libraries 和框架相当多

有一些非ORM的数据库库,也有ORM的,下面列出一些:

ScalaQuery

The first one is ScalaQuery. It is the most mature one, and it tries to make queries use the same for-comprehension as Scala collections do. As an example of syntax style (which might be slightly out of date):
在试用了一下后,发现对MS SERVER 支持地不太好

import java.lang.Integer
import com.novocode.squery._
import com.novocode.squery.Implicit._
import com.novocode.squery.session._
import com.novocode.squery.session.SessionFactory._

// Define table:
object Users extends Table[(Integer, String, String)]("users") {
  def id = intColumn("id", O.AutoInc, O.NotNull)
  def first = stringColumn("first")
  def last = stringColumn("last")
  def * = id ~ first ~ last
}

// Basic usage
val sf = new DriverManagerSessionFactory("org.h2.Driver", "jdbc:h2:mem:test1")
sf withSession {
  // Prepare a simple query
  val q1 = for(u <- Users) yield u

  // Print SQL statement to be executed:
  println(q1.selectStatement)  // displays SELECT t1.id,t1.first,t1.last FROM users t1

  // Print query result:
  for(t <- q1) println("User tuple: "+t)

  // Query statements can also be used with updates:
  val q = for(u <- Users if u.id is 42) yield u.first ~ u.last
  q.update("foo", "bar")
}



Querulous

The second one is Querulous, which is a open source project from Twitter. This one gives you direct access to SQL, while dealing with a bunch of jdbc annoyances. Here's a simple example:

import com.twitter.querulous.evaluator.QueryEvaluator
val queryEvaluator = QueryEvaluator("host", "username", "password")
val users = queryEvaluator.select("SELECT * FROM users WHERE id IN (?) OR name = ?", List(1,2,3), "Jacques") { row =>
  new User(row.getInt("id"), row.getString("name"))
}
queryEvaluator.execute("INSERT INTO users VALUES (?, ?)", 1, "Jacques")
queryEvaluator.transaction { transaction =>
  transaction.select("SELECT ... FOR UPDATE", ...)
  transaction.execute("INSERT INTO users VALUES (?, ?)", 1, "Jacques")
  transaction.execute("INSERT INTO users VALUES (?, ?)", 2, "Luc")
}



Squeryl

The third one is Squeryl. Style-wise, it sits midway between ScalaQuery -- which hides SQL behind Scala comprehensions as much as possible -- and Querulous -- which uses SQL strings directly.

Squeryl provides a SQL-like DSL, which gives you type safety and give you a strong likelyhood that the statements won't fail at run-time if they compile at all. Again, a simple example:

// Defining tables and a schema:
import org.squeryl.PrimitiveTypeMode._

class Author(var id: Long, 
             var firstName: String, 
             var lastName: String)

class Book(var id: Long, 
           var title: String,
           @Column("AUTHOR_ID") // the default 'exact match' policy can be overriden
           var authorId: Long,
           var coAuthorId: Option[Long]) {
  def this() = this(0,"",0,Some(0L))
}

object Library extends Schema {
  //When the table name doesn't match the class name, it is specified here :
  val authors = table[Author]("AUTHORS")
  val books = table[Book]
}

// Basic usage
Class.forName("org.postgresql.Driver"); 
val session = Session.create( 
  java.sql.DriverManager.getConnection("jdbc:postgresql://localhost:5432/squeryl", "squeryl", "squeryl"), 
  new PostgreSqlAdapter 
) 

//Squeryl database interaction must be done with a using block :  
import Library._
using(session) { 
  books.insert(new Author(1, "Michel","Folco"))            
  val a = from(authors)(a=> where(a.lastName === "Folco") select(a)) 
}


O/R Broker
http://code.google.com/p/orbroker/

The fourth is O/R Broker, which, despite the name, is not an ORM. Classes can be designed in any way desired. No interfaces/traits to implement, no conventions to uphold, no annotations needed.
可以快速开发,灵活性较高

case class Song(id: Option[Long], title: String, seconds: Short)
case class Album(id: Option[Long], title: String, year: Short, songs: IndexedSeq[Song])
case class Artist(id: Option[Long], name: String, albums: Set[Album])
Extractors are declarative, written in Scala. Can be reused in other queries that fit the expectation of the extractor.

object SongExtractor extends JoinExtractor[Song] {
  val key = Set("SONG_ID")

  def extract(row: Row, join: Join) = {
    new Song(
          row.bigInt("SONG_ID"), 
          row.string("TITLE").get, 
          row.smallInt("DURATION_SECONDS").get
        )
  }
}

object AlbumExtractor extends JoinExtractor[Album] {
  val key = Set("ALBUM_ID")

  def extract(row: Row, join: Join) = {
    new Album(
          row.bigInt("ALBUM_ID"),
          row.string("TITLE").get,
          row.smallInt("YEAR_ISSUED").get,
          join.extractSeq(SongExtractor, Map("TITLE"->"SONG_TITLE"))
        )  
  }
}

object ArtistExtractor extends JoinExtractor[Artist] {
  val key = Set("ARTIST_ID")

  def extract(row: Row, join: Join) = {
    new Artist(
          row.bigInt("ARTIST_ID"),
          row.string("NAME"),
          join.extractSeq(AlbumExtractor)
        )
  }
}
One could then use that like this:

val ds: javax.sql.DataSource = ...
val builder = new SQLFileBuilder(ds, new java.io.File("sql/"))
val broker = builder.build()

// Print all artists with their albums (if any)
val artists = broker.readOnly() { session =>
  session.selectAll[Artist]('selectArtist) // ' I wish they could fix the Scala Symbol formatting
}
for (ar <- artists) {
  println(a.name)
      if (ar.albums.isEmpty)
        println("\t<No albums>")
      else for (al <- ar.albums) {
        println("\t" + al.title)
        for (s <- al.songs) {
          println("\t\t" + (al.songs.indexOf(s)+1) + ". " + s.title)
        }
      }
}




Anorm
来自于Play框架,it ditches mappings and DSL completely, giving you direct access to SQL. 实例:

// Create an SQL query
val selectCountries = SQL("Select * from Country")

// Transform the resulting Stream[Row] as a List[(String,String)]
val countries = selectCountries().map(row => 
    row[String]("code") -> row[String]("name")
).toList
It also supports pattern matching for row extraction:

val countries = SQL("Select name,population from Country")().collect {
    case Row("France", _) => France()
    case Row(name:String, pop:Int) if(pop > 1000000) => BigCountry(name)
    case Row(name:String, _) => SmallCountry(name)      
}
Binding variables in queries uses this syntax:

SQL(
    """
        select * from Country c 
        join CountryLanguage l on l.CountryCode = c.Code 
        where c.code = {countryCode};
    """
).on("countryCode" -> "FRA")



And it also has support for use of parse combinators to translate queries or even table schemas into data structures. You can either define the parser yourself, or use some default conventions (like a case class mapping field names to column names) and let it do the work for you.

Circumflex ORM
网站上有例子如下:

class Category extends Record[Category] {
  val id = field(Category.id)
  val name = field(Category.name)
  val books = oneToMany(Book.category)    // allows navigating between associations transparently
}

object Category extends Table[Category] with LongIdPK[Category] {
  val name = stringColumn("name")         // creates a column
      .notNull                            // creates NOT NULL constraint
      .unique                             // creates UNIQUE constraint
      .validateNotEmpty                   // adds NotEmpty validation
      .validatePattern("^[a-zA-Z]{1,8}$") // adds Pattern validation
}

class Book extends Record[Book] {
  val id = field(Book.id)
  val title = field(Book.title)
  val category = manyToOne(Book.category)
}

object Book extends Table[Book] with LongIdPK[Book] {
  val title = stringColumn("title")
      .notNull
      .validateNotEmpty
  val category = longColumn("category_id")
      .references(Category)     // creates an association with Category
      .onDeleteSetNull          // specifies a foreign-key action
      .onUpdateCascade
}

new DDLExport(Category, Book).create   // creates database schema

// find category by id
val c = Category.get(2l)
// find all books
val allBooks = Book.all
// find books for category
val cBooks = c.get.books
// find books by title
Book.criteria.add("title" like "a%").list

select()
      .from(Category as "c" join (Book as "b"), Category as "c1")
      .where("c1.name" like "a%")
      .addOrder(asc("c.name"))
      .list

select(count("b.id"), "c.name").from(Category as "c" join (Book as "b")).list


以上资料摘至
http://stackoverflow.com/questions/1362748/wanted-good-examples-of-scala-database-persistence
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics