简体   繁体   English

如何使用 next.jdbc 将 SQLite 中的 bool 列读入 bool Clojure 值? SQLite 将布尔值存储为 0/1

[英]How do I read bool columns from SQLite into bool Clojure values with next.jdbc? SQLite stores booleans as 0/1

Here's the smallest bit of code to see the problem.这是查看问题的最小代码。

  (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}]

I'd like is_active on that last line to be true or false .我希望最后一行的is_activetruefalse I don't want my application code to have to keep track of which columns are bool and do conversion after I get a result set.我不希望我的应用程序代码必须跟踪哪些列是 bool 并在获得结果集后进行转换。

next.jdbc gives you several ways to plug into how it creates Clojure objects from database values. next.jdbc为您提供了几种方法来插入它如何从数据库值创建 Clojure 对象。 The most general of these is by passing a :builder-fn option with a value created from builder-adapter and passing your own (or an existing) builder and a custom "read-column-by-index"其中最通用的是通过: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}]

NOTE: This won't always work if your using plan .注意:如果您使用plan ,这并不总是有效。 For performance, plan doesn't realize values.对于绩效, plan不能实现价值。 In your reduction of a plan, accessing values of the result set will skip over the custom read-column-by-index stuff above.在您减少计划时,访问结果集的值将跳过上面自定义的按索引读取列的内容。 It's up to you to do the work in your reduction.减少工作由您决定。

More info, from the author of next.jdbc , can be found at https://github.com/seancorfield/next-jdbc/blob/develop/doc/tips-and-tricks.md#sqlite and https://github.com/seancorfield/next-jdbc/issues/134 More info, from the author of next.jdbc , can be found at https://github.com/seancorfield/next-jdbc/blob/develop/doc/tips-and-tricks.md#sqlite and https://github .com/seancorfield/next-jdbc/issues/134


The rest of this answer is lagniappe.这个答案的 rest 是 lagniappe。 It's the "almost-there" and wrong turns that I made along the way.这是我一路走来的“几乎就在那里”和错误的转弯。 I'm leaving it as part of the answer for whatever value that may provide.对于可能提供的任何价值,我将其作为答案的一部分。

next.jdbc lets you extend the ReadableColumn protocol with custom functionality for converting values. next.jdbc允许您使用自定义功能扩展ReadableColumn协议以转换值。

https://github.com/seancorfield/next-jdbc/blob/develop/doc/result-set-builders.md#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)))

It might not be perfect.它可能并不完美。 There's another function, read-column-by-label , that doesn't receive the ResultSetMetaData object that we can call getColumnTypeName on to test if the column type is a bool.还有另一个 function, read-column-by-label ,它不接收ResultSetMetaData object,我们可以调用getColumnTypeName来测试列类型是否为布尔值。 read-column-by-label only receives the value and the column name. read-column-by-label只接收值和列名。 I'm not sure what the consequences of not overriding that function.我不确定不覆盖 function 的后果是什么。 Things appear to work with just read-column-by-index overridden.事情似乎只适用于read-column-by-index

Editing to make note of how this isn't a complete fix.编辑以说明这不是一个完整的修复。

plan , for example, returns a reducible.例如, plan返回一个可约数。 The reduce function gets passed a "mapified" result set.减少 function 获得了“映射”结果集。 Some of the implementations of some of the interfaces call read-column-by-label .一些接口的一些实现调用read-column-by-label I think it does this for performance reasons?我认为这样做是出于性能原因? So you can do something like select-keys without "building" the entire map.因此,您可以在不“构建”整个 map 的情况下执行select-keys之类的操作。 So if you want to do something like below with the reducer, then the above fix isn't sufficient.所以如果你想用减速器做下面的事情,那么上面的修复是不够的。

  (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}]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM