# mito-transactions A set of MOP magic adding several features to [mito](https://github.com/fukamachi/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](https://github.com/hanshuebner/bknr-datastore) and [mito](https://github.com/fukamachi/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`) ```cl (make-instance 'person :name "someseven") ;=> # (find-dao 'person 'name "someseven") ;=> # ``` 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. ```cl (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.