在 MapDB 中使用 Clojure 的数据结构
Using Clojure's data structure with 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.Number
或 clojure.lang.IPersistentCollection
他们的平等是
根据 Object.equals
方法确定。略读 来源
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—alternatively, you could consider, say, 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
树图后
存储在其中的 :qty
对象将 与任何其他 :qty
对象相同
实例。因此,相等性检查将正常工作。
我尝试直接将 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.Number
或 clojure.lang.IPersistentCollection
他们的平等是
根据 Object.equals
方法确定。略读 来源
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—alternatively, you could consider, say, 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
树图后
存储在其中的 :qty
对象将 与任何其他 :qty
对象相同
实例。因此,相等性检查将正常工作。