clj-record是仿Ruby on Rails ActiveRecord的一个持久化框架。项目源码在github上,使用说明写的很坑爹,就不能写个能跑起来的代码么。只是断断续续学了不久clojure,被逼去看源码,试了好几次,总算写出了能跑起来的代码。
添加依赖
[mysql/mysql-connector-java "5.1.21"]
[clj-record "1.1.4"]
样例代码:假设有test库,表名users,字段id,name,email。
(默认规则:model叫user,对应的表名叫users。之前一直搞错导致没初始化成功那些crud方法。)
(ns examples.user
(:require clj-record.boot))
(def db {:subprotocol "mysql"
:subname "//127.0.0.1:3306/test"
:user "root"
:password "123456"
:auto-commit true
:fetch-size 1000})
(clj-record.core/init-model)
;;列举几个常用的CRUD
;;具体其他方法可以查看一下clj-record.core的源码就知道了,都在宏定义中
(create {:name "pi" :email "[email protected]"})
(update {:name "pi" :email "[email protected]"})
(find-records {:name "pi"})
(all-records)
(destroy-record {:id 1})
(destroy-records {:name "pi"})
查看clj-record框架的clj-record.core的源码
(defmacro init-model
"Macro to create a model out of a clojure namespace.
The segment of the namespace name following the last dot is used as the model-name.
Model-specific versions of most public functions in clj-record.core are defined
in the model namespace (minus the model-name as first argument).
Optional forms for associations, validation, etc. are specified here.
See test/clj_record/test_model/manufacturer.clj for an example."
[& init-options]
(let [model-name (last (string/split (name (ns-name *ns*)) #"\."))
[top-level-options option-groups] (split-out-init-options init-options)
tbl-name (or (top-level-options :table-name) (dashes-to-underscores (pluralize model-name)))
optional-defs (defs-from-option-groups model-name option-groups)]
`(do
(init-model-metadata ~model-name)
(set-db-spec ~model-name ~'db)
(set-table-name ~model-name ~tbl-name)
(def ~'model-name ~model-name)
(def ~'table-name (table-name ~model-name))
(defn ~'model-metadata [& args#]
(apply model-metadata-for ~model-name args#))
(defn ~'table-name [] (table-name ~model-name))
(defn ~'record-count
([] (record-count ~model-name))
([attributes#] (record-count ~model-name attributes#)))
(defn ~'get-record [id#]
(get-record ~model-name id#))
(defn ~'all-records []
(all-records ~model-name))
(defn ~'find-records [attributes#]
(find-records ~model-name attributes#))
(defn ~'find-record [attributes#]
(find-record ~model-name attributes#))
(defn ~'find-by-sql [select-query-and-values#]
(find-by-sql ~model-name select-query-and-values#))
(defn ~'create [attributes#]
(create ~model-name attributes#))
(defn ~'insert [attributes#]
(insert ~model-name attributes#))
(defn ~'update [attributes#]
(update ~model-name attributes#))
(defn ~'destroy-record [record#]
(destroy-record ~model-name record#))
(defn ~'destroy-records [attributes#]
(destroy-records ~model-name attributes#))
(defn ~'delete-records [attributes#]
(delete-records ~model-name attributes#))
(defn ~'validate [record#]
(~'clj-record.validation/validate ~model-name record#))
(defn ~'after-destroy [attributes#]
(after-destroy ~model-name attributes#))
(defn ~'after-insert [attributes#]
(after-insert ~model-name attributes#))
(defn ~'after-load [rows#]
(after-load ~model-name rows#))
(defn ~'after-save [attributes#]
(after-save ~model-name attributes#))
(defn ~'after-update [attributes#]
(after-update ~model-name attributes#))
(defn ~'after-validation [attributes#]
(after-validation ~model-name attributes#))
(defn ~'before-destroy [attributes#]
(before-destroy ~model-name attributes#))
(defn ~'before-insert [attributes#]
(before-insert ~model-name attributes#))
(defn ~'before-save [attributes#]
(before-save ~model-name attributes#))
(defn ~'before-update [attributes#]
(before-update ~model-name attributes#))
(defn ~'before-validation [attributes#]
(before-validation ~model-name attributes#))
~@optional-defs)))
可以看出的几点:
1、默认的db是采用全局的db变量。
2、model-name默认是文件名。或者说ns的最后一部分
3、table-name可以init的时候作为参数传入。
4、do语句后面顺便帮我们初始化了些crud等方法。一开始没看懂,后来自己学着写了个小例子,总算明白。
user=> (defmacro init [] `(do (defn ~'say [attr#] (prn attr#))))
#'user/init
user=> (init)
#'user/say
user=> (say "hello")
"hello"
nil
这种感觉就像是有一个可以操作数据库的工具类,里面有很多静态方法,全部搬运到我们新建的类中。
(附件为这个例子的lein2工程。我用的IDE是Intellij IDEA。运行之前确定数据库和表存在。汗!!!)
参考资料
https://github.com/duelinmarkers/clj-record