How to handle a PSQLException in java? - java

I have a unique constraint on one of my entities and whenever I get a PSQLException which occurs whenever that constraint is violated, I want to respond with a bad request.
This is my exception handler which I tried to implement:
#ControllerAdvice
public class DatabaseExceptionHandler {
#ExceptionHandler(value = PSQLException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleDatabaseExceptions(PSQLException e) {
// i want to respond with a bad request only when this condition is satisfied
//
// if (e.getSQLState().equals("23505")) {
//
// }
}
}
And this is where the model is saved in db:
public DepartmentForHoliday setDepartment(DepartmentForHoliday department) {
if (department.getDepartmentId() == null) {
Department savedDepartment = new Department();
savedDepartment.setName(department.getName());
try {
departmentRepository.save(savedDepartment);
} catch (PSQLException e) {
/*here i have a compiler error which says that this exception is never thrown in the corresponding try block, but where ?*/
}
}
This is the exception that is thrown when I add a duplicate entry:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "uk_1t68827l97cwyxo9r1u6t4p7d"
Detail: Key (name)=(Tech) already exists.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2458) ~[postgresql-9.4.1211.jre7.jar:9.4.1211.jre7]
How to handle PSQLExceptions ? Should I make my own exception as a wrapper or how to solve this problem ?

Key problem is that PSQLException is wrapped into some Spring exception (which I assume from your code); you have to unwrap it (for example using guava's Throwables):
public DepartmentForHoliday setDepartment(DepartmentForHoliday department) {
if (department.getDepartmentId() == null) {
Department savedDepartment = new Department();
savedDepartment.setName(department.getName());
try {
departmentRepository.save(savedDepartment);
} catch (RuntimeException e) {
Throwable rootCause = com.google.common.base.Throwables.getRootCause(e);
if (rootCause instanceof SQLException) {
if ("23505".equals(((SQLException) rootCause).getSQLState())) {
// do smth interesting :)
}
}
}
}
}
Once you do that you can throw your custom exception and handle it in DatabaseExceptionHandler

You are catching PSQLException. Instead of that, please catch SQLException. With SQLException you will can handle all this SQL exceptions.
You can check the SQLException knowledge at this link
Then in your code just treat the SQLException as you want. The most generic catch clause is the following one:
catch (SQLException e)
{
System.out.println("ERROR: Fetch statement failed: " +
e.getMessage());
}
With this code you are printing the exception. If you want more information, check this

This is quite late, but building on previous responses I was able to solve it as so:
try {
return this.projectRepository.saveAndFlush(patchedProjectEntity);
} catch (DataIntegrityViolationException e) {
if (e.getMostSpecificCause().getClass().getName().equals("org.postgresql.util.PSQLException") && ((SQLException) e.getMostSpecificCause()).getSQLState().equals("23505"))
throw new UniqueConstraintViolationException("", e.getMostSpecificCause());
throw e;
}
Where UniqueConstraintViolationException is a custom exception and handled with a spring controller advice.

You might as well register an exception handler for that wrapped exception (that #radek mentioned) directly.
In your case that's:
#ExceptionHandler(DataIntegrityViolationException::class)
protected fun handleDataIntegrityException(ex: DataIntegrityViolationException, request: WebRequest) : ResponseEntity<SomeBody>{
return ResponseEntity.badRequest().body(someBodyHere)
}
The error is converted within convertHibernateAccessException in org.springframework.orm.jpa.vendorHibernateJpaDialect, which has already processed away from PSQL. You can add a breakpoint there and follow the stacktrace.
There is a lot of proxy'ing happening under the hood, but the takeaway is that there is always a readable, expressive Exception to use directly.

Related

Force validation after EntityManager.remove()

I have a remove() in my DAO class and sometimes I got error because
FOREING KEY violation
. I.E: When user try to remove a Product that is used by a Customer.
This is my remove() method:
public void delete(AbstractEntity entidade) throws DAOException {
try {
entidade = getEm().merge(entidade);
getEm().remove(entidade);
} catch (Exception e) {
logger.error(e);
throw new DAOException(e.getMessage());
}
}
The problem is that error just appear when database touched. I need to validate some exceptions in this point to send a right Exception to USER, like: "You can delete this because other information is using it."

Using specific try catch, error overrides

This is my Exception:
public class MyException extends Exception {
private String errorCode="Unknown_Exception";
public MyException(String message, String errorCode){
super(message);
this.errorCode=errorCode;
}
public String getErrorCode(){
return this.errorCode;
}
}
Now immagine the next scenario, the code is way too long to paste here:
1 I got a Presentation class made in Swing in Presentation package
2 In package calculations I made simple operations with few numbers from received database fields
3 In package connections I got the database connections
Trouble comes here:
-In presentation layer I catch all errors, like this:
try {
//here is a method called updateCombo() wich throws:
//throw new MyException(e.getMessage(),"ERROR_UPDATING_COMBO_BOX");
} catch (MyException ex) {
try {
//Here we process error code, if error is not defined, uses default errors.
processCode(ex);
} catch (MyException ex1) {
Logger.getLogger(Presentacion.class.getName()).log(Level.SEVERE, null, ex1);
}
}
processCode is a simple list with cases, like this:
private void processCode(MyException e) throws MyException {
switch (e.getErrorCode()) {
case "ERROR_UPDATING_COMBO_BOX":
lblErrorText.setText("Error updating combo.");
throw e;
case "ERROR_SELECTING_PRIMARY_KEY":
lblErrorText.setText("Error selecting PK");
throw e;
case "ERROR_OPENING_CONNECTION":
lblErrorText.setText("Error opening connection.");
throw e;
default:
lblErrorText.setText("Excepcion not defined: "+ e.getMessage());
e.printStackTrace();
}
This is the scenario, the connection fails in 3rd package and leads to this:
throw new MyException(e.getMessage(),"ERROR_OPENING_CONNECTION");
As I said, the error is thrown to the upper layer with throws clause in method header, this beeing 2nd package.
2nd package also throws a new exception to Presentation, because of failing connection:
throw new MyException(e.getMessage(),"ERROR_SELECTING_PRIMARY_KEY");
Presentation methods also throw this exception becase 2nd layer failed:
throw new MyException(e.getMessage(),"ERROR_UPDATING_COMBO_BOX");
The main problem:
Using debug i found out that the program does what it has to do. It gets to the connection layer and does this successfully:
throw new MyException(e.getMessage(),"ERROR_OPENING_CONNECTION");
But, in 2nd layer, calculations, if connection fails it throws a new exception:
throw new MyException(e.getMessage(),"ERROR_SELECTING_PRIMARY_KEY");
This is the problem:
throw new
throwing new exception overrides ERROR_OPENING_CONNECTION with ERROR_SELECTING_PRIMARY_KEY. When it gets to presentation due to its "throw new" overrides ERROR_SELECTING_PRIMARY_KEY with ERROR_UPDATING_COMBO_BOX, resulting in the final error shown in the screen:
lblErrorText.setText("Error updating combo.");
Is there any way to return to presentation once first error is caught without overriding by next errors?
Maybe I misunderstood the concept but I want to catch all possible errors because:
-If connection is OK but method in 2nd layer fails it should throw ERROR_SELECTING_PRIMARY_KEY.
-If 2nd layer (calculations) does it OK but there is error in presentation it should lead to ERROR_UPDATING_COMBO_BOX.
You can use e.getCause() which will return a Throwable and check if this cause belongs to MyException. In case it is, you can check the e.getCause() again recursively until you obtain the deepest error code in the stacktrace and perform the validation for this exception.
Here's an example:
public MyException getDeepestException(MyException e) {
Throwable t = e.getCause();
if (t instanceof MyException) {
return getDeepestException((MyException)t);
}
return e;
}
As pointed out by #RealSkeptic, in order to use this approach, you will need to add an additional constructor to your custom exception:
public MyException(String message, Throwable cause, String errorCode){
super(message, cause);
this.errorCode = errorCode;
}
And when throwing your exception, call the proper constructor:
try {
//...
} catch (SomeException e) {
throw new MyException(<a proper message should be here>, e, "ERROR_SELECTING_PRIMARY_KEY");
}
If I understand you correctly, if the exception caught by one package happens to be a MyException, you want the original MyException to be passed up, otherwise (if the exception is some other type of Exception) you want to create a new MyException.
In this case, you should have two catch clauses.
try {
// Whatever you do in the try clause
} catch ( MyException myEx ) {
throw myEx;
} catch ( Exception e ) {
throw new MyException(e.getMessage(),"ERROR_SELECTING_PRIMARY_KEY");
}

How to capture the mybatis exception "org.apache.ibatis.exceptions.PersistenceException"?

Here is the code sample, I want to capture the exception throwed by mybatis:
String resource = "com/sureone/server/db/mybatis-config.xml";
Reader reader = null;
try {
reader = Resources.getResourceAsReader(resource);
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
sqlSession = factory.openSession(true);
tUserMapper = sqlSession.getMapper(TUserMapper.class);
if(tUserMapper.insert(user)>0){ <===Exception throwed here for duplicate entry problem
return verifyLogin(user.getAccount(),user.getPassword());
}
return null;
The exception I want to captured:
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'userName' for key 'account_UNIQUE'
You can capture the PersistenceException as you would do usually :
try {
...
} catch (PersistenceException pe) {
}
But don't forget that this Exception wraps the real one:
From MyBatis code
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
}
So if you would like the get a grip on the cause of the PersistenceException you'll have to use .getCause() method on the PersistenceException
Be aware that MyBatis can also launch its own PersistenceException (TooManyResultException,BindingException ...) classes, those won't have a cause Exception wrapped.
You can capture the ibatis exception by adding a try/catch block around your statements that invoke myBatis query/insert. For instance, if you use the SqlSessionTemplate and the selectList() method, you can do this:
try {
myResults = mySqlSessionTemplate.selectList("getInfoList", parameterMap);
} catch (final org.apache.ibatis.exceptions.PersistenceException ex) {
logger.error("Problem accessing database");
throw ex;
}
Whether you re-throw the exception or consume and deal with it here is your choice. However, beware of "eating" it and not dealing with the problem, since this will allow calling code to progress without knowing about the underlying data access problem.

EntityManager exception handling in session bean

I have a managed stateless session bean with injected EntityManager em.
What I am trying to do is to have a database table with unique column. Then I run some algorithm which is trying to insert an entity. If entity exists however it will update it or skip it.
I would like to have something like this:
try {
em.persist(cd);
em.flush();
} catch (PersistenceException e) {
// Check if the exception is DatabaseException and ConstraintViolation
// Update instead or skip it
}
Problem is that I am able to catch only PersistenceException. DatabaseException is not catched. It is sad because only DatabaseException has method called getDatabaseErrorCode() I would like to use to check duplicate entry. I dont understand it because PersistenceException.getCause() returns DatabaseException.
So my question is: How do I catch DatabaseException and check the MySQL error code?
Thank you for any ideas and experiences with this.
I have a suggestion which is I use in my application. We can retrieve the SQLException from PersistenceException. After that, try to get sql error code for SQLException. If your requirement is to get the sql error code, your can follow my example;
public void insert(Group group) throws DAOException {
try {
//your operation
em.flush();
logger.debug("insert() method has been successfully finisehd.");
} catch (PersistenceException pe) {
String sqlErroCode = getErrorCode(pe);
// do your operation based on sql errocode
}
}
protected String getErrorCode(RuntimeException e) {
Throwable throwable = e;
while (throwable != null && !(throwable instanceof SQLException)) {
throwable = throwable.getCause();
}
if (throwable instanceof SQLException) {
Properties properties = --> load sql error code form configuration file.
SQLException sqlex = (SQLException) throwable;
String errorCode = properties.getProperty(sqlex.getErrorCode() + "");
return errorCode;
}
return "NONE";
}
Example error code configuration of mysql
mysql_error_code.properties
#MySQL Database
1062=DUPLICATE_KEY_FOUND
1216=CHILD_RECORD_FOUND
1217=PARENT_RECORD_NOT_FOUND
1048=NULL_VALUE_FOUND
1205=RECORD_HAS_BEEN_LOCKED

correctly printstacktrace of servlet exception

so i am using a filter to catch servlet exception (because we are using a mix of jsf/plain servlets)
when catching the ServletException and calling printstacktrace most of the information is lost.
the "true" root exception seems to be hidden behind the "funny" expression
((ServletException) e.getRootCause().getCause()).getRootCause().getCause().getCause().getCause()
this is clearly not the way to do it.
is the an easy way to print the "full" information of such an exception.
can someone explain me why the exception is wrapped this way?
Take a look at the ExceptionUtils class from commons-lang. It contains several useful methods for printing the entire chain of exceptions.
after i had a look at ExceptionUtils, this solved the problem!
final StringWriter stacktrace = new StringWriter();
ExceptionUtils.printRootCauseStackTrace(throwable,new PrintWriter(stacktrace));
msg.append(stacktrace.getBuffer());
this prints out the full stacktrace with every piece of information that is relevant.
That is called exception chaining. By wrapping an exception in a different exception you can let exceptions bubble up the stack without having your main application classes to worry about some low-level exceptions.
Example:
public void doStuff() throws StuffException {
try {
doDatabaseStuff();
} catch (DatabaseException de1) {
throw new StuffException("Could not do stuff in the database.", de1);
}
}
This way your application only has to handle StuffException but it can get to the underlying DatabaseException if it really needs to.
To get to the bottom-most (and all other) exception(s) of an exception you caught you can iterator over its root causes:
...
} catch (SomeException se1) {
Throwable t = se1;
logger.log(Level.WARNING, "Top exception", se1);
while (t.getCause() != null) {
t = t.getCause();
logger.log(Level.WARNING, "Nested exception", t);
}
// now t contains the root cause
}
Exception chaining for ServletException is tricky. Depending on the web server implementation and web development framework in use, at runtime the chain may use cause and/or rootCause. This link explains it very well. To complicate things, I've seen exceptions where the cause points to the exception itself.
Here's a recursive method we have used that covers all bases for ServletExceptions:
public static Throwable getDeepCause(Throwable ex) {
if (ex == null) {
return ex;
}
Throwable cause;
if (ex instanceof ServletException) {
cause = ((ServletException) ex).getRootCause();
if (cause == null) {
cause = ex.getCause();
}
} else {
cause = ex.getCause();
}
if (cause != null && cause != ex) {
return getDeepCause(cause);
} else {
// stop condition - reached the end of the exception chain
return ex;
}
}

Categories

Resources