Open njr-11 opened 5 years ago
I was investigating this topic at work too and saw this issue. I just want to add something I just logged while testing:
com.ibm.websphere.ce.cm.DuplicateKeyException Purpose: Originally provided as a convenience to internal code for detecting attempted insertion of and later externalized, only to later come into conflict with the JDBC 4.0 spec-defined SQLException subclass for constraint violations. Replacement Actions: If a JDBC 4.0 compliant JDBC driver is used, switch to the JDBC 4.0 spec standard exception: java.sql.SQLIntegrityConstraintViolationException. If using an out-of-date JDBC driver at an earlier JDBC specification compliance level than JDBC 4.0, trap for SQLException and compare the SQLState (23505 for all databases; 23500, 23L01 for Derby) and error code (2627 for DataDirect Connect for JDBC; -803 for DB2; -239, -268 for Informix; 2627 for Microsoft JDBC; 1 for Oracle, 2601 for Sybase).
This is not correct as Oracle docs say - there isn't any 23505 for Oracle!. When an unique constraint is violated the SQLState
is 23000 (not 23505) and SQLError
is 1 (OK). See this log where we loop throw the exception and check if it's and DuplicateKeyException
:
public static boolean isDuplicateKeyException(Exception exception) {
LOG.entry(exception);
Throwable cause = exception;
while (cause != null) {
LOG.trace("Class: [{}]", cause.getClass().getName());
LOG.trace("Message: [{}]", cause.getMessage());
boolean isIBMDKE = (cause instanceof DuplicateKeyException);
LOG.trace("Ist IBM-DuplicateKeyException: [{}]", isIBMDKE);
boolean isSQLE = (cause instanceof SQLException);
LOG.trace("Ist SQLException: [{}]", isSQLE);
if (cause instanceof SQLException) {
SQLException sqlE = (SQLException) cause;
LOG.trace("SQL-Error: [{}]", sqlE.getErrorCode());
LOG.trace("SQL-State: [{}]", sqlE.getSQLState());
}
if (cause instanceof DuplicateKeyException) {
LOG.exit(true);
return true;
}
cause = cause.getCause();
}
LOG.exit(false);
return false;
}
Log:
1. Loop
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:134 - entry params(javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement)
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:139 - Class: [javax.persistence.PersistenceException]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:140 - Message: [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:143 - Ist IBM-DuplicateKeyException: [false]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:146 - Ist SQLException: [false]
2. Loop
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:139 - Class: [org.hibernate.exception.ConstraintViolationException]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:140 - Message: [could not execute statement]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:143 - Ist IBM-DuplicateKeyException: [false]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:146 - Ist SQLException: [false]
3. Loop
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:139 - Class: [com.ibm.websphere.ce.cm.DuplicateKeyException]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:140 - Message: [ORA-00001: unique constraint (<NAME OF MY CONSTRAINT>) violated]
2020-04-23T09:22:21,130 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:143 - Ist IBM-DuplicateKeyException: [true]
2020-04-23T09:22:21,146 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:146 - Ist SQLException: [true]
2020-04-23T09:22:21,146 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:150 - SQL-Error: [1]
2020-04-23T09:22:21,146 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:151 - SQL-State: [23000]
2020-04-23T09:22:21,146 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:156 - exit with(true)
I should mentione here that the SQLState
seems to affect more errors, e.g. when violating a check constraint (X
is allowed but inserting Y
) the SQLError
is 2290
as logs show. Also the Exception is another (as expected):
3. Loop
2020-04-23T09:49:57,107 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:139 - Class: [java.sql.SQLIntegrityConstraintViolationException]
2020-04-23T09:49:57,107 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:140 - Message: [ORA-02290: check constraint (<NAME OF MY CONSTRAINT>) violated]
2020-04-23T09:49:57,107 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:143 - Ist IBM-DuplicateKeyException: [false]
2020-04-23T09:49:57,107 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:146 - Ist SQLException: [true]
2020-04-23T09:49:57,107 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:150 - SQL-Error: [2290]
2020-04-23T09:49:57,107 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:151 - SQL-State: [23000]
2020-04-23T09:49:57,107 TRACE [.Alarm Pool : 0] d.i.c.g.u.DaoUtil:162 - exit with(false)
@Bukama thanks for spotting this problem. I went back and checked the code and it does look for SQL State 23505 in all cases, which ends up being a no-op for Oracle. It gets away with this because it is also looking for the error code of 1, and considering either condition being met as indicating the DuplicateKeyException. While that is so, it doesn't seem to constitute a good reason why we should direct the user to perform a no-op check for Oracle, so I'll looking into rewriting that proposed section to be more precise.
Write up some detailed instructions on how to replace exceptions in the com.ibm.websphere.ce.cm and com.ibm.websphere.ce.j2c packages with standard JDBC spec exceptions. The intent is to improve the migration tool with this information.
com.ibm.websphere.ce.j2c package
if
block of ResourceAllocationException which checks ifresourceAllocationException.getClass().getName().equals("ConnectionWaitTimeoutException")
.com.ibm.websphere.ce.cm package
com.ibm.websphere.ce.cm.ConnectionWaitTimeoutException Purpose: Originally provided as a convenience for distinguishing timeouts waiting for a connection from the pool from other error conditions, only to later come into conflict with the JDBC 4.0 spec-defined SQLException subclass for transient connection errors. Replacement Actions: Switch to the JDBC 4.0 spec standard exception: java.sql.SQLTransientConnectionException. If further disambiguation is needed between SQLTransientConnectionException due to connection wait timeout vs other reasons, the application can also check for a cause exception where the simple class name is "ConnectionWaitTimeoutException".
com.ibm.websphere.ce.cm.DuplicateKeyException Purpose: Originally provided as a convenience to internal code for detecting attempted insertion of a duplicate key and later externalized, only to later come into conflict with the JDBC 4.0 spec-defined SQLException subclass for constraint violations. Replacement Actions: If a JDBC 4.0 compliant JDBC driver is used, switch to the JDBC 4.0 spec standard exception: java.sql.SQLIntegrityConstraintViolationException. If using an out-of-date JDBC driver at an earlier JDBC specification compliance level than JDBC 4.0, trap for SQLException and compare the SQLState (23505 for most databases not including Oracle; 23500, 23L01 for Derby) or error code (2627 for DataDirect Connect for JDBC; -803 for DB2; -239, -268 for Informix; 2627 for Microsoft JDBC; 1 for Oracle, 2601 for Sybase).
com.ibm.websphere.ce.cm.ObjectClosedException Purpose: Originally raised when an operation is attempted but the application server found the connection/statement/resultset to be closed prior to attempting any operation on the JDBC driver. Later, this came into conflict with the JDBC 4.0 spec-defined SQLException subclasses. Replacement Actions: Switch to the JDBC 4.0 spec standard exception: java.sql.SQLRecoverableException. If further disambiguation is needed between SQLRecoverableException due to closed JDBC resources vs other reasons, the application can check for if the message text begins with "DSRA9110E", which indicates closed JDBC resources detected by the application server.
com.ibm.websphere.ce.cm.PortableSQLException Purpose: A base class for all of the custom exception types to inherit from, which let the application server know that it had already been processed/categorized, but this doesn't provide any value to the end user. The only user value would have been in supplying custom exceptions of one's own and then setting up mappings for them via a custom DataStoreHelper - however, custom DataStoreHelper wasn't something that apps provide, it was a separate integration point that tied directly into JDBC driver configuration. This doesn't otherwise provide any end-user value. Replacement Actions: First course of action should be to remove references to PortableSQLException if it is only used in exports. If the user supplies code, probably in a separate library rather than the application, that implements PortableSQLException, that needs to be dealt with as a separate feature. This analysis for migration only concerns the application itself.
com.ibm.websphere.ce.cm.StaleConnectionException Purpose: A convenience for identifying more generic SQLExceptions as indicating a bad connection, signaling the user to retry the operation with a new connection request in a new transaction. This mechanism later came into conflict with the JDBC 4.0 spec-defined SQLException subclasses for classifying connection errors. Replacement Actions: Switch to the JDBC 4.0 spec standard exceptions: java.sql.SQLRecoverableException, java.sql.SQLNonTransientConnectionException, or the combination of java.sql.SQLTransientConnectionException with errorCode of -4498 on DB2. If not using a JDBC 4.0 driver, then trap for SQLException and use the database/SQLState/error code combinations to determine if stale. Stale connection error codes for DB2: -514, -518, -1776, -4498, -4499 Stale connection error codes for Derby Network Server: -4499, 40000, 45000, 50000 Stale connection error codes for Derby Embedded: 40000, 45000, 50000 Stale connection error codes for Informix: -710, -908, -4499, -25580, -27002, -43207, -79716, -79735, 43012 Stale connection error codes for SQL Server: 230, 6001, 6002, 6005, 6006 Stale connection error codes for Oracle: 20, 28, 1012, 1014, 1033, 1034, 1035, 1089, 1090, 1092, 3113, 3114, 12505, 12541, 12560, 12571, 17002, 17008, 17009, 17410, 17401, 17430, 17447, 25408, 24794 Stale Connection SQLStates for DB2 on i native JDBC driver HY017 Stale connection SQL States for Sybase: JZ0C0, JZ0C1 Stale connection SQL States for all databases: 08001, 08003, 08006, 08S01, 40003, 55032, S1000
com.ibm.websphere.ce.cm.StaleStatementException Purpose: Used internally to categorize a particular type of stale connection exception as indicating a change was made that causes cached statements to become invalid. Replacement Actions: Trap for SQLException and use the database/SQLState/error code combinations to determine if stale. Stale statement error codes for DB2: -514, -518 Stale statement error codes for Informix: -710 Stale statement SQLStates for Derby: XCL10