JDBCを軽くラップし、Scalaで使いやすくしたシンプルなO/Rマッパです。
以下の特徴を持ちます。
以下の環境で動作します。
| OS | Javaが動作するOS(Windows/Linux/Mac OS Xなど) |
|---|---|
| Database | JDBC 2.0およびSQL92に準拠したデータベース |
| Java | Java 5(1.5)以上のランタイム |
| Scala | Scala 2.7.2以上のランタイム |
注意:
モデルとは、SELECT(検索)/INSERT(挿入)/UPDATE(更新)/DELETE(削除)されるレコードとしてマッピングされるオブジェクトであり、検索・更新などを使います。
1レコードはモデルのインスタンスと等価で、複数レコードは、Seq(シーケンス)のオブジェクトとして抽象化され扱われます。Seqオブジェクトの定義はDB Flavorが用意しますので必要ありません。
通常関数型言語プログラミングにおいて、なるべく副作用が起こらないようimmutable(不変)のコレクションなどを使いますが、DB Flavorではプログラミングをしやすくすることを目的としているため、mutable(可変)のモデルとして定義します。
Scalaの場合は、プロパティメソッドを定義します。プロパティメソッドがそのままデータベーステーブルのフィールドにマッピングされます。
明示的にマッピングする場合は、メソッドに@DBFieldアノテーションをつけます。
クラス名はそのままテーブル名として扱われます。
class Person {
@DBField var name: String = ""
@DBField var address: String = ""
@DBField var phone: String = ""
// 確認用
override def toString =
"Person(name=" + name + ",address=" + address + ",phone=" + phone + ")"
}
Javaの場合は、Java Beans規則に沿ったプロパティメソッドを定義します。プロパティメソッドがそのままデータベーステーブルのフィールドにマッピングされます。
明示的にマッピングする場合は、メソッドに@DBFieldアノテーションをつけます。
クラス名はそのままテーブル名として扱われます。
public class Person {
String name = "";
String address = "";
String phone = "";
@DBField
public String setName(String value) {
name = value;
}
@DBField
public String getName() {
return name;
}
@DBField
public String setAddress(String value) {
address = value;
}
@DBField
public String getAddress() {
return address;
}
@DBField
public String setName(String name) {
this.name = name;
}
@DBField
public String getName() {
return name;
}
// 確認用
@Override
public String toString() {
return "Person(name=" + name + ",address=" + address + ",phone=" + phone + ")";
}
}
import jp.ne.cappuccino.keisuken.db.flavor._
// モデルクラス(2.2参照)
class Person {
...
}
val PERSON = classOf[Person]
// データベースオブジェクト作成
val url = "..."
val db = new DBFlavor(url)
// 検索と結果表示
// SELECT * FROM PERSON
for (person <- db.find(PERSON))
println(person)
// データベースクローズ
db.close
// モデルクラス(2.2参照)
class Person {
...
}
val db = new DBFlavor(url)
val person = new Person(
"NISHIMOTO Keisuke", "Japan, Okayama, ...", ...)
db.append(person)
db.close
class Person {
...
}
val db = new DBFlavor(url)
val person = new Person
person.name = "Foo"
// 全フィールド更新
db.update(
person,
or("name = ?", "Foo")
)
db.update(
person,
List('name),
or("name = ?", "NISHIMOTO Keisuke")
)
db.close
class Person {
...
}
val db = new DBFlavor(url)
val person = new Person
person.name = "Foo"
db.remove("name = ?" -> "NISHIMOTO Keisuke")
db.close
val PERSON = classOf[Person]
// 条件が1つの場合
// DELETE FROM PERSON WHERE name = 'NISHIMOTO Keisuke'
db.remove(
PERSON,
"name = ?", "NISHIMOTO Keisuke"
)
// 条件が複数ですべてANDの場合
// DELETE FROM PERSON WHERE name = 'NISHIMOTO Keiuske' and name = 'Foo' and 'Bar'
db.remove(
PERSON,
and("name = ?" -> List("NISHIMOTO Keisuke", "Foo", "Bar")
)
// 条件が複数でANDとORを組み合わせる場合
// DELETE FROM PERSON
// WHERE
// (name = 'Foo' or name = 'Boo') and (address = 'Okayama' or address = 'Japan')
val values = List(
"name" -> List("Foo", "Boo"),
"address" -> List("Okayama", "Japan")
)
db.remove(
PERSON,
and(values.map {(name, values) =>
or(name + " = ?", values)
})
)
DB Flavorはデータベースの複雑な操作をサポートしません。しかし実際のデータベースを使ったアプリケーションでは、複雑な検索や挿入などをしたい場合があります。
この場合は、DB FlavorではSQLを書くことをお勧めします。
たとえば、次のようなサブクエリ場合、
SELECT COUNT(ID) FROM ( SELECT ID FOO WHERE NAME LIKE ' )
実行前に実行SQL文を得ることができます。
val PERSON = classOf[Person] val statement = db.removeStatement(PERSON, "name = ?", "NISHIMOTO Keisuke") println(statement) // DELETE FROM PERSON WHERE name = 'NISIHMOTO Keisuke'
API Reference
構文を示す型です。
// ???必要か???
trait Statement {
def children: Condition
def +=(child: Condition)
def statement: String
def statement(buf: StringBuffer): StringBuffer
}
条件式を示す型です。
// 条件式
trait Condition {
def children: Seq[Condition]
def statement: Stirng
def statement(buf: StringBuffer): StringBuffer
}
// 条件式テンプレート
class TemplateCondition(val template, val value: Any) {
def children: Seq[Condition]
def statement: String = DBUtils.????(template, value)
def statement(buf: StringBuffer): StringBuffer = {
buf.append(statement)
buf
}
}
AND条件を記述するためのテンプレートメソッド
def and(template: String, values: Any*): Condition def andSeq(template: String, values: Seq[Any]): Condition def andSeq(conds: Seq[(String,Any)]: Condition // = def andEq(name: String, values: Any*) def andEqSeq(name: String, values: Seq[Any]) // != def andNq // < def andLt // <= def andLe // > def andGt // >= def andGe // between ? and ? def andBetween
val db = new DBFlavor(...)
val PERSON = classOf[Person]
db.remove(PERSON,
and("name = ?", "Foo", "Boo", Bar")
)
...
val db = new DBFlavor(...)
val PERSON = classOf[Person]
val values = Liist("Foo", "Boo", "Bar")
db.remove(PERSON,
andSeq("name = ?", values)
)
...
val db = new DBFlavor(...)
val PERSON = classOf[Person]
val values = List("name = ?" -> "Foo", "address = ?" -> "Japan")
db.remove(PERSON,
andSeq(values)
)
...
db.remove(PERSON,
andEq("name", "Foo", "Boo", "Bar")
)
// WHERE
// (name = 'Foo' and address = 'Japan')
// or (name = 'Boo' and address = 'Okayama')
val names = List('name, 'address)
val values = List(
List("Foo", "Japan"),
List("Boo", "Okayama")
)
db.remove(PERSON,
orSeq(values.map {(name, address) ->
("name = ? and address = ?", name, address)
})
)
db.remove(PERSON,
orSeq(values, {andSeq(names, _)})
)
OR条件を記述するためのテンプレートメソッド
def or(conds: (String,Any)*): Condition def or(conds: Seq[(String,Any)]: Condition
val db = new DBFlavor(...)
val PERSON = classOf[Person]
db.remove(PERSON,
or("name = ?", "Foo", "Boo", Bar")
)
...