Approaching Clojure-Hibernate integration using hibernate.default_entity_mode = dynamic-map (Feedback Request)
by Shantanu Kumar
Hi,
(I am cross-posting this on Clojure and Hibernate-users mailing list.)
DISCLAIMER: I am a Clojure newbie - please let me know if you find any of my
assumptions / statements to be incorrect.
Hibernate has an experimental support for working with maps rather than
POJOs using the following configuration property:
hibernate.default_entity_mode=dynamic-map
I have done few simple successful tests using the above-mentioned technique,
using Clojure/Hibernate/XML-hbm-files. The stack I tested these with is
below:
Windows XP 32-bit SP3
Sun JDK 1.6
Clojure 1.0
Hibernate 3.2
PostgreSQL 8.3
JDBC 3 driver
I am wondering if I can take this idea to a larger implementation scale
using Clojure. What I intend to achieve is described below in detail. The
intention of this post is to communicate the idea and gather feedback -
please provide your input, feedback, suggestions and warnings.
- I am trying to visualize a dual layered easy-ORM for Clojure using a
subset of Hibernate. The first layer "Hibernate integration primitives" will
provide low level access to Hibernate functionaly using Clojure and another
layer "Easy ORM semantics" will build on the top of the first layer and will
provide Django / ActiveRecord like ease of use.
- The domain model will be expressed using Clojure syntax (DSL). As a
pre-compile step, a utility function will process the domain model to
automatically generate Hibernate XML-mapping files. Those XML-mapping files
can be used to carry out hbm2ddl (optional) activities. The domain model
will also be rich enough to capture metadata for subsequent Hibernate
operations.
.-----> Auto-generate Hibernate XML as a precompile step
/
/
Clojure ORM Domain Model
\
\
*-----> Keep model's metadata in Clojure
(so that they can be applied in subsequent
operations)
- The captured domain model will retain information about
entity-identification, property names, associations etc. A fictitious sample
(only for example, not necessarily correct syntax) is given below:
; define model (HBM = Hibernate Mapping)
;
; extra (optional) attributes appear in the map
;
(defmodel :blogentry ; assumed
:entity-name=:blogentry, :table=(upper-case :blogentry)
(hbm-id :id :long {:column "AUTO_ID" :generator "sequence"
:sequence-name "blogentry_auto_id_seq"}) ; assumed :name=:id, :type=:long,
:column="AUTO_ID"
(hbm-property :title :string {:width 100 }) ; assumed
:name=:title, :type=:string, :column=(upper-case :title)
(hbm-property :content :string {:width 3000}) ; assumed
:name=:content, :type=:string, :column=(upper-case :content)
(hbm-property :when-posted :timestamp ) ; assumed
:name=:when-posted, :type=:timestamp, :column=(upper-case
(dash-to-underscore :when-posted))
(hbm-property :is-deleted :boolean ) ; assumed
:name=:is-deleted, :type=:boolean, :column=(upper-case
(dash-to-underscore :is-deleted))
(hbm-property :sample :integer {:column "SAMPLE_CHAPTER" }) ;
assumed :name=:sample, :type=:integer, :column="SAMPLE_CHAPTER")
; associations
(hbm-many-to-one :blog {:column "BLOG_ID" :entity :blog-entity})
(hbm-set-one-to-many :usercomments {:key-column "AUTO_ID", :entity
:usercomments-entity} ))
(defn create-blog-instance []
(model-instance :blogentry
{ :title "Nandi Hills", :content "Fill here", :when-posted
(time-now), :is-deleted false } ))
; create instance and persist (HBO = Hibernate Operation)
;
(let [blogentry (create-blog-instance)]
(hbo-persist blogentry))
- There will be a Clojure DSL for persistence operations (that ultimately
translates into HQL or JPA QL). Some examples are below:
(hbo-persist blogentry-instance)
(hbo-findby-id blogentry 45)
(:blog (hbo-findby-id blogentry 45)) ;
returns the blog entity from many-to-one association
(hbo-findby-any blogentry (:where [:eq :title "Amarnath Trip"]))
(hbo-findby-any [blogentry :e usercomments :c] (:where [:eq [:e :id] [:c
:entry-id]]) ; Join
(hbo-findby-any usercomments (:where [:not-in (:select [:id] :from
[:spammers])])) ; Sub-select
(hbo-updateall blogentry :set [:title "New title" :is-deleted false])
(hbo-updateby-any blogentry :set [:title "New title" :is-deleted false]
(:where [:gt :when-posted (parse-date "2009-06-30")]) )
(hbo-deleteby-id blogentry 57)
- These are the simple design notes:
;- DSL Syntax for models should capture both Hibernate mapping and model
metadata
;
;- Remain close to Hibernate idioms, but do not inherit the XML-wrinkles
;
;- Important HBM attributes are mandatory parameters in the domain model
;
;- Rest of the (optional) attributes can appear as a map e.g. {:column
"CUSTOMER"}
;
;- Simplify in favour of succinctness (Sensible and auto-deduced
metadata defaults)
;
;- Overridable simplicity (Can override attributes by providing maps,
add children)
;
;- Not everything is covered - use hbm-any for direct Hibernate API
access (cook your own)
;
;- Use Clojure metadata support and (Cannot avoid) macros for model
definition
;
;- Use multimethods for XML auto-generation
;
;- Use macros and multimethods for persistence operations (render as HQL
/ JPA QL)
Regards,
Shantanu
15 years, 3 months