author: @deltam
Colojure用DBアクセスライブラリ、Kormaの検索系機能の紹介とリレーション機能の実験。
Korma 0.3.0-beta11
https://github.com/korma/Korma
簡単なブログシステム
Userテーブル
Postテーブル: User_idカラムでユーザを持つ
User 1->N Post の関係
CREATE TABLE `User` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` text,
`email` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `Post` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` text,
`text` text,
`createdAt` timestamp NULL DEFAULT NULL,
`updatedAt` timestamp NULL DEFAULT NULL,
`User_id` int(11) unsigned DEFAULT NULL,
`name` text, /* リレーションのテスト用 */
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
project.clj:
(defproject korma-sample "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.3.0"]
[korma "0.3.0-beta9"]
[mysql/mysql-connector-java "5.1.13"]]
:plugins [[lein-swank "1.4.4"]])
core.clj:
(ns korma-sample.core
(:use [korma db core]))
;; DBアクセスの定義
(defdb mydb
(mysql
{:db "kormatest"
:user "korma"
:password "korma"}))
;; Userテーブル
(defentity user
(has-many post)
(database mydb))
;; Postテーブル
(defentity post
(belongs-to user)
(database mydb))
user> (select user)
[{:email "[email protected]", :name "deltam", :id 1}
{:email "[email protected]", :name "dio", :id 2}
{:email "[email protected]", :name "qtaro", :id 3}]
Where句:
user> (clojure.pprint/pprint
(select user
(where (= :id 1))))
[{:email "[email protected]", :name "deltam", :id 1}]
Where句でSQL関数を使う場合:
user> (select post
(where (< :createdAt (sqlfn NOW)))))
[{:name "kkk",
:User_id 1,
:updatedAt #<Timestamp 2012-09-17 20:18:15.0>,
:createdAt #<Timestamp 2012-09-17 20:18:07.0>,
:text "テスト・テスト",
:title "kormaをつかってみた",
:id 1}
{:name "kkk",
:User_id 1,
:updatedAt #<Timestamp 2012-09-18 20:18:15.0>,
:createdAt #<Timestamp 2012-09-18 20:18:07.0>,
:text "テスト・テスト",
:title "kormaをつかってみた",
:id 2}]
Where句はこういう書き方もできる(中置記法っぽい)
(select post
(where {:createdAt [< (sqlfn now)]})))
いろいろめんどくさいのでWhere句をSQLそのまま書きたい場合
(select post
(where (raw "17=DAY(createdAt)")))
じっさいどんなSQLが流れてるの? -> sql-only, dry-runを使う
(sql-only
(select post
(where (raw "17=DAY(createdAt)"))))
"SELECT `post`.* FROM `post` WHERE 17=DAY(createdAt)"
(dry-run
(select post
(where (raw "18=DAY(createdAt)"))))
dry run :: SELECT `post`.* FROM `post` WHERE 18=DAY(createdAt) :: []
[{:id 1}]
user> (select post
(with user))
[{:text "テスト・テスト",
:name "kkk",
:id_2 1,
:title "kormaをつかってみた",
:User_id 1,
:createdAt #<Timestamp 2012-09-17 20:18:07.0>,
:email "[email protected]",
:updatedAt #<Timestamp 2012-09-17 20:18:15.0>,
:id 1,
:name_2 "deltam"}
{:text "テスト・テスト",
:name "kkk",
:id_2 1,
:title "kormaをつかってみた",
:User_id 1,
:createdAt #<Timestamp 2012-09-18 20:18:07.0>,
:email "[email protected]",
:updatedAt #<Timestamp 2012-09-18 20:18:15.0>,
:id 2,
:name_2 "deltam"}]
残念ながら、いまのバージョンのbelongs-toではリレーション先カラムとリレーション元カラムが混じってしまう。UserとPostに同じカラムがあった場合は、リレーション先のカラムがcolumn_2
など変更される様子。
次にhas-manyではどうなるか。
user> (select user
(with post))
({:post
[{:name "kkk",
:User_id 1,
:updatedAt #<Timestamp 2012-09-17 20:18:15.0>,
:createdAt #<Timestamp 2012-09-17 20:18:07.0>,
:text "テスト・テスト",
:title "kormaをつかってみた",
:id 1}
{:name "kkk",
:User_id 1,
:updatedAt #<Timestamp 2012-09-18 20:18:15.0>,
:createdAt #<Timestamp 2012-09-18 20:18:07.0>,
:text "テスト・テスト",
:title "kormaをつかってみた",
:id 2}],
:email "[email protected]",
:name "deltam",
:id 1}
{:post [], :email "[email protected]", :name "dio", :id 2}
{:post [], :email "[email protected]", :name "qtaro", :id 3})
こっちはうまく行ってるみたいです。
dry-runしてみると2回クエリ発行しているみたい。
(dry-run (select user (with post)))
dry run :: SELECT `user`.* FROM `user` :: []
dry run :: SELECT `post`.* FROM `post` WHERE (`post`.`user_id` = ?) :: [1]
({:post [{:id 1}], :id 1})
(select user
(join post (= :post.User_id :user.id)))
[{:email "[email protected]", :name "deltam", :id 1}
{:email "[email protected]", :name "deltam", :id 1}
{:email "[email protected]", :name "dio", :id 2}
{:email "[email protected]", :name "qtaro", :id 3}]
なぜかJoin結果が来ない。。。
(sql-only
(select user
(join post (= :post.User_id :user.id))))
"SELECT `user`.* FROM `user` LEFT JOIN `post` ON `post`.`User_id` = `user`.`id`"
UserテーブルのカラムしかSelectされてなかったので、select対象のフィールド追加。
(select user
(fields (raw "*"))
(join post (= :post.User_id :user.id))
(where (not= nil :post.id)))
[{:text "テスト・テスト",
:name "deltam",
:id_2 1,
:title "kormaをつかってみた",
:User_id 1,
:createdAt #<Timestamp 2012-09-17 20:18:07.0>,
:email "[email protected]",
:updatedAt #<Timestamp 2012-09-17 20:18:15.0>,
:id 1,
:name_2 "kkk"}
{:text "テスト・テスト",
:name "deltam",
:id_2 2,
:title "kormaをつかってみた",
:User_id 1,
:createdAt #<Timestamp 2012-09-18 20:18:07.0>,
:email "[email protected]",
:updatedAt #<Timestamp 2012-09-18 20:18:15.0>,
:id 1,
:name_2 "kkk"}]
無事Joinはできました。
リレーション機能は発展途上ですが、全体的に筋が良い感じがする。
いまのうちに貢献しとくと将来的にモテるのでは。