Distinguish basic SQLException types in Java? - java

Sometimes a command is expected to fail in certain situations.
Suppose I have pseudo-code
try {
// insert row
} catch(SQLException ex) {
// create table
// insert row
}
I would like to be more specific than just catching a general SQLException. I would like to distinguish between.
A normal error such as table doesn't exist or column count doesn't match.
and
A more serious error such as storage space exceeded or communication error between Java and MySQL.
How can I do this? I've looked at the various choices such as error codes or subclasses, but I'm not sure which way to do it.
My target database is MySQL, but I would like this to be cross-database if there is an easy way to do it. (such as just catching a certain class more specific than SQLException)

This is very opinion-based, but I think SQLException is too general to be used as is.
I understand that most programs accessing a database won't be faced with the task of handling cases when tables do not exist, but there are cases, such as when writing a framework or a tool when checking if the table exists IS important. For these cases it would make more sense to check also if the required columns are also present and have the right types. In these cases it makes sense to access the data dictionary. Of course only if the dictionary is available, which is not the case on certain production products.
When the data dictionary is not available then the only recourse is the error message and the error code, and both of them are vendor-specific. Which means there's no one solution fits all.
A long time ago I wrote a JDBCExceptionHandler, which would receive the SQLException, analyze its contents and would throw a more specialized Exception, even judging if the exception was recoverable or not. Eventually I moved on to Hibernate, which has a more comprehensive set of exceptions, coupled with the fact that I got tired of maintaining it as vendors changed their implementations.
But if you do want to pursue this line, then my advice is to use error codes and parsers on the error message. Subclassing will work, but only for your own set of exceptions. So, have an Exception Handler that will read the code and the message and then throw you own set of exceptions, subclassing them as you see fit.

From Class SQLException:
Direct Known Subclasses:
BatchUpdateException, RowSetWarning, SerialException, SQLClientInfoException, SQLNonTransientException, SQLRecoverableException, SQLTransientException, SQLWarning, SyncFactoryException, SyncProviderException
And:
public int getErrorCode()
Retrieves the vendor-specific exception code for this SQLException object.
Returns:
the vendor's error code
Error codes for MySQL can be found at Appendix B Errors, Error Codes, and Common Problems
You should catch the most specific exception (the appropriate subclass) and then use getErrorCode() if you require additional information.

In my opinion SQL Error codes are the way to go in this kind of scenario because they may provide actual implementation specific error codes as mentioned in Oracle docs:
SQL error code. This is an integer value identifying the error that
caused the SQLException instance to be thrown. Its value and meaning
are implementation-specific and might be the actual error code
returned by the underlying data source. Retrieve the error by calling
the method SQLException.getErrorCode.
For example, if you call the method CoffeesTable.dropTable with Java DB as your DBMS, the table COFFEES does not exist, and you remove the call to JDBCTutorialUtilities.ignoreSQLException, the output will be similar to the following:
SQLState: 42Y55
Error Code: 30000
Message: 'DROP TABLE' cannot be performed on
'TESTDB.COFFEES' because it does not exist.
Hope this helps.

you can do some thing like this
catch(SQLException e)
{
if(e.getMessage().indexOf("ORA-00001")!=-1)
JOptionPane.showMessageDialog(null,"you have add some
information not duplicated ,please click ok and try again...");
}

Related

How often do db specific error codes change

I need to throw specific exceptions when the database returns unique key violation exception. For this I'm using getSQLState() and getErrorCode() to identify when unique key constraint is violated.
I would like to know if and how often the vendor specific error codes and SQL States are changed
If I use sqle.getErrorCode() and sqle.getSQLState() to identify the exception type how often will I need to change the code.
Databases I am testing on are Postgres, Oracle and MySQL. Code looks somewhat like this
catch (SQLException sqle) {
if(sqle.getSQLState().equals(someSQLState) && sqle.getErrorCode() == someErrorCode) {
throw new SomeException(SomeParameters);
}
}
Oracle - The error codes never change. There's no written guarantee of this, but the error codes are thoroughly documented. I've never seen one change and surely Oracle knows that changing error codes would be disastrous.
PostgreSQL - The above answer (for Oracle) applies also to PostgreSQL. The following statement can be found in the documentation:
Applications that need to know which error condition has occurred
should usually test the error code, rather than looking at the textual
error message. The error codes are less likely to change across
PostgreSQL releases, and also are not subject to change due to
localization of error messages.
MySQL - ?

Differentiate SQLExceptions - list of error codes for Oracle?

Our application is based on Java, JPA and an Oracle database. If a database operation fails, I get a SQLException or one of it's derivates.
In my case I'd like to distinguish, whether a SQLIntegrityConstraintViolationException has been caused by a problem with a unique or a foreign key constraint.
The log shows the error code ORA-00001 for the unique constraint violation, however, I was looking for a list of the other codes in order to reacte to the right ones.
I'm aware that these codes are vendor specific and SO has an easy to find answer for MySQL. I would need the same for Oracle.
Not surprisingly, there's a list in the OracleDOcumentation:
http://docs.oracle.com/cd/B28359_01/server.111/b28278/toc.htm
but digging in from the top it's also not so easy to find. Since MySql-Answers were so much easier to find, I hope, this Q&A serves to provide an equally quick path to this information.
Unfortunately, one cant't easily search inside the list because it has been spread across multiple pages without much indication on which range contains what kinds of errors.
However, I managed to find the codes for my specific problem: UQ violation is ORA-00001, where as the foreign key errors are found under ORA-02290 (missing parent) and ORA-02291 (attempted delete with dependant children existing).
This problem might be even broader. Error handling is vendor specific and JDBC does not handle vendor specific stuff.
For example JBOSS has class in it called: ExceptionSorter see OracleExceptionSorter. This class lists all the critical error codes describing the situation that the error is on "connection level". So the connection has to be closed and (after some time) a new one has to be created.
Then there are errors on "transaction level". Obviously it is a deadlock "ORA-0060" and then some crazy error returned by concurrent "MERGE" statements. These error can be usually solved by rollback a re-trying the whole transaction.
The rest of the errors are usually related to data integrity, and these can not be re-tried.
I'm not aware of any exception sorted implementation, which covers all classes of error. Maybe it is not even possible to implement, as it can depend on application needs.

Idiomatic way to detect cause of SQLException before reporting to user

I am writing a database backed web server and I was wondering about the best practise/idiomatic way to implement the following:
So when a user creates account the server needs to insure that the username has not been already taken. The username field in the database is defined as a unique key. So rather than doing a two step process of checking the existence of the username and then adding it, I was hoping I could take advantage of the DBMS. By this I mean, just add the record and trust that an SQLException will be thrown.
My issue is how to find the cause of the exception. As in I certainly cannot return exception.getMessage() to the user but I need a way of identifying if it was due to a duplication in an unique key field that caused the error (and therefore let the user know) and not another type of issue that JDBC may throw (other issues should instead be logged).
I could try to parse e.getMessage() but I was hoping there would be a more full proof way to do this.
The best practice is capture the sqlexception, handle it (depends on your db provider exception codes) to transform into a new Exception like UserAlreadyExistException throwing it to the presentation layer. Finally in the presentation layer capture it and put an user-friendly message.

How do you catch a MySQLIntegrityConstraintViolationException correctly?

I am working on a web application, and at a point where I submit forms that are unique to the user, a MySQLIntegrityConstraintViolationException Duplicate entry -'username'
for key 'PRIMARY'
I am well aware of why this is being thrown, because I am attempting to enter a duplicate entry into the database with the same primary key. I would appreciate no comments on this, as it might be a redundant.
What I am really asking is how should I handle this. I have attempted to catch this exception, but apparently my lack of experience has caught up to me.
I would appreciate your help with it.
Is there a better method for checking for duplicate entries in a database?
For example, should I do what I do when checking for duplicate members, and just do a check to the database for a matching username and password?
Thanks
Generally, if it's possible avoid throwing Exceptions since they are a heavy mechanism for controlling program workflow. You should only use them to trace bugs and irregular activities/situations. All that means that if you catch an exception, the only thing you can do is log some more, wrap the exception in another exception and eventually triggering some fall-back program logic.
Don't expect to catch exceptions and just check for correct user name. I suppose you will be doing this check a lot if you are developing some sort of registration form ... there are a few popular words for common users and if you implement it with exceptions, you will catch it pretty often...
Unfortunately, if your database is very large, the primary keys are many and complex objects , than you can't afford to keep some of the elements in memory to perform this check for duplicate entries.
Yes, especially in this scenario it is better to check whether another user exists with the same username before trying to insert the user.
I also got the same problem , try handling the exception with the catch (MySQLIntegrityConstraintViolationException sql) .
But don't forget there are 2 types ofMySQLIntegrityConstraintViolationException one for JDBC and one for JDBC4 .
So i recommend to catch both in two separate try catch loops.

Find if an SQLException was thrown because of a duplicate

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.
This is exactly what SQLException.getSQLState() is for. Acoording to Google, "23000" indicates a unique constraint violation in at least MySQL, PostgreSQL, and Oracle.
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:
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)
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).
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.
You could "train" the program on startup (or config) by inserting a known duplicate key, and recording the thrown error code.
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.
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.
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?
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.
If you are using spring, set your unique KEY name as UK_user_id for example and catch the DataIntegrityViolationException. You can then compare ex.getCause().getConstraintName() with UK_user_id.
Maybe it's not the right way to solve this problem, but I have the same issue and I resolved with the following code:
try{
//here goes your code
//Conection to DB, stmt, ...
}catch (SQLException sqlEx){
//Check if the SQLException message contains the words "duplicate entry" and "for key"
if(sqlEx.getMessage().contains("Duplicate entry")
&& sqlEx.getMessage().contains("for key")){
System.out.println("This Key already exists in the database");
}else {
//if your Exception is not due to the duplicate key, print your SQLException
System.out.println("Error SQL:" + sqlEx.getMessage());
}
}
I hope this solution can be helpful to someone!!
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.

Categories

Resources