By Peter Bell

Insert vs. Update: An Important Distinction

As part of my URIs and my internal method names, I'm trying to determine the best general approach (yeah, I know it depends, yada yada – my generator can't chat with users and my users are too cheap to pay for real programmers, so the generator needs generalized approaches that work well for vast majority of use cases) - providing explicit insert and update methods or providing a generalized save method which then determines internally which to do.

Currently I have general save() methods which then decide (usually at the DAO level) whether to insert or update a record based on object identity rules for that object. I liked this as it slightly simplified my API and it seemed easy enough for the DAO to use identity rules to figure out whether to insert or update. However, doing some more thinking I realized that this approach disregards important information . . .

The important information is the intent of the user, and usually their intent is very clear. Usually if a user fills out an “add” form, they want to insert a new record. If that record has the same identity as an existing record (say if product identity is defined by the Product SKU and the SKU entered already exists in the database), it shouldn’t just update that SKU, it should explicitly tell you that the add failed because there was already such an object. Equally, if a user fills out an edit form and the object is deleted while they are filling out the form, it should not simply add the record, but it should return with the error that the record being edited has been deleted, perhaps providing the option to “add” the record if they so wish.

Of course, some times you DO want the DAO to just figure it out. If you want to import a list of email addresses into a newsletter management system without duplication, you probably want to just save() them, with each record being inserted or updated based upon whether the object identity (in this case, probably the email address) is novel.

So, add(), edit() and save() methods all need to exist in the Service layer as public methods (and in the DAO as package/protected methods) if you want to be able to solve all of these problems.

Disclaimer: For a worthwhile subset of applications save() may not be necessary and it is possible to conceive of cases where save() would be an acceptable solution instead of add() and edit(), but for the general case, all three are required.

Comments
My DAO uses SQL-like method names.. e.g.:

insert();
update();
delete();
replace();
select();

seems to cover all your cases.. you'll end up with 3 different methods for 'saving' data, but the purpose is clear..

(also memcached for example uses a simular api)
# Posted By Evert | 11/15/06 10:34 PM
Thanks for the comment!

I'm not sure what exactly is the distinction between update() and replace()?

Also, I add save() which will either add() or edit() based upon whether or not the object identity is unique. Useful for importing a list of email addresses and de-duping them.
# Posted By Peter Bell | 11/15/06 10:40 PM
I use the following.

private methods:
_insert()
_read()
_update()
_delete()
_exists()

public methods:
init() - takes an id argument. calls _read() to create the obj
save() - uses exists() to see if the records exists in the database. If it does it calls _update(), if not calls _insert()
delete() - calls _delete()
validate() - used to do simply validation. return a list of the column names it caught errors on.

All my DAOs extend an abstract CFC the automagically creates all the getters, setters, and a getinstance() method.

Some people add a list() or selectall() method to their DAO so they can do filter queries or get all the records from the database. I don't like doing this as I believe a DAO should be used to access a single row in the table.
# Posted By Tony Petruzzi | 11/15/06 10:45 PM
Perhaps its a good idea to stick with the standards for these names.. (at least what people are used to)

REPLACE INTO (either inserts a new record or updates if the key already exists)
INSERT INTO (inserts a new record, throws error if key already exists)
UPDATE (updates a record, throws error if record does not exist)

so yea, in your example :

save = replace
add = insert
update = update

:) guess we're talking about the same thing
# Posted By Evert | 11/15/06 10:46 PM
Hi Tony,

I generally like the approach (especially the exists() method), but the single save() method could conceptually run into the problems I listed above. What happens if someone fills out an ADD form but the item already exists? You never get this problem with GUIDs or numeric IDs (unless something is messed up), but if you use real world keys, you can't just rely on exists() to decide whether to add or update.

@Evert - I didn't realize replace was a standard name, although it makes perfect sense as described. Re: insert and update those are sql specific names, so nothing wrong with using them, but I wouldn't say they are standards within the application space - I see more add and edit methods as the service layer and even the public interface of the DAO should be thinking and talking in object rather than relational terms and insert and update are a little persistence mechanism specific for my personal preferences. Agreed though - we're talking about the same thing!
# Posted By Peter Bell | 11/15/06 11:04 PM
@Peter,

As with anything, the style I'm using are what I run into 99.99% of the time. I can see where the exists method could cause problems. In that case you can either edit it directly to include the business logic you are talking about, or you could extend the DAO object and just overload this method if you didn't want to touch the original CFC.
# Posted By Tony Petruzzi | 11/15/06 11:21 PM
Hi Tony,

Makes perfect sense. FYI I use save() as well, and it was just thinking this use case through that showed me where it might become a problem as I start to use "real world keys". I always though just using save() was cleaner, and as you say, it's all down to the use case!
# Posted By Peter Bell | 11/15/06 11:28 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.