Skip to content

llnek/hoard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hoard

Build Status

hoard is a light weight object-relationl mapping framework.

Installation

Add the following dependency to your project.clj file:

[io.czlab/hoard "2.2.0"]

Documentation

Supported Databases

Currently supports

  • Postgresql
  • SQL Server
  • Oracle
  • H2

Data Types

  • :Timestamp
  • :Calendar
  • :Date
  • :Boolean
  • :Password
  • :String
  • :Double
  • :Long
  • :Int
  • :Float
  • :Bytes

Modeling

Defining a model in hoard is very similar to defining a SQL table, with the additional capability of defining relations. Each model must have an unique identifier, follow by a set of field definitions, indexes and associations. All models have a built-in primary key defined to be a database autoincrement field of type long.

For example, to model a address object:

(ns demo.app
  (:require [czlab.hoard.core :as hc]))

  (dbmodel<> ::Address
    (dbfields
      {:addr1 {:size 200 :null? false}
       :addr2 {:size 64}
       :city {:null? false}
       :state {:null? false}
       :zip {:null? false}
       :country {:null? false}})
    (dbindexes
      {:i1 #{:city :state :country }
       :i2 #{:zip :country }
       :i3 #{:state }
       :i4 #{:zip } }))

The convention is to define models as a group. To do that, use the macro dbschema<>.

(ns demo.app
  (:require [czlab.hoard.core]))

  (dbschema<>
    (dbmodel<> ::Address
      (dbfields
        {:addr1 {:size 200 :null? false}
         :addr2 {:size 64}
         :city {:null? false}
         :state {:null? false}
         :zip {:null? false}
         :country {:null? false}})
      (dbindexes
        {:i1 #{:city :state :country }
         :i2 #{:zip :country }
         :i3 #{:state }
         :i4 #{:zip } }))
    (dbmodel<> ::Person
      (dbfields
        {:first_name {:null? false }
         :last_name {:null? false }
         :iq {:domain :Int}
         :bday {:domain :Calendar :null? true}
         :sex {:null? true} })
      (dbindexes
        {:i1 #{ :first_name :last_name }
         :i2 #{ :bday } })
      (dbassocs
        ;a person can have [0..n] Address objects
        ;that is, a one-to-many relation with Address
        {:spouse {:kind :o2o :other ::Person }
         :addrs {:kind :o2m :other ::Address :cascade? true} })))

Connections

Connections to the underlying database can be done in 2 ways

  • pooled connections
  • raw connections

(dbio<+> jdbc-info schema) or (dbio<> jdbc-info schema)

SQL DDL

To extract the ddl from your schema, use (get-ddl db-flavor).
For example, (get-ddl :h2) will return the DDL as String.

To upload the ddl to the underlying database, use (upload-ddl ...)

Basic CRUD operations

(def schemaObj (dbschema<> ...))
(def db (dbio<> ... schemaObj))

(let [person-model (find-model schemaObj ::Person)

      ;create a person object
      joe (-> (dbpojo<> person-model)
              (db-set-flds*
                :first_name "Joe" :last_name  "Blogg" :age 21))

      ;insert into db returning a fresh object
      ;joe2 is the persisted version, has primary field set
      joe2 (-> (db-simple db) (add-obj joe))

      ;update joe
      joe3 (db-set-fld joe2 :sex "male")
      joe4 (-> (db-simple db) (mod-obj joe3))

      ;query for joe
      joe5 (-> (db-simple db)
               (find-one ::Person {:first_name "Joe" :last_name "Blogg"}))
      ;got joe from db
      ;delete joe
      _ (-> (db-simple db) (del-obj joe5))]
  (println "no more joe"))

Relations

hoard supports the 3 classic relations:

  • many to many
    • e.g. a teacher can have many students and a student can have many teachers.
  • one to many
    • e.g. a person can own many cars.
  • one to one
    • e.g. a person can have one spouse.

Many to Many

(def schemaObj (dbschema<> (dbmodel<> ::Teacher ...)
                           (dbmodel<> ::Student ...)
                           (dbjoined<> ::t-and-s ::Teacher ::Student)))
(def db (dbio<> ... schemaObj))

  ;let's assume we have some teachers T1, T2, T3 and
  ;students S7, S8, S9 in the database

  (let [sql (db-composite db)
        sim (db-simple db)]

    (transact! sql 
               (fn [tx]
                 (db-set-m2m {:joined ::t-and-s :with tx} T1 S7)
                 (db-set-m2m {:joined ::t-and-s :with tx} S8 T1)
                 (db-set-m2m {:joined ::t-and-s :with tx} S8 T2)))

    ;let's check the db
    ;returns 2 teachers (T1, T2)
    (db-get-m2m {:joined ::t-and-s :with sim} S8)
    ;returns 2 students (S7, S8)
    (db-get-m2m {:joined ::t-and-s :with sim} T1)

    ;school closes, clear all
    (transact! sql 
               (fn [tx]
                 (db-clr-m2m {:joined ::t-and-s :with tx} T1)
                 (db-clr-m2m {:joined ::t-and-s :with tx} S8)))

One to Many

(def schemaObj (dbschema<> ...))
(def db (dbio<> ... schemaObj))

  ;following previous examples,
  ;we have joe in the database

  (let [sql (db-composite db)]

    (transact! sql 
               (fn [tx]
                 (db-set-o2m {:as :addrs :with tx}
                             joe
                             (add-obj tx (create-a-address-object)))
                 (db-set-o2m {:as :addrs :with tx}
                             joe
                             (add-obj tx (create-another-address-object)))))

    ;let's check the db
    ;returns all the addresses
    (db-get-o2m {:as :addrs :with sql} joe)

    ;joe moves and clears all addresses
    (transact! sql 
               (fn [tx]
                 (db-clr-o2m {:as :addrs :with tx} joe)))

One to One

(def schemaObj (dbschema<> ...))
(def db (dbio<> ... schemaObj))

  ;following previous examples,
  ;we have joe and another person mary in the db
  (let [sql (db-composite db)]

    ;joe and mary gets married
    (transact! sql 
               (fn [tx]
                 (db-set-o2o {:as :spouse :with tx} joe mary)
                 (db-set-o2o {:as :spouse :with tx} mary joe)))

    ;let's check the db
    ;returns joe
    (db-get-o2o {:as :spouse :with sql} mary)
    ;return mary
    (db-get-o2o {:as :spouse :with sql} joe)

    ;joe and mary separates
    (transact! sql 
               (fn [tx]
                 (db-clr-o2o {:as :spouse :with tx} joe)
                 (db-clr-o2o {:as :spouse :with tx} mary)))

Contacting me / contributions

Please use the project's GitHub issues page for all questions, ideas, etc. Pull requests welcome. See the project's GitHub contributors page for a list of contributors.

License

Copyright © 2013-2024 Kenneth Leung

Distributed under the Apache License either version 2.0 or (at your option) any later version.

About

sql orm for clojure

Resources

License

Stars

Watchers

Forks

Packages

No packages published