Primarily technical blog on Lisp, .NET, C# development.

Thursday, April 29, 2010

How to make Postmodern ignore incomplete mapped DAO

Yesterday I was working with a database using Postmodern (http://marijn.haverbeke.nl/postmodern/) and I ran into an issue trying to adding a column to one of my databases. 

According to its current implementation, postmodern raises an error if the DAO and the table scheme are not matching. In order words, the DAO class must have all fields from the table it maps to. 

In a real world environment, a web application running live for example, it might not be acceptable to bring the application down just because a new column was added to the table it uses. 

Sometimes, depending on the database design, a table can contain information from several DAO's, specially if they have a parent-child object relationship. 

In order to remove this validation from postmodern allowing you to have DAO's mapping to a subset of the fields of a table all it is needed is a little patch on the postmodern table.lisp file on the function as follows:

(defun dao-row-reader (class)
  "Defines a row-reader for objects of a given class."
  (row-reader (query-fields)
    (let ((column-map (append *custom-column-writers* (dao-column-map class))))
      (loop :while (next-row)
            :collect (let ((instance (allocate-instance class)))
                       (loop :for field :across query-fields
                             :for writer := (cdr (assoc (field-name field) column-map :test #'string=))
                             :do (etypecase writer
                                   (null (next-field field))
                                   (symbol (setf (slot-value instance writer) (next-field field)))
                                   (function (funcall writer instance (next-field field)))))
                       (initialize-instance instance)
                       instance)))))

Compare with the current version code where if the field is not found it raises an error:

(defun dao-row-reader (class)
  "Defines a row-reader for objects of a given class."
  (row-reader (query-fields)
    (let ((column-map (append *custom-column-writers* (dao-column-map class))))
      (loop :while (next-row)
            :collect (let ((instance (allocate-instance class)))
                       (loop :for field :across query-fields
                             :for writer := (cdr (assoc (field-name field) column-map :test #'string=))
                             :do (etypecase writer
                                   (null (error "No slot named ~a in class ~a. DAO out of sync with table, or incorrect query used."
                                                (field-name field) (class-name class)))
                                   (symbol (setf (slot-value instance writer) (next-field field)))
                                   (function (funcall writer instance (next-field field)))))
                       (initialize-instance instance)
                       instance)))))

Friday, April 9, 2010

Save Lisp and Die

If you are running SBCL and are using threads, this is the way I found out that works for me to stop the threads before saving the core.

(dolist (thread (sb-thread:list-all-threads))
    (unless (eq thread sb-thread:*current-thread*)
      (sb-thread:terminate-thread thread)))
 
then you can run...

(sb-ext:save-lisp-and-die "core-file")
 
I am using SBCL 1.0.36 64bit on a Linux Ubuntu 9.10 64bit.