首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >数据库迁移

数据库迁移
EN

Code Review用户
提问于 2014-02-13 16:49:12
回答 1查看 133关注 0票数 6

我开始学习Clojure,并希望对我为管理数据库迁移而编写的一些代码提供反馈。任何让它更健壮、更高效、更地道、更优雅的建议.不客气!

代码语言:javascript
复制
(ns myapp.models.migrations
  (:require [clojure.java.jdbc     :as sql]
            [myapp.models.database :as db]))

;;;; Manages database migrations.
;;;;
;;;; Usage:
;;;;
;;;; user=> (migrate!)          ; migrate to the latest version
;;;; user=> (migrate! 20140208) ; migrate to a specific version

(let [db-spec db/spec]

  ;; WARNING: Only works with PostgreSQL!
  ;;
  ;; TODO: Can this be made generic to all databases? Look into using the
  ;; JDBC database metadata to determine if a table exists.
  (defn table-exists? [table-name]
    (-> (sql/query db-spec 
                   ["select count(*) from pg_tables where tablename = ?" table-name])
        first :count pos?))

  ;;; The migrations to apply
  ;;;
  ;;; The order in which migrations are apply is determined by the :version property.
  ;;; Each migration must have :apply and :remove functions so we can migrate up or down.

  (def migration-0 {:version 0
                    :description "Starting point. Does nothing, but allows us to remove all other migrations if we want to."
                    :apply (fn [] nil)
                    :remove (fn [] nil)})

  (def migration-20140208 {:version 20140208
                           :description "Create the articles table."
                           :apply (fn []
                                    (when (not (table-exists? "articles"))
                                      (sql/db-do-commands db-spec (sql/create-table-ddl :articles
                                                                                        [:title "varchar(32)"]
                                                                                        [:content "text"]))))
                           :remove (fn []
                                     (when (table-exists? "articles")
                                       (sql/db-do-commands db-spec (sql/drop-table-ddl :articles))))})

  (def db-migrations [ migration-0
                       migration-20140208 ])

  ;;; Forms for processing the migrations.

  (defn create-migrations-table! []
    (when (not (table-exists? "migrations"))
      (sql/db-do-commands db-spec
                          (sql/create-table-ddl :migrations [:version :int]))))

  (defn drop-migrations-table! []
    (when (table-exists? "migrations")
      (sql/db-do-commands db-spec
                          (sql/drop-table-ddl :migrations))))

  (defn migration-recorded? [migration]
    (create-migrations-table!)
    (-> (sql/query db-spec ["select count(*) from migrations where version = ?" (:version migration)])
        first :count pos?))

  (defn record-migration! [migration]
    (create-migrations-table!)
    (when (not (migration-recorded? migration))
      (sql/insert! db-spec :migrations {:version (:version migration)})))

  (defn erase-migration! [migration]
    (create-migrations-table!)
    (when (migration-recorded? migration)
      (sql/delete! db-spec :migrations ["version = ?" (:version migration)])))

  (defn migrate-up! [to-version]
    (let [filtered-migrations (sort-by :version (filter #(<= (:version %) to-version) db-migrations))]
      (doseq [m filtered-migrations]
        (when (not (migration-recorded? m))
          ((:apply m))
          (record-migration! m)))))

  (defn migrate-down! [to-version]
    (let [filtered-migrations (reverse (sort-by :version (filter #(> (:version %) to-version) db-migrations)))]
      (doseq [m filtered-migrations]
        (when (migration-recorded? m)
          ((:remove m))
          (erase-migration! m)))))

  (defn migrate!  
    ([]
     (let [last-migration (last (sort-by :version db-migrations))]
       (when last-migration (migrate! (:version last-migration)))))

    ([to-version]
     (let [version (or to-version 0)
           migration-exists (not (nil? (some #(= (:version %) version) db-migrations)))
           already-applied (migration-recorded? {:version version})]
       (cond
         (not migration-exists)
           (println (format "migration %s was not found" version))
         already-applied
           (migrate-down! version)
         :else
           (migrate-up! version))))))
EN

回答 1

Code Review用户

回答已采纳

发布于 2014-03-19 14:05:40

老实说,我觉得这个代码看起来很棒!这对一个刚学过Clojure的人来说特别好。我只做了几个小小的改进:

代码语言:javascript
复制
(defn create-migrations-table! []
  (when-not (table-exists? "migrations")
    (sql/db-do-commands db-spec
                        (sql/create-table-ddl :migrations [:version :int]))))

使用(when-not x)而不是(when (not x)) --它将为您节省一些括号:)

代码语言:javascript
复制
(defn record-migration! [migration]
  (create-migrations-table!)
  (when-not (migration-recorded? migration)
    (sql/insert! db-spec :migrations {:version (:version migration)})))

(when-not也是如此)

代码语言:javascript
复制
(defn migrate-up! [to-version]
  (let [filtered-migrations (sort-by :version (filter #(<= (:version %) to-version) db-migrations))]
    (doseq [m filtered-migrations]
      (when-not (migration-recorded? m)
        ((:apply m))
        (record-migration! m)))))

(使用when-not的另一个机会)

代码语言:javascript
复制
(defn migrate!  
  ([]
   (when-let [last-migration (last (sort-by :version db-migrations))]
     (migrate! (:version last-migration))))
...

只要您有(let [x (something)] (when x (do-something)))表单的语句,就可以将其简化为(when-let [x (something)] (do-something))

最后,我会考虑调用migration-exists migration-exists?,因为它表示一个布尔值。

对我来说,唯一突出的是,您将(create-migrations-table!)作为第一行包含在其他几个函数中。这似乎是一种解决方案,并可能从函数式编程的角度引起问题。您可以考虑将(when-not (table-exists? "migrations" ...create-migrations-table!的函数定义中删除,并将其作为检查包含在其他3个函数中,如下所示:

代码语言:javascript
复制
(defn create-migrations-table! []
  (sql/db-do-commands db-spec
                      (sql/create-table-ddl :migrations [:version :int])))

(defn record-migration! [migration]
  (when-not (table-exists? "migrations") 
    (create-migrations-table!))
  (when-not (migration-recorded? migration)
    (sql/insert! db-spec :migrations {:version (:version migration)})))    

在我看来,这种方式似乎更直观-- create-migrations-table!应该假设还没有这样的方法,除非您将(table-exists? "migrations")作为条件检查,否则您可能不会使用它。另一方面,这更冗长,所以为了简单起见,您可能更喜欢保留它的方式。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/41568

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档