seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

どんな DB にも接続できる? dbconsole

dbconsole という新しいツールを公開しました。

これは DB 接続のための sbt console です。

https://github.com/seratch/scalikejdbc/tree/master/scalikejdbc-cli

インストールは簡単です。Windows にはまだ対応していません。 (2012/12/12 追記) id:rabitarochan が Windows 対応をしてくれました!

Mac OS XLinux の場合は

curl -L http://git.io/dbconsole | sh
source ${HOME}/.bash_profile

Windows の場合は

http://git.io/dbconsole.bat

をダウンロードして実行してください。

ヘルプを見てみましょう。

$ dbconsole -h

dbconsole is an extended sbt console to connect database easily.

Usage:
  dbconsole [OPTION]... [PROFILE]

General options:
  -e, --edit    edit configuration, then exit
  -c, --clean   clean sbt environment, then exit
  -h, --help    show this help, then exit

早速 sandbox のデータベースを使って試してみます。

sandbox はインストール時に用意されたファイルベースの h2 database です。dbconsole -e で確認すると以下のような設定があらかじめされています。

sandbox.jdbc.url=jdbc:h2:file:db/sandbox
sandbox.jdbc.username=
sandbox.jdbc.password=
#mysql.jdbc.url=jdbc:mysql://localhost:3306/dbname
#postgres.jdbc.url=jdbc:postgresql://localhost:5432/dbname
#oracle.jdbc.url=jdbc:oracle:thin:@localhost:1521:dbname

dbconsole は起動時に引数にプロファイル(設定の prefix)を渡します。あとは sbt が起動するのを少し待ちます。初回は依存ライブラリの解決などで少し時間がかかってしまうと思います。

$ dbconsole sandbox

Starting sbt console for sandbox...

[info] Set current project to default-8d98e7 (in build file:/Users/seratch/bin/scalikejdbc-cli/)
[info] Starting scala interpreter...
[info] 
import scalikejdbc._
import scalikejdbc.StringSQLRunner._
initialize: ()Unit
Welcome to Scala version 2.9.2 (Java HotSpot(TM) Client VM, Java 1.6.0_29).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

どんなテーブルがあるかは tables で確認できます。

scala> tables
IRIS

あらかじめ Iris data set を入れたテーブルが用意されています。テーブルの情報を見るには describe(String) を使います。

scala> describe("iris")

Table: IRIS (The Iris flower data set is a multivariate data set introduced by Sir Ronald Fisher (1936) as an example of discrimina..)
+--------------+-------------+------+-----+---------+-------+-------------+
| Field        | Type        | Null | Key | Default | Extra | Description |
+--------------+-------------+------+-----+---------+-------+-------------+
| IRIS_ID      | INTEGER(10) | NO   | PRI | NULL    |       |             |
| SEPAL_LENGTH | DOUBLE(17)  | NO   |     | NULL    |       |             |
| SEPAL_WIDTH  | DOUBLE(17)  | NO   |     | NULL    |       |             |
| PETAL_LENGTH | DOUBLE(17)  | NO   |     | NULL    |       |             |
| PETAL_WIDTH  | DOUBLE(17)  | NO   |     | NULL    |       |             |
| SPECIES      | VARCHAR(16) | NO   |     | NULL    |       |             |
+--------------+-------------+------+-----+---------+-------+-------------+
Indexes:
  "PRIMARY_KEY_2" UNIQUE, (IRIS_ID)

この出力は MySQLPostgreSQL のものを混ぜた独自のものですが、どんな DB につないでもこのフォーマットで出力されます。この例では FK がないですが FK がある場合は FK 一覧も出力されます。Oracle では Description と Index、FK 一覧が取得できません。。

それでは SQL を実行してみましょう。

文字列に #run というメソッドが追加されているのでまずはそれを使います。結果がそれぞれ Map[String, Any] に変換されて返ってきます。

scala> "select * from iris limit 3".run
res4: List[Map[String,Any]] = List(Map(PETAL_LENGTH -> 1.4, SPECIES -> setosa, IRIS_ID -> 1, SEPAL_LENGTH -> 5.1, PETAL_WIDTH -> 0.2, SEPAL_WIDTH -> 3.5), Map(PETAL_LENGTH -> 1.4, SPECIES -> setosa, IRIS_ID -> 2, SEPAL_LENGTH -> 4.9, PETAL_WIDTH -> 0.2, SEPAL_WIDTH -> 3.0), Map(PETAL_LENGTH -> 1.3, SPECIES -> setosa, IRIS_ID -> 3, SEPAL_LENGTH -> 4.7, PETAL_WIDTH -> 0.2, SEPAL_WIDTH -> 3.2))

run はどんな結果でも List[Map[String,Any]] という型で結果を返します。

scala> "select count(1) from iris".run
res2: List[Map[String,Any]] = List(Map(COUNT(1) -> 150))

結果が一つだけなので Long だけにしたい、という場合は #as[A] を使います。

scala> "select count(1) from iris".as[Long]
res3: Long = 150

結果が返ってこないかもしれない場合は #asOption[A] で受け取ります。

scala> "select species from iris where iris_id = 1".asOption[String]
res1: Option[String] = Some(setosa)

scala> "select species from iris where iris_id = 777".asOption[String]
res2: Option[String] = None

一つのカラムだけで結果が複数件返ってくる場合は #asList[A] が便利です。

scala> "select iris_id from iris limit 3".asList[Int]
res5: List[Int] = List(1, 2, 3)

insert などの更新系や DDL の実行も同じようにできます。

scala> "insert into iris values (151, 0.1, 0.2, 0.3, 0.4, 'xxx');".as[Boolean]
res6: Boolean = false

scala> "create table members (id int primary key, name varchar(256), created_at timestamp)".run
res6: List[Map[String,Any]] = List(Map(RESULT -> false))

scala> tables
IRIS
MEMBERS

トランザクションを扱いたい場合は ScalikeJDBC のDB.localTx などを使ってください。

https://github.com/seratch/scalikejdbc/wiki/GettingStartedInJapanese

scala> DB localTx { implicit session =>
     |   "insert into iris values (152, 0.1, 0.2, 0.3, 0.4, 'yyy');".run
     |   throw new RuntimeException
     | }
java.lang.RuntimeException
    at $anonfun$1.apply(<console>:16)
    at $anonfun$1.apply(<console>:14)
    at scalikejdbc.DB$$anonfun$localTx$2$$anonfun$apply$1.apply(DB.scala:606)
    at scala.util.control.Exception$Catch.apply(Exception.scala:88)
    at scalikejdbc.DB$$anonfun$localTx$2.apply(DB.scala:604)
    ...

scala> "select * from iris where iris_id = 152".run
res0: List[Map[String,Any]] = List()

現在は MySQLPostgreSQLOracle、H2、HSQLDBDerbySQLite であれば dbconsole -e で設定を追加するだけですぐにつながるようになっています。

カスタマイズしたい場合は ${HOME}/bin/scalikejdbc-cli/build.sbt を書き換えてください。