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 - ?
Related
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.
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...");
}
I'm accessing IBM DB2 datatable available as ODBC datasource, using sun.jdbc.odbc.JdbcOdbcDriver driver.
The problem that I've encountered is, when I'm executing DELETE FROM table using java.sql.Statement.executeUpdate(String), and the table is empty, the java.sql.SQLException is thrown (the expected behaviour is that 0 should be returned). The exception message is "No data found".
It's my first contact with IBM DB2, so my question is, is that expected behaviour on that database? Or this is something that can be configured on database level? Or it is ODBC-specific thing?
If it is standard behaviour, how should I recognize that 'exception' from real exceptions? I don't like parsing exception messages for business logic, but if it is necessary, can I expect, it will always be "No data found"?
I've tried to google the specification of that behaviour, but I've found no documentation for that case.
This is completely normal. DB2 did not found anything to delete, and return this warning.
Many databases have the same status: sqlstate 02000. Take a look in Google and you will see that other RDBM have the same code. However, DB2 has sqlcode to explain the reason, and for this sqlstate, the corresponding code is sql0100W.
You have to deal with this behaviour while working with DB2, this is a SQLWarning (The W at the end of the sqlcode).
http://publib.boulder.ibm.com/infocenter/db2luw/v10r1/topic/com.ibm.db2.luw.messages.sql.doc/doc/msql00100w.html
For the next time, take a look at the sqlcode. It will help you to find the problem while working with DB2.
Business logic is coded in pl/sql packages procedures and functions.
Java programs call pl/sql packages procedures and functions to do database work.
pl/sql programs store exceptions into Oracle tables whenever an exception is raised.
How would my java programs get the exceptions since the exception instead of being propagated from pl/sql to java is getting persisted to a oracle table and the procs/functions just return 1 or 0.
Sorry folks i should have added this constraint much earlier and avoided this confusion. As with many legacy projects we don't have the freedom to modify the stored procedures.
Assuming you can't change the PLSQL code, you'll have to monitor the table. And of course, that will only work if the error table stores some sort of session or use identifier.
java.sql.CallableStatement throws java.sql.SQLException. If your stored proc throws an exception, your Java code will know about it. The code that calls the stored proc will have to handle this exception.
You have a choice: you can either have the stored proc throw the exception or have the Java code check the return value and query the table for the exception if the error code is returned.
But the exception isn't "lost" either way. You get it from the JVM or the table.
I vote for the JVM because it's easier. Less PL/SQL code, less Java code.
"is getting persisted to a oracle table"
You could create a trigger on that table that rejects the insert. For example, if the error table contains an 'ERROR_DESCRIPTION' column, you could have a BEFORE INSERT ON error_table FOR EACH ROW trigger which does a RAISE_APPLICATION_ERROR(-20001,:NEW.ERROR_DESCRIPTION)
When the PL/SQL code goes to log the error, that will fail with the replacement error and that will, if you are lucky, get propogated to the Java layer.
It is an ugly hack, but if you truly can't change the code, it may work.
Simply if you use a framework that supports aspects, it would be easy to make an aspect that checks for the exception in the appropriate table. If not, then you could write something similar to this code:
ResultSet exRs = null;
try {
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
statement.execute(normalSql);
exRs = statement.executeQuery(exceptionSql);
exRs.next();
} catch (SQLException e) {
e.printStackTrace();
connection.rollback();
}
if (null != exRs.getString(exceptionColumn))
connection.commit();
else
connection.rollback();
Sorry I couldn't be more specific.
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.