EDIT: Il y a maintenant une assez bonne wiki à propos de la Scala bibliothèques et frameworks ici.
Je sais de cinq utilisable non-ORM bibliothèques de base de données pour Scala. Il y a aussi un ORM que je mentionne ci-dessous, car il ne veut pas cacher SQL, ce qui peut faire un bon ajustement.
Slick (previsously nommé Scalaquery)
Le premier est Lisse. Il est le plus mature, et il essaie de faire des recherches à utiliser le même pour les-de la compréhension que Scala collections. Comme un exemple de la syntaxe de style (ce qui pourrait être légèrement en dehors de la date):
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")
}
Comme le projet a été renommé récemment, certaines ressources sont encore sous scalaquery nom (site web, groupes). Slick va bientôt être inclus dans Typesafe pile qui peut être vu comme une preuve de sa maturité.
Querulous
Le second est Querulous, qui est un projet open source à partir de Twitter. Celui-ci vous donne un accès direct à SQL, tout en traitant avec un tas de jdbc désagréments. Voici un exemple simple:
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
Le troisième est Squeryl. Côté Style, elle se situe à mi-chemin entre ScalaQuery -- qui cache SQL derrière Scala interprétations, autant que possible, -- et Querulous -- qui utilise des chaînes SQL directement.
Squeryl fournit un type SQL LIS, qui vous donne le type de la sécurité et de vous donner une forte probabilité que les états ne tombera pas en panne au moment de l'exécution si la compilation. Encore une fois, un exemple simple:
// 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 Courtier
Le quatrième est O/R Courtier, qui, malgré son nom, n'est pas un ORM. Les Classes peuvent être conçus de toute manière souhaitée. Pas d'interfaces/traits à mettre en œuvre, pas de conventions à respecter, pas d'annotations nécessaires.
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])
Les extracteurs sont déclaratives, écrit en Scala. Peuvent être réutilisés dans d'autres requêtes qui correspondent à l'attente de l'extracteur.
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)
)
}
}
On pourrait alors l'utiliser comme ceci:
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
Anorm vient de Jouer de Cadre, et je ne sais pas si il peut être utilisé de façon autonome ou pas. Fondamentalement, il les fossés mappages et DSL complètement, vous donnant un accès direct à SQL. Une simple requête peut ressembler à ceci:
// 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
Il prend également en charge la correspondance de modèle pour la ligne d'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)
}
La liaison des variables dans les requêtes utilise cette syntaxe:
SQL(
"""
select * from Country c
join CountryLanguage l on l.CountryCode = c.Code
where c.code = {countryCode};
"""
).on("countryCode" -> "FRA")
Et il possède aussi un support pour l'utilisation d'analyser les combinators de traduire les requêtes ou même des schémas de table dans des structures de données. Vous pouvez soit définir l'analyseur de vous-même, ou l'utilisation de certaines conventions par défaut (comme une classe de cas de mappage de noms de champ pour les noms de colonne) et de le laisser faire le travail pour vous.
Circonflexe ORM
Enfin, il y a un accent Circonflexe ORM. Je suis la copie ici quelques exemples de leur site:
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
Si j'ai manqué aucun projet existant, il suffit de déposer un commentaire et je vais les ajouter à cette réponse. Ne vous embêtez pas avec les blogs, documents, wikis ou autres, si.
jOOQ
Bien que jOOQ est actuellement principalement solution Java, il rend encore SQL tout à fait bien dans Scala grâce à la Scala de capacités d'omettre des points et des parenthèses pour certains appels de méthode. L'exemple suivant a été prise à partir d'une autorité de blog. D'autres exemples ici.
// Fetch book titles and their respective authors into
// a result, and print the result to the console.
val result = (create
select (
BOOK.TITLE as "book title",
AUTHOR.FIRST_NAME as "author's first name",
AUTHOR.LAST_NAME as "author's last name")
from AUTHOR
join BOOK on (AUTHOR.ID equal BOOK.AUTHOR_ID)
where (AUTHOR.ID in (1, 2, 3))
orderBy (AUTHOR.LAST_NAME asc)
fetch)
et aussi
// Iterate over authors and the number of books they've written
// Print each value to the console
for (r <- (create
select (AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, count)
from AUTHOR
join BOOK on (AUTHOR.ID equal BOOK.AUTHOR_ID)
where (AUTHOR.ID in (1, 2, 3))
groupBy (AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
orderBy (AUTHOR.LAST_NAME asc)
fetch)) {
print(r.getValue(AUTHOR.FIRST_NAME))
print(" ")
print(r.getValue(AUTHOR.LAST_NAME))
print(" wrote ")
print(r.getValue(count))
println(" books ")
}