Commit message | Age | Author | Refs | |||
---|---|---|---|---|---|---|
r5:94e6ef46
|
|
0 | ||||
r4:f8b202e4
|
|
0 | ||||
r3:5048c883
|
|
0 | ||||
r2:8350841e
|
|
0 | ||||
r1:4df1cd4a
|
|
0 | ||||
r0:21cd5557
|
|
0 |
mito-transactions
A set of MOP magic adding several features to mito.
- Transactions with rollback and commit operating on both Lisp image and SQL db
- "Caching" objects so they keep their identity on subsequent fetches
Rationale
I needed a good object database for Common Lisp. bknr.datastore and mito immediately became the first candidates but I've met troubles with both.
Mito doesn't really behave like an object database, the most obvious sign of it is different identity for the same object (the same row) fetched from the DB. Also it doesn't support transactions and requires specifying column types for each slot but there's nothing to do about the latter in an ORM.
bknr.datastore is a prevalence database and there's always a risk that your data will grow faster than RAM. Also I've lost data during development more than once and its migration protocol is somewhat convoluted. But the biggest problem is that Common Lisp is currently the only language that can read bknr.datastore format.
So, the obvious thing to do is to take a library that gets more things right and implement the missing ones.
Cache
Just load mito-transactions
system and use/import/local-nickname mito-transactions
package instead of mito
.
Whenever an object is fetched from the database using any means (find-dao
, retrieve-dao
, select-dao
, select-by-sql
) the object will be checked against the cache and if an object with the same id is already present its slots would be updated with the new data from the db. Otherwise the object is placed in the cache. Objects are only flushed when they are GC'ed or deleted from the DB (using delete-dao
or delete-by-values
)
(make-instance 'person :name "someseven")
;=> #<PERSON 54 {10057EB383}>
(find-dao 'person 'name "someseven")
;=> #<PERSON 54 {10057EB383}>
They'll have the same identity unless GC runs between the two calls.
There are only two new functions:
GET-DAO CLASS ID
Retrieves an object by ID by first checking the cache and only accessing database if the object is not in the cache.
GET-DAO-ONLY-FROM-CACHE CLASS ID
Same, but never accesses the database. If the object is not cached it returns nil. Mostly useful for testing.
Transactions
The transaction system tries to keep in-image objects and database rows in sync. Ideally they should only be out-of-sync during a transaction. When a transaction is committed all changed objects are synced to the database. When a transaction is rolled back all objects get their pre-transaction slot values back. make-instance
automatically puts the object in both the database and the cache. You don't have to ever use mito:save-dao
, mito:insert-dao
, or mito:update-dao
. Modifying non-ghost slots outside of a transaction is forbidden.
(let ((person (make-person)))
(with-transaction ()
(incf (age person))
(if (check-something person)
(commit)
(rollback)))
(setf (name person) "foo")) ; ERROR
Transactions are also committed on normal exit and rolled back on non-local exit.
WITH-TRANSACTION () \&BODY BODY
Runs the body inside a transaction. Nested transactions are allowed but internal ones can't call commit
and will rollback all nested transactions on rollback
.
COMMIT and ROLLBACK
Functions to commit or rollback a transaction. Defined locally inside with-transaction
body. Either call terminates the transaction without evaluating its remainder.
License
Licensed under the LLGPL License.