Spring Data JPA Closed Connection - spring

There is a function which inserts a record in the database with status "Running" at the start then do some long processing and at the end it updates the status to "Success" or "failed".
I am getting an error in the end while updating the status as processing took a long time ( 4 hrs to upload data to third-party app).
java.sql.BatchUpdateException: ORA-02396: exceeded maximum idle time, please connect again
SQL: update STATUS set status=?
o.h.engine.jdbc.spi.SqlExceptionHelper : ORA-02396: exceeded maximum idle time, please connect again
ERROR [-,f292b6c9becb8716,f292b6c9becb8716,false] 13556 --- [nio-8080-exec-7] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.GenericJDBCException: could not execute batch]
WARN [-,f292b6c9becb8716,f292b6c9becb8716,false] 13556 --- [nio-8080-exec-7] com.zaxxer.hikari.pool.ProxyConnection : HikariPool-2 - Connection oracle.jdbc.driver.T4CConnection#7fb2645b marked as broken because of SQLSTATE(08003), ErrorCode(17008)
java.sql.SQLRecoverableException: Closed Connection
at oracle.jdbc.driver.PhysicalConnection.getAutoCommit(PhysicalConnection.java:1828)
at oracle.jdbc.driver.PhysicalConnection.rollback(PhysicalConnection.java:1953)
at com.zaxxer.hikari.pool.ProxyConnection.rollback(ProxyConnection.java:377)
at com.zaxxer.hikari.pool.HikariProxyConnection.rollback(HikariProxyConnection.java)
at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.rollback(AbstractLogicalConnectionImplementor.java:116)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.rollback(JdbcResourceLocalTransactionCoordinatorImpl.java:251)
at org.hibernate.engine.transaction.internal.TransactionImpl.rollback(TransactionImpl.java:100)
How I can handle this situation?
Below is my code snippet:
public void upload() {
entity.setStatus("RUNNING");
repository.save(entity);
try {
//Uploads data to thrid party;
callingThridPartyApp();
log.info("Upload successfull.");
entity.setStatus("SUCCESS");
repository.save(entity);
} catch (Exception e) {
log.error("Upload failed.", e);
entity.setStatus("FAILED");
repository.save(entity);
}
}

I solved this issue by stating a new transaction at the end of the long process to update the status in database.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateStatus(Entity entity) {
log.info("Upload successfull.");
entity.setStatus("SUCCESS");
repository.save(entity);
} catch (Exception e) {
log.error("Upload failed.", e);
entity.setStatus("FAILED");
repository.save(entity);
}
}

Related

Spring batch - issue with explicitly setting the job as failed

I am trying to set the status of job as failed in afterJob listener based on few conditions. But the job commits the changes and exits by marking job completed. Below is how I am setting the status. What could be wrong here?
jobExecution.addFailureException(e);
jobExecution.setExitStatus(new ExitStatus("FAILED", "Incorrect data"));
jobExecution.setStatus(BatchStatus.FAILED);
Version: Spring Boot 2.7.0
public void afterJob(JobExecution jobExecution) {
String message = "Completed job.";
try {
//get context and batch
ExecutionContext executionContext = jobExecution.getExecutionContext();
//processing done here
} catch (Exception coe) {
jobExecution.addFailureException(coe);
jobExecution.setExitStatus(new ExitStatus("FAILED", "Could not complete job"));
jobExecution.setStatus(BatchStatus.FAILED);
message = jobExecution.getExitStatus().getExitDescription();
} finally {
try {
//Database update here. When this fails, the Job is expected be marked FAILED, but its marked COMPLETED
throw new Exception("DB update failed"); //simulating exception
} catch (Exception e) {
System.out.println("Exception while saving final details to DB. This is printed");
jobExecution.addFailureException(e);
jobExecution.setExitStatus(new ExitStatus("FAILED", "Could not complete job"));
jobExecution.setStatus(BatchStatus.FAILED);
}
}
}

Grails transaction wokflow

I have a scheduler code(In grails) which runs inside transaction block:
Plan.withTransaction{
.....
plan.save(failOnError: true)
transaction.save(failOnError: true)
try{
smsService.sendSMS(transaction) // transactional service
}catch(Exception e){
}
..........
}
Inside the sendSMS method:
def sendSMS(Transaction transaction){
try{
getSMSContent()
sendSms()
} catch(Exception e){ print ...exception catched here.... }
}
String getSMSContent(...){
..... it throws exception in some cases
}
Now the issue is that plan and transaction is not saved(validate returns ok with no errors) if getSMSContent() throws exception. I think the transaction should not roll back because we catched the exception in the same service. Do I need to catch the exception in the same method which is getSMSContent so that transaction does not rollback and plan/transaction is saved even if sms is failed ?
Or should I mark the sms service as "Requires_New" so that new transaction gets created ?

Cannot update any attribute in the testUpdateBookOrdersShippingAddress

I have a problem about updating BookOrder. BookOrder is connected to DetailOrder via composite key according to many to many relationship.
The error is shown below.
May 23, 2019 2:33:48 PM com.mchange.v2.c3p0.impl.NewPooledConnection
INFO: [c3p0] Exceptions occurred while trying to close a PooledConnection's resources normally.
May 23, 2019 2:33:48 PM com.mchange.v2.c3p0.impl.NewPooledConnection
INFO: [c3p0] NewPooledConnection close Exception.
java.sql.SQLException: ORA-03106: fatal two-task communication protocol error
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:494)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:441)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:436)
at oracle.jdbc.driver.T4C7Ocommoncall.processError(T4C7Ocommoncall.java:86)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:623)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:252)
at oracle.jdbc.driver.T4C7Ocommoncall.doOLOGOFF(T4C7Ocommoncall.java:62)
at oracle.jdbc.driver.T4CConnection.logoff(T4CConnection.java:908)
at oracle.jdbc.driver.PhysicalConnection.close(PhysicalConnection.java:2005)
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:642)
at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:622)
at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1076)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1101)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1062)
at com.mchange.v2.resourcepool.BasicResourcePool.access$100(BasicResourcePool.java:44)
at com.mchange.v2.resourcepool.BasicResourcePool$5.run(BasicResourcePool.java:1316)
May 23, 2019 2:33:48 PM com.mchange.v2.resourcepool.BasicResourcePool
WARNING: Failed to destroy resource: com.mchange.v2.c3p0.impl.NewPooledConnection#3dd18dc6
java.sql.SQLException: Some resources failed to close properly while closing com.mchange.v2.c3p0.impl.NewPooledConnection#3dd18dc6
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:664)
at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:622)
at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1076)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1101)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1062)
at com.mchange.v2.resourcepool.BasicResourcePool.access$100(BasicResourcePool.java:44)
at com.mchange.v2.resourcepool.BasicResourcePool$5.run(BasicResourcePool.java:1316)
I think there can be a session problem. Session can be automatically closed when it is needed to open.
My test class
#Test
public void testUpdateBookOrdersShippingAddress() {
Integer orderId = 48;
BookOrders bookorders = ordersDAO.get(orderId);
System.out.println("Update Before (ShippingAddress) : " + bookorders.getShippingAddress());
bookorders.setShippingAddress("New Home Shipping Address");
try {
ordersDAO.update(bookorders);
} catch (Exception e) {
e.printStackTrace();
}
BookOrders updatedOrder = ordersDAO.get(orderId);
System.out.println("Update After (ShippingAddress) : " + updatedOrder.getShippingAddress() );
assertEquals(bookorders.getShippingAddress(), updatedOrder.getShippingAddress());
}
Hibernate Class for get and update method.
public T update(T t) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
session.merge(t);
session.flush();
session.getTransaction().commit();
session.close();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
transaction.rollback();
}
return t;
}
public T get(Class<T> type,Object id) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
T t = null;
try {
transaction = session.beginTransaction();
t = session.get(type, (int)id);
session.flush();
session.getTransaction().commit();
session.close();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
transaction.rollback();
}
return t;
}
In update function of hibernate class based on generic type, I could use session.update(t); instead of session.merge(t); and I could solve it.

How to catch integrity constraint deletion error?

There are two database tables role and role_menu. There is foreign key in role_menu referencing role's primary key.
Now I am deleting a row of the master table role which row has a foreign key associated in role_menu. I tried to enclose the delete code inside try catch but the execution does not enter in the catch block :
#Override
#Transactional
public void delete(String role_code) {
try {
sessionFactory.getCurrentSession().delete((Role) sessionFactory.getCurrentSession().get(Role.class, role_code));
} catch (Exception e) {
System.out.println("error : there is fk !");
}
}
In console I got these texts :
WARN : org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 2292, SQLState: 23000
ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ORA-02292: integrity constraint (PTA.FK_ROLE_MEN_R_ROLE_ME_ROLE) violated - child record found
So how to deal with integrity constraint deletion error ?
The problem came from the fact that the exception is thrown only when the session is flush to the database just before the transaction is committed. Since you are using the #Transactional annotation, it is happening at the end of the method right after the catch block. Flushing the session at the end of the try block should do the trick :
#Override
#Transactional
public void delete(String role_code) {
try {
Session session = sessionFactory.getCurrentSession();
session.delete((Role) session.get(Role.class, role_code));
session.flush();
} catch (Exception e) {
System.out.println("error : there is fk !");
}
}
Note that it may be better to avoid using the flush there and catch the exception at another level, but it depends on the use case.

Can't catch StaleObjectStateException with hibernate

I'm having some trouble trying to catch an exception when there are concurrency violations using hibernate and Spring AOP. This is my scenario:
(My MyConcurrentStateControl has a #Version column)
Service layer
#Override
public Integer saveWork(WorkDto dto) throws MyException {
try {
return workBusinessLogicService.saveWork(dto);
} catch (PersistenceException ex) {
// -- Concurrent insert exception
if (ex.getCause() instanceof ConstraintViolationException) {
String errorMsg = "The same item is being created by another user. Please refresh.";
LOGGER.error(errorMsg, ex);
throw new MyException(errorMsg);
}
} catch (StaleObjectStateException ole){
// -- Concurrent update exception
String errorMsg = "The same item is being updated by another user. Please refresh";
LOGGER.error(errorMsg, ole);
throw new MyException(errorMsg);
}
return -1;
}
Business Logic layer
#Override
#Transactional(rollbackFor = {Exception.class, MyException.class, PersistenceException.class, StaleObjectStateException.class})
public Integer saveWork(WorkDto dto) throws MyException, PersistenceException, StaleObjectStateException {
MyConcurrentStateControl concurrentState = concurrentStateManager.getState(dto.getId());
if (concurrentState == null) {
concurrentState = new MyConcurrentStateControl();
}
// -- Do updates in some other tables --
// Save Concurrent State for concurrency check (Optimistic Locking)
Integer id = concurrentStateManager.save(concurrentState);
// -- Also tried entityManager.flush();
}
This is the error I'm getting in the log, and it is thrown in this line:
return workBusinessLogicService.saveWork(dto);
It is the error I'm expecting when multiple threads call the service, but I can't do anything with it.
[#|2015-01-30T20:53:49.793+0000|WARNING|glassfish3.1.2|javax.enterprise.system.core.transaction.com.sun.jts.jta|_ThreadID=32;_ThreadName=Thread-12;|JTS5054: Unexpected error occurred in after completion
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.mymodel.entity.ConcurrentState#7]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2471)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3123)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3021)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3350)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)
......................
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1012)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at net.bull.javamelody.MonitoringSpringInterceptor.invoke(MonitoringSpringInterceptor.java:74)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.mymodel.business.core.service.WorkServiceImpl$$EnhancerByCGLIB$$b1800d24.saveWork(<generated>)
I know the actual queries are triggered on transaction commit, and at that point the control has moved out of the method, therefore I'm not being able to catch the StaleObjectStateException. But how can I do that, or is there some alternative? All I want is:
Roll back all transactions.
Show a reasonable message to the user that there have been concurrent updates and he needs to refresh the UI.

Resources