integral - new o/r mapper for common lisp

40
New O/R Mapper for Common Lisp 株式会社はてな id:nitro_idiot 2014/01/23 Lisp Meet Up presented by Shibuya.lisp #13

Upload: fukamachi

Post on 23-Jun-2015

10.512 views

Category:

Technology


3 download

DESCRIPTION

A short introduction about a new O/R Mapper for Common Lisp named "Integral" and it's inside.

TRANSCRIPT

Page 1: Integral - New O/R Mapper for Common Lisp

New O/R Mapper for Common Lisp

株式会社はてな id:nitro_idiot

2014/01/23 Lisp Meet Up presented by Shibuya.lisp #13

Page 2: Integral - New O/R Mapper for Common Lisp

自己紹介•深町英太郎

• id:nitro_idiot

•株式会社はてな勤務

• Webアプリケーションエンジニア

•ブログ「八発白中」 blog.8arrow.org

Page 3: Integral - New O/R Mapper for Common Lisp

作ったもの

• Clack

• Caveman

• ningle

• CL-TEST-MORE

• CL-DBI

• CL-Project

• Shelly

• Lesque

• Quickdocs

• Integral

See 8arrow.org

Page 4: Integral - New O/R Mapper for Common Lisp

O/R Mapper

• O/Rマッパー知ってますか

• DBのデータをオブジェクトとして扱う

• ObjectStore知ってますか

• ↑と同じだけど永続化の概念がある

Page 5: Integral - New O/R Mapper for Common Lisp

O/R Mapper

• O/Rマッパー知ってますか

• Postmodern, CLSQL

• ObjectStore知ってますか

• Elephant, AllegroCache

Page 6: Integral - New O/R Mapper for Common Lisp

O/R Mapper

• O/Rマッパー知ってますか

• Postmodern, CLSQL

• ObjectStore知ってますか

• Elephant, AllegroCache

Page 7: Integral - New O/R Mapper for Common Lisp

例: Postmodern(defclass country ()! ((name :col-type string :initarg :name! :reader country-name)! (inhabitants :col-type integer :initarg :inhabitants! :accessor country-inhabitants)! (sovereign :col-type (or db-null string) :initarg :sovereign! :accessor country-sovereign))! (:metaclass dao-class)! (:keys name))

Page 8: Integral - New O/R Mapper for Common Lisp

例: Postmodern(defclass country ()! ((name :col-type string :initarg :name! :reader country-name)! (inhabitants :col-type integer :initarg :inhabitants! :accessor country-inhabitants)! (sovereign :col-type (or db-null string) :initarg :sovereign! :accessor country-sovereign))! (:metaclass dao-class)! (:keys name))

CREATE TABLE country (! name TEXT NOT NULL,! inhabitants INTEGER NOT NULL,! sovereign TEXT,! PRIMARY KEY (name))

Page 9: Integral - New O/R Mapper for Common Lisp

例: Integral(defclass country ()! ((name :col-type string :initarg :name! :primary-key t! :not-null t! :reader country-name)! (inhabitants :col-type integer :initarg :inhabitants! :not-null t! :accessor country-inhabitants)! (sovereign :col-type string :initarg :sovereign! :accessor country-sovereign))! (:metaclass <dao-table-class>))

Integralでもほとんど同じ。 :metaclassを指定する

Page 10: Integral - New O/R Mapper for Common Lisp

メタクラス?

•クラスのクラス

•クラス定義時に<dao-table-class>のインスタンスが作成されてcountryクラスになる

•指定しないとstandard-classのクラスになる

Page 11: Integral - New O/R Mapper for Common Lisp

メタクラス?

• (Integralでは) 何に使っているか

•自動的に <dao-class> を継承させる

•スロットのクラスを指定できる

•通常はstandard-direct-slot-definition

•その他、定義・再定義時の処理など

Page 12: Integral - New O/R Mapper for Common Lisp

メタクラス?

•まあ、変なクラスを作れるってことです。

Page 13: Integral - New O/R Mapper for Common Lisp

例: Postmodern

•話はPostmodernにもどる

Page 14: Integral - New O/R Mapper for Common Lisp

例: Postmodern

• defclass に (:metaclass dao-class) をつける

•カラムの型を指定できる

• PRIMARY KEYを指定できる

一見、良さそうに見える

Page 15: Integral - New O/R Mapper for Common Lisp

既存のORMの問題

•こんなスキーマを開発初期から定義できるわけねーだろーが!!

(defclass country ()! ((name :col-type string :initarg :name! :reader country-name)! (inhabitants :col-type integer :initarg :inhabitants! :accessor country-inhabitants)! (sovereign :col-type (or db-null string) :initarg :sovereign! :accessor country-sovereign))! (:metaclass dao-class)! (:keys name))

Page 16: Integral - New O/R Mapper for Common Lisp

既存のORMの問題

•最初にCREATE TABLE文を流してしまうと、クラス定義を変更したいときに困る

•カラム追加したくなったら?

•カラムのデータ型変えたくなったら?

• Postmodernではテーブルの再作成しかない

Page 17: Integral - New O/R Mapper for Common Lisp

Integral

• New O/Rマッパー

• Postmodernの持つ問題の解決

Page 18: Integral - New O/R Mapper for Common Lisp

Integral

•クラス定義を変更したらDBスキーマにも変更を反映する

= マイグレーション

Page 19: Integral - New O/R Mapper for Common Lisp

マイグレーション

(defclass user ()! ((name :col-type text! :initarg :name))! (:metaclass <dao-table-class>))

Page 20: Integral - New O/R Mapper for Common Lisp

マイグレーション

(defclass user ()! ((name :col-type text! :initarg :name)! (profile :col-type text! :initarg :profile))! (:metaclass <dao-table-class>))

Page 21: Integral - New O/R Mapper for Common Lisp

マイグレーション

(migrate-table 'user)!;-> ALTER TABLE `user` ADD COLUMN `profile` TEXT AFTER `name`;!;=> NIL

(defclass user ()! ((name :col-type text! :initarg :name)! (profile :col-type text! :initarg :profile))! (:metaclass <dao-table-class>))

Page 22: Integral - New O/R Mapper for Common Lisp

マイグレーション

(defclass user ()! ((id :col-type serial! :primary-key t)! (name :col-type (varchar 64)! :initarg :name)! (profile :col-type text! :initarg :profile))! (:metaclass <dao-table-class>))

(migrate-table 'user)!;-> ALTER TABLE `user` DROP COLUMN `%oid`;!; ALTER TABLE `user` MODIFY COLUMN `name` VARCHAR(64);!; ALTER TABLE `user` ADD COLUMN `id` SERIAL NOT NULL PRIMARY KEY FIRST;!;=> NIL

Page 23: Integral - New O/R Mapper for Common Lisp

オートマイグレーションモード

• *auto-migration-mode*をTにすると発動

•クラスを再定義するたびにmigrate-table

•開発時に超便利

Page 24: Integral - New O/R Mapper for Common Lisp

マイグレーションの実装

•泥臭い

•普通にDBスキーマ引いてきてスロットと比較しているだけ

• DBの種類を見て、ValidなSQLを吐く

• ALTER TABLEの実行順序も考慮

Page 25: Integral - New O/R Mapper for Common Lisp

オートマイグレーションモードの実装

• initialize-instance と reinitialize-instance の :after で *auto-migration-mode* の値を見て T なら migrate-table 走らせるだけ

Page 26: Integral - New O/R Mapper for Common Lisp

Integral

•クラス定義 → DBスキーマ の追随の話をしましたね。

•既にあるテーブルと一緒に使うにはどうしたらいいの???

Page 27: Integral - New O/R Mapper for Common Lisp

Integral

• DBスキーマ → クラス定義 の追随もできるよ

Page 28: Integral - New O/R Mapper for Common Lisp

:generate-slots

(defclass user () ()! (:metaclass <dao-table-class>)! (:generate-slots t))

!

!

DBからスキーマ定義を取ってきてスロットをつっこむ

Page 29: Integral - New O/R Mapper for Common Lisp

:generate-slots

(defclass user () ()! (:metaclass <dao-table-class>)! (:generate-slots t))

!

!

DBからスキーマ定義を取ってきてスロットをつっこむ

(make-instance 'user :name "Eitarow Fukamachi")!;=> #<USER %oid: <unbound>>

Page 30: Integral - New O/R Mapper for Common Lisp

:generate-slots

(defclass user () ()! (:metaclass <dao-table-class>)! (:generate-slots t))

!

!

DBからスキーマ定義を取ってきてスロットをつっこむ

class User < ActiveRecord::Base!end

Page 31: Integral - New O/R Mapper for Common Lisp

:generate-slots

(defclass user () ()! (:metaclass <dao-table-class>)! (:generate-slots t))

!

!

DBからスキーマ定義を取ってきてスロットをつっこむ

class User < ActiveRecord::Base!end

なんか似てますね

Page 32: Integral - New O/R Mapper for Common Lisp

:generate-slots の実装

•定義したときはスロットは空

•最初にmake-instanceする直前にDBスキーマを取ってきてスロットをつっこむ

•定義時にやらない理由は、定義時にDB接続がある保証が無いから

Page 33: Integral - New O/R Mapper for Common Lisp

:generate-slots の実装

•既存のクラスにスロットを追加するのは意外と面倒

• c2mop:ensure-class-using-class を使って上書きする

•このときスロット名が同じものが既に定義されてると死ぬのでちゃんとマージする

Page 34: Integral - New O/R Mapper for Common Lisp

CRUD

• Create: (save-dao (make-instance ‘user :name “Eitarow”))

• Read: (select-dao ‘user (where (:= :name “Eitarow”)))

• Update: (save-dao user-obj)

• Delete: (delete-dao user-obj)

Page 35: Integral - New O/R Mapper for Common Lisp

CRUD

• update-daoやdelete-daoのとき実行されるSQLはUPDATE文とDELETE文

• WHERE句が一意である必要がある

• (でないと関係ない列まで変更する)

•通常はPRIMARY KEYを使う

Page 36: Integral - New O/R Mapper for Common Lisp

CRUD

• Integralでは、PRIMARY KEYの定義が無いときは自動で%oidというPRIMARY KEYを付与

•後でPRIMARY KEYが定義されるとマイグレーションでDROP COLUMNされるから別に問題ないよね

Page 37: Integral - New O/R Mapper for Common Lisp

暗黙の継承<dao-table>

• select-dao, save-dao, delete-dao は <dao-table> のメソッドとして定義されている

• Postmodernでは定義クラスが単一のクラスを継承しない (metaclassのみ)

•テーブルクラスに共通のメソッドを定義できない

Page 38: Integral - New O/R Mapper for Common Lisp

共通のクラスを継承する利点

•たとえばRailsのScaffoldみたいな機能

•テーブルクラスからHTMLのformを吐くみたいな機能が作れる

•他バリデーション機能とか

Page 39: Integral - New O/R Mapper for Common Lisp

Postmodern vs Integral

Postmodern Integral

対応DB PostgreSQL MySQL, PostgreSQL, SQLite3

マイグレーション なし あり

自動PK なし あり

スロット自動生成

なし あり

Page 40: Integral - New O/R Mapper for Common Lisp

Integral

https://github.com/fukamachi/integral