PL/SQL exception and Java programs - java

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.

Related

Distinguish basic SQLException types in 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...");
}

How to create a table in MySQL when using java

I'm trying to create a table in mysql through java. I'm using putty for this by the way. Here is a bit of the code I have so far but it doesn't work.
rs=s.executeQuery("CREATE TABLE test(id CHAR(2),name VARCHAR(3),PRIMARY KEY(id)); ");
while(rs.next())
{
System.out.println(rs.getString(1));
}
catch (SQLException ex)
{
System.out.println("SQLException:"+ ex.getMessage());
}
executeQuery() is for quires (usually SELECT) that return a ResultSet.
With DML (and DDL) queries you need to use executeUpdate() method.
For more information and examples use Setting Up Tables tutorial.
See this post here: Data Manipulation Statements
You should be using executeUpdate() if you wish to actually modify the database.
Your query is ok! But you don't get a result set! the CREATE TABLE won't give an rows or columns.
You have been tricked be the documentation:
Returns:
a ResultSet object that contains the data produced by the given query; never null
however
Throws:
SQLException - if a database access error occurs,... the given SQL statement produces anything other than a single ResultSet object, ...
In my opinion a call of "execute" would be the proper way.
I don't think its ever a good idea to generate your database schema via Java. Use the utility tool that comes with your database to create your schema. This way, you or anyone (such as a DBA) can create your tables, views, indexes, constraints, grant permissions, etc without having to know Java. You can even have your database utility generate an SQL script that you can run to re-generate the schema from scratch. Last point: I believe you will be better off calling your primary key test_id and making it type numberic, long, or int. this way, when you refer to it as a foreign key in another table, you will immediately know it refers back to the test table.

PostgreSQL Transaction fails: "there is no transaction in progress"

I've got a pretty long transaction I'm trying to execute using JDBC for PostgreSQL. In JDBC I can't use COMMIT and ROLLBACK, so I'm trying to implement my desired behaviour in Java code...
try {
con = DriverManager.getConnection(url, user, password);
con.setAutoCommit(false);
Statement st = con.createStatement();
st.execute(myHugeTransaction);
con.commit();
} catch (SQLException ex) {
try {
con.rollback();
} catch (SQLException ex1) {
// log...
}
// log...
}
For small statements, this works pretty well, but for the large ones with about 10K statements in a single transaction, this fails in the con.commit line with
org.postgresql.util.PSQLException: ERROR: kind mismatch among backends. Possible last query was: "COMMIT" kind details are: 0[C] 1[N: there is no transaction in progress]
The funny thing is, if I catch SQL Warnings with st.getWarnings(); I can see that the database is actually processing the whole script I've sent, just when it comes to the commit, it all fails.
btw, the transaction is totally fine. I write an exact copy of it into a file and I can run it without errors by copying it into pgAdmin. Hope you can help me on that one, I've been searching and testing stuff for hours now...
edit
Maybe I didn't get this right, so two questions:
Can I execute multiple statements in one call to Statement.execute()?
If not, what is the right way to run a Script with multiple statements using JDBC (without the need to parse and split it into single statements)?
Honestly, if this is a SQL script you are better off doing a shell escape to psql. That is the best way to handle this. In general I have had way too many unpleasant surprises from people trying to parse out SQL code and run it against the db. This way madness lies.
you say "smaller scripts" which leads me to conclude you are doing something like setting up a database (or upgrading one, but that's less likely since there are no queries). Use psql through a shell escape and don't look back. That really is the best way.
I suppose if you have to you could try adding explicit BEGIN and COMMIT to your script.
I am not sure why it seems to be committing the transaction implicitly. you have set autocommit off properly. There are no obvious problems in your code. Is it possible you have an old or buggy JDBC driver? If not, I would recommend filing a bug report with the PostgreSQL JDBC driver project.

how to check database table permissions in java? (DataBase independently)

Does anyone know a way (in java) to check permissions to a specific table for a user? It has to work on the most Databases. SELECT right is simple to check (just trying to execute a simple SELECT statement), but how to check INSERT permission?
You can use the DatabaseMetaData to get this information. It should work in most cases. However, it might be inaccurate in special cases, if the privileges can not be represented in this abstract way:
DatabaseMetaData metaData connection.getMetaData();
metaData.getTablePrivileges();
metaData.getColumnPrivileges();
though not a direct solution but the only option that comes to my mind is to use TRY CATCH blocks, issue an INSERT statement and then checking the error message in java.sql.SQLException object returned in case of exception.

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