Assuming you want to keep this in the app layer I would advice to use a model layer with properties per field to do the logging. All data access then goes trough this data model, giving you a hook to add functionality.
An activerecord based example (VBScript):
class cSomeEntity
public db ' link to a db wrapper
private id, dirty, loaded ' varous flags
private sub class_initialize
dirty = false
loaded = false
end sub
private sub class_terminate
if dirty then
db.execute("update some_table set some_field=? where id=?", array(p_some_field, id))
end if
end sub
public sub load_by_id(value)
dim rs
set rs = db.fetch_rs("select id, some_field from some_table where id=?", array(id))
id = rs("id")
p_some_field = rs("some_field")
loaded = true
end sub
private p_some_field
public property get some_field
some_field = p_some_field
end property
public property let some_field(value)
if not loaded then err.raise 1, , "Entity not yet initialized, call .load_by_id() first!"
if value <> p_some_field then
dirty = true
make_log_entry("some_value", p_some_field, value)
p_some_field = value
end if
end property
private sub make_log_entry(field, old_value, new_value)
db.execute("insert into audit_log (table, field, old_value, new_value) values (?, ?, ?, ?)", _
array("some_table", field, old_value, new_value))
end sub
end class
It might seem a bit bloated, but it is more flexible than a trigger based approach. For example you can easily implement range checks and such.
Secondly, when you need to write multiple entity classes you can push a lot of functionality in to a delegate class and use code template to write the property getter & setters.