clsqlがとまる
clsqlについては
http://d.hatena.ne.jp/yuki_neko_nyan/searchdiary?word=clsql
がすごくよくまとまっている。参考になる。
CLOSを用いた簡易O/R Mapperのような機能をつかい、セレクタマクロを書いて、dbからレコードを取得していた。
(defmacro db-select-1 (db slot target) `(car (clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t))) (defmacro $d (db slot target) `(db-select-1 ,db ,slot ,target)) ($d 'user 'id 0) -> <USER>
しかし、長時間アクセスがないと、dbがとまってしまうようだ。
($d 'user 'id 0) ; Error 2006 / MySQL server has gone away ; has occurred. ; [Condition of type CLSQL-SYS:SQL-DATABASE-DATA-ERROR]
これでは、webなどつくったとき、すぐ止まってしまってつかいものにならない。
とりあえず以下のようにした。
(defun retry-sql-access (condition) (let ((*connect-if-exists* :new)) (when (= (clsql:sql-error-error-id condition) 2006) (clsql:reconnect :database (clsql:sql-error-database condition)) (invoke-restart 'retry-sql-access)))) (defmacro db-select-1 (db slot target) (let ((sel `(clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t))) `(car (handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access)) (restart-case ,sel (retry-sql-access () ,sel)))))) (defmacro $d (db slot target) `(db-select-1 ,db ,slot ,target))
sqlエラーが出た際、スタックを巻き戻し、再接続してから処理を再起動する。
もっとよい方法もあるのだろうか。
ちなみにほかのマクロ
- 条件に合う複数のdbレコードを取得
(defmacro db-select (db slot target) (let ((sel `(clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t))) `(handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access)) (restart-case ,sel (retry-sql-access () ,sel))))) (defmacro $ds (db slot target) `(db-select ,db ,slot ,target)) ;($ds 'user 'age 25)
- スロットの値が唯一であるか検索
(defmacro unique-value? (db slot target) (let ((sel `(clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t))) `(null (handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access)) (restart-case ,sel (retry-sql-access () ,sel)))))) (defmacro $u? (db slot target) `(unique-value? ,db ,slot ,target)) ;($u? 'user 'age 150)
- dbインスタンスをつくって、そのままdbへ保存。
(defmacro make-new-instance-and-save (db &rest initargs) (let ((sel `(clsql:update-records-from-instance (make-instance ,db ,@initargs)))) `(handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access)) (restart-case ,sel (retry-sql-access () ,sel))))) (defmacro $c! (db &rest initargs) `(make-new-instance-and-save ,db ,@initargs)) ;($c! 'user :id 100 :name "nagayoru" :age 21)
こんなかんじ。
それにしても、Commonlispのデザインパターンはまだいまいちわからない。