clojure - 将 Clojure 的数据结构与 MapDB 结合使用

标签 clojure mapdb

我尝试直接将 Clojure 的 hashmap 与 MapDB 一起使用,但遇到了奇怪的行为。我检查了 Clojure 和 MapDB 源代码,但无法理解这个问题。

首先一切看起来都很好:

lein try org.mapdb/mapdb "1.0.6"

; defining a db for the first time
(import [org.mapdb DB DBMaker])
(defonce db (-> (DBMaker/newFileDB (java.io.File. "/tmp/mapdb"))
                .closeOnJvmShutdown
                .compressionEnable
                .make))
(defonce fruits (.getTreeMap db "fruits-store"))
(do (.put fruits :banana {:qty 2}) (.commit db))

(get fruits :banana)
=> {:qty 2}
(:qty (get fruits :banana))
=> 2
(first (keys (get fruits :banana)))
=> :qty
(= :qty (first (keys (get fruits :banana))))
=> true

CTRL-D
=> Bye for now!

然后我尝试再次访问数据:

lein try org.mapdb/mapdb "1.0.6"

; loading previsously created db
(import [org.mapdb DB DBMaker])
(defonce db (-> (DBMaker/newFileDB (java.io.File. "/tmp/mapdb"))
                .closeOnJvmShutdown
                .compressionEnable
                .make))
(defonce fruits (.getTreeMap db "fruits-store"))

(get fruits :banana)
=> {:qty 2}
(:qty (get fruits :banana))
=> nil
(first (keys (get fruits :banana)))
=> :qty
(= :qty (first (keys (get fruits :banana))))
=> false
(class (first (keys (get fruits :banana))))
=> clojure.lang.Keyword

为什么相同的关键字对于 = 会有所不同? 是否发生了一些奇怪的引用问题?

最佳答案

该问题是由关键字相等的工作方式引起的。看着 实现= function我们看到,由于关键字不是 clojure.lang.Numberclojure.lang.IPercientCollection 它们的相等性是 根据 Object.equals 方法确定。浏览source of clojure.lang.Keyword我们了解到关键字不会覆盖 Object.equals 因此两个关键字相等,当且仅当它们相同 对象

MapDB的默认序列化器是org.mapdb.SerializerPojo,它是 org.mapdb.SerializerBase。在 its documentation我们可以读到 这是一个

Serializer which uses ‘header byte’ to serialize/deserialize most of classes from ‘java.lang’ and ‘java.util’ packages.

不幸的是,它不能很好地与 clojure.lang 类一起使用;事实并非如此 保留关键字的同一性,从而打破平等。 为了修复它,让我们尝试编写自己的 serializer使用 EDN format —或者,您可以考虑,例如,Nippy ——并使用 它在我们的 MapDB 中。

(require '[clojure.edn :as edn])

(deftype EDNSeralizer []
  ;; See docs of org.mapdb.Serializer for semantics.
  org.mapdb.Serializer
  (fixedSize [_]
    -1)
  (serialize [_ out obj]
    (.writeUTF out (pr-str obj)))
  (deserialize [_ in available]
    (edn/read-string (.readUTF in)))
  ;; MapDB expects serializers to be serializable.
  java.io.Serializable)

(def edn-serializer (EDNSeralizer.))

(import [org.mapdb DB DBMaker])
(def db (.. (DBMaker/newFileDB (java.io.File. "/tmp/mapdb"))
            closeOnJvmShutdown
            compressionEnable
            make))

(def more-fruits (.. db
                     (createTreeMap "more-fruits")
                     (valueSerializer (EDNSeralizer.))
                     (makeOrGet)))
(.put more-fruits :banana {:qty 2})
(.commit db)

在定义了 EDNSeralizer 的 JVM 中重新打开 more-fruits TreeMap 后 存储在其中的 :qty 对象将与任何其他 :qty 对象相同 实例。因此,相等性检查将正常工作。

关于clojure - 将 Clojure 的数据结构与 MapDB 结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28719945/

相关文章:

json - 在 clojure 中使用嵌套映射/向量结构的惯用且简洁的方式

database - MapDB 是线程安全的吗?

java - MapDB实现BTreeMap时如何使用byte数组作为key

java - MapDB在创建CircularQueue时不接受Serializer

Clojure - 返回闭包或部分应用函数更惯用?

recursion - Clojure,对向量列表求和,沿途记录位置

clojure - 在结果元组中检索实体 ID 时,Datomic 未返回正确的 "min"结果

sql - 为什么有人要使用 JDBC 而不是 korma 之类的库?

java - MapDB批量导入性能

Java:使用mapDB,在数据库中写入会覆盖以前的条目