views:

794

answers:

8

I have a Java program that is agnostic from the database and I need to know, while inserting, if an SQLException was thrown because of a duplicate key.

If I was using a single database driver I would simply use the ErrorCode, but since I can be using very different engines the ErrorCode are not the same.

Has anyone done this before? Any ideas?

Many TIA!

Edit: I have a configuration file where I store the driver class (ie: org.apache.derby.jdbc.ClientDriver) and some other needed information (ie: username, password, url...). The connection is always passed as a "java.SQL.Connection" so I don't really care what drivers is being used.

+1  A: 

I believe a simple and reliable way is to check if the key exists prior to doing the insert. As you have rightly pointed out, each database has it's own way of reporting the error.

wentbackward
It depends on how the data is being accessed in code. This isn't clear from the question, so I definitely wouldn't say this is the "only" way.
Brad Barker
I can't see how data being accessed in code will affect a duplicate key on insert. However I cannot assert that this is the only solution. My answer has been amended.
wentbackward
+1  A: 

Well, if you can't rely on the exception to tell you why it was thrown, you could test by following the exception with a "select count(*) from table where key = @keyfailedtoinsert;"

Unfortunately, the exception isn't guaranteed to give you the table name and key name. In some cases, the java code that called called the JDBC driver may never have had them, e.g., if the insert happened wihin a stored procedure, or as in a trigger.

So you're back to having to trust each JDBC driver's vendor.

tpdi
+2  A: 

You could "train" the program on startup (or config) by inserting a known duplicate key, and recording the thrown error code.

Mark Brackett
This makes the assumption that the error returned is indeed the duplicate key on insert. You have no way to prove the error was in fact the one you expected.
wentbackward
@wentbackward - You can either take the 90% case, and assume it's right after doing connect, select, test, select - and/or just ask the user to read the error message and confirm. It's not an exact science - but it's very likely close enough.
Mark Brackett
A: 

Am I missing something? If you're using JDBC you should get back a duplicate key exception, regardless of the DB being used.

Or did you ask how you would determine a dupkey BEFORE you tried teh insert?

Lou
A: 

I'm assuming you aren't using JDBC or this would be a very simple error lookup.

Do you have a different set of classes for accessing the different databases? If so, you could catch the exception in the database specific classes and throw your own exception type that is shared among all the database types.

Brad Barker
+2  A: 

I think the ideal solution would be to have the data layer throw a specific exception in this case, perhaps a subclass of SQLException for DuplicateKeyException or something similar.

If you want to be able to treat different exceptions differently, then you have to throw different exception types (or sub-types) to begin with.

I think this is an area where the Spring Framework gets things really right: they provide a very rich hierarchy of "database exceptions" all of which extend DataAccessException , with sub-trees of types for "recoverable exceptions", "transient exceptions", "data integrity exceptions", etc etc. This leaves your client code free to catch any (or none) of the exception types which it can handle or care about: exceptions that indicate an error that may not be repeatable if you re-run the transaction, a fatal non-recoverable error, or you can simply catch the root type.

matt b
+3  A: 

With basic JDBC, there really isn't a way to do what you are saying in a cross-database manner. As you mentioned getErrorCode could be used, but required vendor-specific error codes.

The only three ways I see to get around this is:

  1. Use some sort of framework that does all of the translating from error code to meaningful exceptions (Hibernate would probably do this, someone else mentioned that Spring does)
  2. Check for the duplicate manually (with a select) prior to doing your insert. (This wouldn't be 100%, as its technically possible that someone could have done an insert after your query).
  3. After you get any sql exception on the insert, try to query for that id. If you can actually find the match - you can be fairly sure that the error you received was due to a duplicate primary key. (Although its possible that there was multiple problems, and that wasn't actually the one that was thrown).

My recommendation would be to write your code to avoid the problem as much as possible, and then (if absolutely necessary), use #3.

kenj0418
+2  A: 

Hi ferro. This is exactly what SQLException.getSQLState() is for. Acoording to Google, "23000" indicates a unique constraint violation in at least MySQL, PostgreSQL, and Oracle.

Matt Solnit
For instance on DB2 it's not the same
ferro
Hrm, you're right. How many databases do you need to support? Is it really an infinite number? You may be better off figuring out what the SQLState/ErrorCode pair is for each DB, and handling them all. It will still perform much, much better than doing a SELECT to check for the row ahead of time.
Matt Solnit
I'm going to create a method that will receive the DB connection, to check which driver is being used, and the exception, then I'll compare the getSQLState() and check if it a duplicate key.
ferro
In that case, you should probably take matt b's suggestion and use the Spring Framework JDBC translation layer. At the very least, you can re-use their list of error codes. See http://fisheye1.atlassian.com/browse/springframework/spring/src/org/springframework/jdbc/support/sql-error-codes.xml?r=MAIN
Matt Solnit