     query-object))
 
 
+
+;;; ----------------------------------------------------------------------------
+;;; user functions and macros
+;;; ----------------------------------------------------------------------------
+
+(defn retrieve [entity-record-type key-value &
+                {:keys [parent kind]
+                 :or {kind (unqualified-name (.getName entity-record-type))}}]
+  (let [key-object (if parent
+                       (KeyFactory/createKey (get-key-object parent)
+                                             kind
+                                             (coerce-key-value-type key-value))
+                       (KeyFactory/createKey kind
+                                             (coerce-key-value-type key-value)))
+        entity (.get (get-datastore-service) key-object)
+        raw-properties (into {} (.getProperties entity))
+        properties (reduce (fn [m [k v]]
+                             (assoc m
+                               (keyword k)
+                               (coerce-java-type v)))
+                           {}
+                           raw-properties)
+        ;; XXX: No good choice but to use eval here. No way to know the number
+        ;; of arguments to the record constructor at compile-time, and no clean
+        ;; way to access any custom constructor defined by defentity, since that
+        ;; constructor would be in a different namespace.
+        entity-record (eval `(new ~entity-record-type ~@(repeat (count raw-properties) nil)))]
+    (with-meta (merge entity-record properties) {:key (.getKey entity)})))
+
+
+(defn delete! [target]
+  (let [target (if (sequential? target) target [target])
+        key (coerce-to-key-seq target)]
+    (.delete (get-datastore-service) key)))
+
+
+(defmacro defentity [name properties &
+                     {:keys [kind]
+                      :or {kind (unqualified-name name)}}]
+  (let [key-property-name (first (filter #(= (:tag (meta %)) :key) properties))
+        key-property (if key-property-name (keyword (str key-property-name)) nil)]
+    `(defrecord ~name ~properties
+       EntityProtocol
+       (get-key-object [this#]
+         (get-key-object-helper this# ~key-property ~kind))
+       (get-key-object [this# parent#]
+         (get-key-object-helper this# ~key-property ~kind :parent parent#))
+       (get-entity-object [this#]
+         (get-entity-object-helper this# ~kind))
+       (save! [this#]
+         (save!-helper this#)))))
+
+
+(extend-type Iterable
+  EntityProtocol
+  (save! [this] (save-many-helper! this)))
+
+
+(defmacro new* [entity-record-type property-values & {:keys [parent]}]
+  (if parent
+      `(let [parent# ~parent
+             entity# (new ~entity-record-type ~@property-values)]
+         (if (nil? parent#)
+             entity#
+             (with-meta entity# {:key (get-key-object entity# parent#)})))))
+
+
+;;; Note that the code relies on the API's implicit transaction tracking
+;;; wherever possible, but the *current-transaction* value is still used for
+;;; query construction.
+(defmacro with-transaction [& body]
+  `(binding [*current-transaction* (.beginTransaction (get-datastore-service))]
+     (try
+       (let [body-result# (do ~@body)]
+         (.commit *current-transaction*)
+         body-result#)
+       (catch Throwable err#
+         (do (.rollback *current-transaction*)
+             (throw err#))))))
+
+
 (defn query [& {:keys [kind ancestor filter sort keys-only?
                        count-only? in-transaction?
                        limit offset

            Query Query$FilterOperator Query$SortDirection]))


(defonce *datastore-service* (atom nil))
(defonce *current-transaction* nil)


  (save! [this]
    "Writes the given entity to the data store."))



;;; ----------------------------------------------------------------------------
;;; helper functions; do not use these directly
;;; ----------------------------------------------------------------------------

(defn- unqualified-name [sym]
  (let [s (str sym)
