The following methods tries to write data to DB (firmRepository.save()) and to ElasticSearch (firmSearchRepository.save()).
The problem is that I do not want to write to ES repository if the DB save failed. I am trying to catch the exception with DataAccessException, but through my tests I see that such exceptions as unique constraint violation -> get through and persistence in ElasticSearch still executes.
#Override
public FirmDTO save (FirmDTO firmDTO) {
log.info("Database Request to save Firm: {}", firmDTO.getFirmId());
Firm firm = firmMapper.firmDTOtoFirm(firmDTO);
try {
firm = firmRepository.save(firm);
firmSearchRepository.save(firm);
return firmMapper.firmToFirmDTO(firm);
} catch (DataAccessException ex) {
log.error(ex.getLocalizedMessage());
return null;
}
}
Is there way to catch those specific exceptions related to SQL?
Using the saveAndFlush() JPA repository method made catching Spring Data exceptions possible.
#Override
#Transactional
public FirmDTO save (FirmDTO firmDTO) {
log.info("Database Request to save Firm: {}", firmDTO.getFirmId());
Firm firm = firmMapper.firmDTOtoFirm(firmDTO);
try {
firm = firmRepository.saveAndFlush(firm);
firmSearchRepository.save(firm);
return firmMapper.firmToFirmDTO(firm);
} catch (DataAccessException ex) {
log.error("my exception");
log.error(ex.getLocalizedMessage());
return null;
}
}
Related
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.
Suppose i have two triggers and both raise two different errors in the database.
When i try to insert a new entity and catch the persistance exception, how can i discriminate between those two errors? I mean something like this:
try {
entityManager.getTransaction().begin();
entityManager.persist(entity);
entityManager.getTransaction().commit();
} catch (EntityExistsException exception) {
//...
} catch (PersistenceException exception) {
if(ERROR_FROM_TRIGGER_ONE) {
//do something about it
} else if(ERROR_FROM_TRIGGER_TWO) {
//do something about it
}
}
JPA uses JDBC API as low level and from side of jdbc driver there is no difference between error in incorrect query syntax or broken trigger. So generally you cant implement such check.
But some database vendors (Oracle for example) provide unique error number in message when exception occurs, which can be used to identify real problem such as broken trigger.
Isn't the first error thrown a EJBTransactionRollBack? If not just catch a general error, siphon down to the root cause and then handle it appropriately
catch (Exception e) {
while(e.getCause() != null) {
e = e.getCause();
}
if(e instanceof PersistenceException) {
//do something
} else if (e instanceof EntityExistsException) {
//do something else
}
}
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.
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
This is a very simple example of hibernate usage in java: a function that when it's called, it creates a new object in the database. If everything goes fine, the changes are stored and visible immediately (no cache issues). If something fails, the database should be restored as if this function was never called.
public String createObject() {
PersistentTransaction t = null;
try {
t = PersistentManager.instance().getSession().beginTransaction();
Foods f = new Foods(); //Foods is an Hibernate object
//set some values on f
f.save();
t.commit();
PersistentManager.instance().getSession().clear();
return "everything allright";
} catch (Exception e) {
System.out.println("Error while creating object");
e.printStackTrace();
try {
t.rollback();
System.out.println("Database restored after the error.");
} catch (Exception e1) {
System.out.println("Error restoring database!");
e1.printStackTrace();
}
}
return "there was an error";
}
Is there any error? Would you change / improve anything?
I don't see anything wrong with your code here. As #Vinod has mentioned, we rely on frameworks like Spring to handle the tedious boiler plate code. After all, you don't want code like this to exist in every possible DAO method you have. They makes things difficult to read and debug.
One option is to use AOP where you apply AspectJ's "around" advice on your DAO method to handle the transaction. If you don't feel comfortable with AOP, then you can write your own boiler plate wrapper if you are not using frameworks like Spring.
Here's an example that I crafted up that might give you an idea:-
// think of this as an anonymous block of code you want to wrap with transaction
public abstract class CodeBlock {
public abstract void execute();
}
// wraps transaction around the CodeBlock
public class TransactionWrapper {
public boolean run(CodeBlock codeBlock) {
PersistentTransaction t = null;
boolean status = false;
try {
t = PersistentManager.instance().getSession().beginTransaction();
codeBlock.execute();
t.commit();
status = true;
}
catch (Exception e) {
e.printStackTrace();
try {
t.rollback();
}
catch (Exception ignored) {
}
}
finally {
// close session
}
return status;
}
}
Then, your actual DAO method will look like this:-
TransactionWrapper transactionWrapper = new TransactionWrapper();
public String createObject() {
boolean status = transactionWrapper.run(new CodeBlock() {
#Override
public void execute() {
Foods f = new Foods();
f.save();
}
});
return status ? "everything allright" : "there was an error";
}
The save will be through a session rather than on the object unless you have injected the session into persistent object.
Have a finally and do a session close also
finally {
//session.close()
}
Suggestion: If this code posted was for learning purpose then it is fine, otherwise I would suggest using Spring to manage this boiler plate stuff and worry only about save.