Clojure - next.jdbc に入門した
logs clojure · getting-started
next.jdbc を初めて触ってみたので、その備忘録
モチベーション
Clojure 自体の入門がぼちぼちできてきた感じがうっすらするので、具体的なアプリケーションを作成してみたい。 JVM といえばサーバサイド、サーバサイドといえば RDB を避けては通れないよね? ということで next.jdbc に入門してみる。
ちなみに、Clojure でのDBアクセスと言えば clojure.java.jdbc がデファクトのようなので、本当はそっちを先に学ぶべき。
next.jdbc ってなに?
clojure.java.jdbc の作者が開発している低レベルなJDBCベースのdatabaseアクセスライブラリ。
clojure.java.jdbc
A low-level Clojure wrapper for JDBC-based access to databases. This project is “Stable” (no longer “Active”). It has effectively been superseded by seancorfield/next.jdbc. https://github.com/clojure/java.jdbc#clojurejavajdbc
next.jdbc
The next generation of clojure.java.jdbc: a new low-level Clojure wrapper for JDBC-based access to databases. https://github.com/seancorfield/next-jdbc#nextjdbc-
とのことなので、作者の Sean Corfield としては 今後は next.jdbc の方の開発を進めてゆく感じのようだ。
導入
まずは導入として、 もっとも基本的な機能である get-connectionでのデータベース接続 と execute! および execute-one! でのクエリ実行をやってみる
データベースを構築
まずは 操作対象となる データベースをローカルに構築する。 docker-compose を使ってローカルに PostgreSQL 1 を立てるために、設定ファイルと定義ファイルを準備。
$ mkdir -p postgresql/conf
$ # postgres.conf
$ cat <<EOF > postgresql/conf/postgres.conf
listen_addresses = '*'
max_connections = 10
shared_buffers = 128MB
EOF
$ #Dockerfile
$ cat <<EOF > postgresql/Dockerfile
FROM postgres:10.5
# ロケール設定
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
EOF
続いて、 docker-compose.yml での起動定義を記述
$ cat <<EOF > docker-compose.yml version: '3.1'
services:
rdb:
build:
context: ./postgresql
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_DB: next-jdbc-basic
#POSTGRES_PASSWORD: passwd
ports:
- "5432:5432"
volumes:
- ./postgresql/conf:/etc/postgresql/
EOF
コンテナを起動して、 host=localhost
かつ user=postgres
で疎通ができれば準備完了
$ docker-compose up -d
...
$ psql -h localhost -U postgres -c "\l next-jdbc-basic";
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------------+----------+----------+------------+------------+-------------------
next-jdbc-basic | postgres | UTF8 | ja_JP.utf8 | ja_JP.utf8 |
(1 row)
いつもなら ここで DDL を実行するところだけど、今回は next.jdbc からDDLを発行してみる。
REPL から触ってみる
clj
で repl を起動し、スキーマの初期化およびレコードを投入してみる。
PostgreSQL 向けの JDBCドライバは pgjdbc-ng を採用することとして、 deps.edn に next.jdbc および JDBCドライバ への依存を定義
;; deps.edn
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "RELEASE"}
seancorfield/next.jdbc {:mvn/version "1.0.7"}
com.impossibl.pgjdbc-ng/pgjdbc-ng {:mvn/version "0.8.2"}}}
clj
を起動し、 user namespace に next.jdbc をロード
$ clj
Clojure 1.10.1
user=> (require 'next.jdbc 'clojure.pprint)
nil
next.jdbc
にて定義されている各種関数は第1引数に Database Connection を受け取るようなので、
まずは next.jdbc/get-connection
を実行し、 Connection を生成する。
user=> (def db-spec {:dbtype "pgsql" :dbname "next-jdbc-basic" :user "postgres"})
#'user/db-spec
user=> (def ds (next.jdbc/get-connection db-spec))
#'user/ds
user=> (type ds)
com.impossibl.postgres.jdbc.PGDirectConnection
生成された Connection を使って、クエリを発行してみる
user=> ; テーブル作成
user=> (next.jdbc/execute! ds ["
CREATE TABLE todo (
id UUID PRIMARY KEY,
title VARCHAR(256) NOT NULL,
done BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);"])
[#:next.jdbc{:update-count -1}]
user=> ; レコードを投入
user=> (next.jdbc/execute! ds ["
INSERT INTO todo(id, title) VALUES (?, ?);
", (java.util.UUID/randomUUID), "my first todo item"])
[#:next.jdbc{:update-count 1}]
user=> ; レコードを抽出
user=> ; next.jdbc/exectute-one! は返却値を hash-map で返却する
user=> (-> (next.jdbc/execute-one! ds ["SELECT * FROM todo;"])
(clojure.pprint/pprint))
#:todo{:id #uuid "70702da6-1b6c-4cd8-a799-b7756f091314",
:title "my first todo item",
:done false,
:created_at #inst "2019-09-24T01:57:22.658499000-00:00"}
nil
next.jdbc/execute!
を使用して SELECT
を発行した場合は、抽出結果は ベクタ で返却されるようだ
user=> (-> (next.jdbc/execute! ds ["SELECT * FROM todo;"])
(clojure.pprint/pprint))
[#:todo{:id #uuid "70702da6-1b6c-4cd8-a799-b7756f091314",
:title "my first todo item",
:done false,
:created_at #inst "2019-09-24T01:57:22.658499000-00:00"}
#:todo{:id #uuid "d1397c78-5dde-4dfb-974a-23e128d985e9",
:title "my second todo item",
:done false,
:created_at #inst "2019-09-24T02:00:32.907179000-00:00"}]
nil
とりあえず、テーブル作成・レコード登録・抽出はできた 😀
おわりに
next.jdbc では next.jdbc.sql
配下に SQL操作 を簡易にするための関数群が用意されているらしく、
実際の開発時にはそちらを使用することになるだろう。friendly-sql-functions.md#friendly-sql-functions
関数 | 用途 |
---|---|
insert! | 単一テーブルに単一行を登録する |
insert-multi! | insert! の複数行版 |
query | execute! のエイリアス。行を抽出 |
update! | 単一テーブルの行更新 |
delete! | 単一テーブルから行削除 |
簡単なプロジェクトも作ったので、これらについても忘れないうちにメモっておきたいところ。
サンプルプロジェクト: https://github.com/micheam/examples-clj/tree/master/next-jdbc-basic
-
ちょっと ライブラリを 触ってみるだけなのに大仰な感じもするし、本家の Getting Started にも記載されている通り H2 Database で試して見るのが良いんだろうけど。個人的にはまとまったアプリケーションを作るなら PostgreSQL を使いたいし、ということで。 ↩︎