Let me answer your questions in the reverse order that you asked them
in:
First, you should be using auto generated id fields whenever you can.
They are fast, easy, and they stay out of the way. I even use them in
intersect tables. On a totally unrelated note: I recommend that you
use the Oracle naming convention, which is to give all auto increment
primary keys the same name; e.g., id. The DB2 convention (having a pk
named for the table and the id–e.g., docId or userId–strikes me as
just plain silly, since using this convention causes you to have to
constantly describe your tables while writing sql. (Although I prefer
DB2 as a database over Oracle as a database, I do prefer this one
Oracle convention–which is external to the actual databases.)
Second, whenever someone tells you that an object oriented language
can’t create non-implementation specific interfaces, either they are
wrong or the language isn’t really object oriented. Allowing for
non-implementation specific interfaces is what Object Oriented
languages do best (literally). So don’t listen to all the nay-sayers
who claim that you can’t write code that will get you across multiple
databases. Not only can it be done, but it is surprisingly easy.
There are several ways to do this from simple wrapper classes to whole
APIs. (I’m currently writing a whole API, which doesn’t bare
reproducing on a mailing list.) The easiest way that comes to my mind
is to use a simple wrapper with an abstract method (mind you, the
following is way, way paired down so that it fits in this email):
require “dbi”
DISCLAIMER: there may be some minor typos in this code,
since I’ve written it off the top of my head and have
not tried to run it. But it should give you the a
clear idea of what I’m getting at.
class Connection
def initialize(dsn, user=nil, auth=nil, parms=nil)
@dbh = connect(dsn, user, auth, parms)
end
def quote(str)
return @dbh.quote(str)
end
def execute(sql, *vals)
return @dbh.execute(sql, *vals)
end
def executeInsert(sql, *vals)
raise(RuntimeError, "Abstract method. Implement in subclass")
end
def do(sql, *vals)
return @dbh.do(sql, *vals)
end
def select_one(sql, *vals)
return @dbh.select_one(sql, *vals)
end
# insert other DatabaseHandle methods here so that you wrap up
# the full complement of DatabaseHandle functions
def disconnect()
@dbh.disconnect()
end
end
Now you can extend this class as follows:
for mysql
class Connection_mysql < Connection
def executeInsert(sql, *vals)
self.do(sql, *vals)
return self.select_one("SELECT last_insert_id()")[0]
end
end
for postgress
class Connection_pg < Connection
def executeInsert(sql, *vals)
self.do(sql, *vals)
return self.select_one("SELECT currval('table_idfield_seq')")[0]
end
end
You can implement other databases the same way. For example, in a
Connection_ora for Oracle, you’ll want to grab the value from a
sequencer and with a Connection_db2 for DB2 you’ll want to grab and
parse in the unique value.
Now you can either hard-code the type of Connection that you’ll be want
(e.g., Connection_pg or Connection_mysql) into your code, or you can
create an static method (might I suggest calling it “connect”) in your
Connection class that chooses which type of Connection object to return
based on the Driver being used (e.g., with a big case statement).
···
On Thursday, January 2, 2003, at 11:46 PM, Tom Sawyer wrote:
as it stands when i insert a new record into a table, i then do:
“SELECT
currval(‘table_idfield_seq’) as recid;” to get the latest automatically
generated record id. and thus know which record i just inserted. but
this
seems to me like a jerry-rig. i don’t like it and i doubt it works on
anything but postgresql. what’s the best way to deal with this? how
does one
get auto generated id fields in such a way as to be cross-compatible?
or am i
mistaken in using auto generated id feilds in the first place?
David King Landrith
(w) 617.227.4469x213
(h) 617.696.7133
One useless man is a disgrace, two
are called a law firm, and three or more
become a congress – John Adams
public key available upon request