2
2
3
3
[ ![ Build Status] ( https://travis-ci.org/fukamachi/mito.svg?branch=master )] ( https://travis-ci.org/fukamachi/mito )
4
4
[ ![ Coverage Status] ( https://coveralls.io/repos/fukamachi/mito/badge.svg?branch=master&service=github )] ( https://coveralls.io/github/fukamachi/mito?branch=master )
5
-
6
- Work in progress.
5
+ [ ![ Quicklisp dist] ( http://quickdocs.org/badge/mito.svg )] ( http://quickdocs.org/mito/ )
7
6
8
7
Mito is yet another object relational mapper and it aims to be a successor of [ Integral] ( https://github.com/fukamachi/integral ) .
9
8
10
- * Support PostgreSQL
11
- * Better abstraction for RDBMS
12
- * Better code structure
9
+ * Supports MySQL, PostgreSQL and SQLite3
10
+ * Migrations
11
+ * DB schema versioning
12
+
13
+ ## Warning
14
+
15
+ This software is still ALPHA quality. The APIs will be likely to change.
13
16
14
17
## Usage
15
18
16
19
``` common-lisp
20
+ (mito:connect-toplevel :mysql :database-name "myapp" :username "fukamachi" :password "c0mon-1isp")
21
+ ;=> #<DBD.MYSQL:<DBD-MYSQL-CONNECTION> {100691BFF3}>
22
+
17
23
(defclass user ()
18
24
((name :col-type (:varchar 64)
19
25
:initarg :name
20
26
:accessor user-name)
21
27
(email :col-type (or (:varchar 128) :null)
22
28
:initarg :email
23
29
:accessor user-email))
24
- (:metaclass dao-table-class))
30
+ (:metaclass mito:dao-table-class))
31
+ ;=> #<MITO.DAO.TABLE:DAO-TABLE-CLASS COMMON-LISP-USER::USER>
32
+
33
+ (mito:table-definition 'user)
34
+ ;=> #<SXQL-STATEMENT: CREATE TABLE user (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) NOT NULL, email VARCHAR(128))>
25
35
26
36
(defclass tweet ()
27
37
((status :col-type :text
@@ -30,9 +40,172 @@ Mito is yet another object relational mapper and it aims to be a successor of [I
30
40
(user :col-type user
31
41
:initarg :user
32
42
:accessor tweet-user))
33
- (:metaclass dao-table-class))
43
+ (:metaclass mito:dao-table-class))
44
+ ;=> #<MITO.DAO.TABLE:DAO-TABLE-CLASS COMMON-LISP-USER::TWEET>
45
+
46
+ (mito:table-definition 'tweet)
47
+ ;=> #<SXQL-STATEMENT: CREATE TABLE tweet (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, status TEXT NOT NULL, user_id BIGINT UNSIGNED NOT NULL)>
48
+ ```
49
+
50
+ ### Connecting to DB
51
+
52
+ Mito provides a function ` connect-toplevel ` and ` disconnect-toplevel ` to establish a connection to RDBMS.
53
+
54
+ ` connect-toplevel ` takes the same arguments as ` dbi:connect ` , typically the driver-type, the database name to connect, user name and password.
55
+
56
+ ``` common-lisp
57
+ (mito:connect-toplevel :mysql :database-name "myapp" :username "fukamachi" :password "c0mon-1isp")
58
+ ```
59
+
60
+ ` connect-toplevel ` sets ` *connection* ` the new connection and returns it.
61
+
62
+ If you want to use a connection lexically, just bind it:
63
+
64
+ ``` common-lisp
65
+ (let ((mito:*connection* (dbi:connect :sqlite3 :database-name #P"/tmp/myapp.db")))
66
+ (unwind-protect (progn ...)
67
+ ;; Ensure that the connection is closed.
68
+ (dbi:disconnect mito:*connection*)))
34
69
```
35
70
71
+ ### Class Definitions
72
+
73
+ In Mito, you can define a class which corresponds to a database table by specifying ` (:metaclass mito:dao-table-class) ` .
74
+
75
+ ``` common-lisp
76
+ (defclass user ()
77
+ ((name :col-type (:varchar 64)
78
+ :initarg :name
79
+ :accessor user-name)
80
+ (email :col-type (or (:varchar 128) :null)
81
+ :initarg :email
82
+ :accessor user-email))
83
+ (:metaclass mito:dao-table-class))
84
+ ```
85
+
86
+ The above defines a Common Lisp normal class except that it allows additional options.
87
+
88
+ ```
89
+ (defclass {class-name} ()
90
+ ({column-definition}*)
91
+ (:metaclass mito:dao-table-class)
92
+ [[class-option]])
93
+
94
+ column-definition ::= (slot-name [[column-option]])
95
+ column-option ::= {:col-type col-type} |
96
+ {:primary-key boolean} |
97
+ {:ghost boolean}
98
+ col-type ::= { keyword |
99
+ (keyword . args) |
100
+ (or keyword :null) |
101
+ (or :null keyword) }
102
+ class-option ::= {:primary-keys symbol*} |
103
+ {:unique-keys {symbol | (symbol*)}*} |
104
+ {:keys {symbol | (symbol*)}*} |
105
+ {:table-name table-name}
106
+ {:auto-pk boolean}
107
+ ```
108
+
109
+ Note the class automatically adds a primary key named ` id ` if there's no primary keys.
110
+
111
+ ``` common-lisp
112
+ (c2mop:class-direct-slots (find-class 'user))
113
+ ;=> (#<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS MITO.DAO.TABLE::%SYNCED>
114
+ ; #<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS MITO.DAO.TABLE::ID>
115
+ ; #<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS COMMON-LISP-USER::NAME>
116
+ ; #<MITO.DAO.COLUMN:DAO-TABLE-COLUMN-CLASS COMMON-LISP-USER::EMAIL>)
117
+ ```
118
+
119
+ To prevent this behavior, add ` :auto-pk nil ` to the class options.
120
+
121
+ The class inherits ` mito:dao-class ` implicitly.
122
+
123
+ ``` common-lisp
124
+ (find-class 'user)
125
+ ;=> #<MITO.DAO.TABLE:DAO-TABLE-CLASS COMMON-LISP-USER::USER>
126
+
127
+ (c2mop:class-direct-superclasses *)
128
+ ;=> (#<STANDARD-CLASS MITO.DAO.TABLE:DAO-CLASS>)
129
+ ```
130
+
131
+ This may be useful when you define methods which can be applied for all table classes.
132
+
133
+ ### Generating Table Definitions
134
+
135
+ ``` common-lisp
136
+ (mito:table-definition 'user)
137
+ ;=> #<SXQL-STATEMENT: CREATE TABLE user (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) NOT NULL, email VARCHAR(128))>
138
+
139
+ (sxql:yield *)
140
+ ;=> "CREATE TABLE user (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) NOT NULL, email VARCHAR(128))"
141
+ ; NIL
142
+ ```
143
+
144
+ ### Creating DB tables
145
+
146
+ ``` common-lisp
147
+ (mito:execute-sql (mito:table-definition 'user))
148
+
149
+ (mito:ensure-table-exists 'user)
150
+ ```
151
+
152
+ ### CRUD
153
+
154
+ ``` common-lisp
155
+ (defvar me
156
+ (make-instance 'user :name "Eitaro Fukamachi" :email "[email protected] "))
157
+ ;=> USER
158
+
159
+ (mito:insert-dao me)
160
+ ;-> ;; INSERT INTO `user` (`name`, `email`) VALUES (?, ?) ("Eitaro Fukamachi", "[email protected] ") [0 rows] | MITO.DAO:INSERT-DAO
161
+ ;=> #<USER {10053C4453}>
162
+
163
+ ;; Same as above
164
+ (mito:create-dao 'user :name "Eitaro Fukamachi" :email "[email protected] ")
165
+
166
+ ;; Getting the primary key value
167
+ (mito:object-id me)
168
+ ;=> 1
169
+
170
+ ;; Retrieving from the DB
171
+ (mito:find-dao 'user 1)
172
+ ;-> ;; SELECT * FROM `user` WHERE (`id` = ?) LIMIT 1 (1) [1 row] | MITO.DB:RETRIEVE-BY-SQL
173
+ ;=> #<USER {10077C6073}>
174
+
175
+ ;; Updating
176
+ (setf (slot-value me 'name) "nitro_idiot")
177
+ ;=> "nitro_idiot"
178
+
179
+ (mito:save-dao me)
180
+ ;-> ;; UPDATE `user` SET `id` = ?, `name` = ?, `email` = ? WHERE (`id` = ?) (1, "nitro_idiot", "[email protected] ", 2) [0 rows] | MITO.DB:EXECUTE-SQL
181
+
182
+ ;; Deleting
183
+ (mito:delete-dao me)
184
+ ;-> ;; DELETE FROM `user` WHERE (`id` = ?) (1) [0 rows] | MITO.DAO:DELETE-DAO
185
+ ```
186
+
187
+ ### Relation
188
+
189
+ ### Migrations
190
+
191
+ ## Installation
192
+
193
+ ```
194
+ $ mkdir -p ~/common-lisp
195
+ $ cd ~/common-lisp
196
+ $ git clone https://github.com/fukamachi/mito
197
+ $ ros -L ~/common-lisp/mito/mito.asd install mito
198
+ ```
199
+
200
+ ``` common-lisp
201
+ (ql:quickload :mito)
202
+ ```
203
+
204
+ ## See Also
205
+
206
+ * [ CL-DBI] ( https://github.com/fukamachi/cl-dbi )
207
+ * [ SxQL] ( https://github.com/fukamachi/sxql )
208
+
36
209
## Author
37
210
38
211
* Eitaro Fukamachi (
[email protected] )
0 commit comments