【问题标题】:How do I read bool columns from SQLite into bool Clojure values with next.jdbc? SQLite stores booleans as 0/1如何使用 next.jdbc 将 SQLite 中的 bool 列读入 bool Clojure 值? SQLite 将布尔值存储为 0/1
【发布时间】:2020-07-21 15:12:46
【问题描述】:

这是查看问题的最小代码。

  (require '[next.jdbc :as jdbc])

  (def db-spec {:dbtype "sqlite" :dbname "example-db"})

  (jdbc/execute!
   db-spec
   ["create table if not exists users (name text, is_active bool)"])

  (jdbc/execute!
   db-spec
   ["insert into users (name, is_active) values (?, ?)" "alice" true])

  (jdbc/execute!
   db-spec
   ["select * from users"])
  ;; => [#:users{:name "alice", :is_active 1}]

我希望最后一行的is_activetruefalse。我不希望我的应用程序代码必须跟踪哪些列是 bool 并在获得结果集后进行转换。

【问题讨论】:

    标签: sqlite jdbc clojure


    【解决方案1】:

    next.jdbc 为您提供了几种方法来插入它如何从数据库值创建 Clojure 对象。其中最通用的方法是传递 :builder-fn 选项,该选项具有从 builder-adapter 创建的值,并传递您自己的(或现有的)构建器和自定义的“read-column-by-index”

      (require '[next.jdbc.result-set :as rs])
      (defn sqlite-column-by-index-fn [builder ^ResultSet rs ^Integer i]
        (let [rsm ^ResultSetMetaData (:rsmeta builder)]
          (rs/read-column-by-index
           (if (re-find #"(?i)(bool|bit)" (.getColumnTypeName rsm i))
             (.getBoolean rs i)
             (.getObject rs i))
           rsm
           i)))
      (def sqlite-builder (rs/builder-adapter rs/as-maps sqlite-column-by-index-fn))
      (jdbc/execute!
       db/db-spec
       ["select id, \"email-confirmed?\" from user;"]
       {:builder-fn sqlite-builder})
      ;; => [#:user{:id 1, :email-confirmed? true} #:user{:id 2, :email-confirmed? false}]
    
    

    注意:如果您使用plan,这并不总是有效。对于性能,plan 没有实现价值。在您减少计划时,访问结果集的值将跳过上面自定义的按索引读取列的内容。减少工作由您决定。

    更多信息,来自next.jdbc的作者,可以在https://github.com/seancorfield/next-jdbc/blob/develop/doc/tips-and-tricks.md#sqlitehttps://github.com/seancorfield/next-jdbc/issues/134找到


    这个答案的其余部分是 lagniappe。这是我一路走来的“几乎就在那里”和错误的转弯。对于可能提供的任何价值,我将其作为答案的一部分。

    next.jdbc 允许您使用自定义功能扩展 ReadableColumn 协议以转换值。

    https://github.com/seancorfield/next-jdbc/blob/develop/doc/result-set-builders.md#readablecolumn

    (extend-protocol result-set/ReadableColumn
      Integer
      (read-column-by-index [x mrs i]
        (if (re-find #"(?i)bool" (.getColumnTypeName mrs i))
          (if (= 1 x) true false)
          x)))
    

    它可能并不完美。还有另一个函数read-column-by-label,它不接收ResultSetMetaData 对象,我们可以调用getColumnTypeName 来测试列类型是否为布尔值。 read-column-by-label 只接收值和列名。我不确定不覆盖该功能的后果是什么。事情似乎只需要覆盖read-column-by-index

    编辑以说明这不是一个完整的修复。

    plan,例如,返回一个可约。 reduce 函数被传递了一个“映射”的结果集。一些接口的一些实现调用read-column-by-label。我认为这样做是出于性能原因?因此,您可以在不“构建”整个地图的情况下执行 select-keys 之类的操作。所以如果你想用减速器做下面这样的事情,那么上面的修复是不够的。

      (def plan (jdbc/plan db-spec ["select * from users;"]))
      (defn reduce-fn [a b]
        (conj a (select-keys b [:users/name :users/is_active])))
      (r/reduce reduce-fn [] plan)
      ;; => [#:users{:name "alice", :is_active 1}
      ;;     #:users{:name "bob", :is_active 0}]
    
      (jdbc/execute! db-spec ["select * from users;"])
      ;; => [#:users{:name "alice", :is_active true, :age 1}
      ;;     #:users{:name "bob", :is_active false, :age 0}]
    

    【讨论】:

    • plan 如果你不覆盖read-column-by-label(不是name),调用可能会中断。
    • 谢谢。我想可能是这样。我用问题中的示例代码尝试了 (r/foldcat (jdbc/plan datasource ["select * from users;"]))) 并且它有效。我会继续挖掘,但如果我无法弄清楚,你能否扩展它在什么情况下会崩溃?
    • 很高兴看到您在此处更新了答案以反映最新信息——谢谢!
    猜你喜欢
    • 2019-05-16
    • 2019-02-10
    • 2010-10-25
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 1970-01-01
    相关资源
    最近更新 更多