How to catch transaction exceptions in #Async? - java

When writing transactional methods with #Async, it's not possible to catch the #Transactional exceptions. Like ObjectOptimisticLockingFailureException, because they are thrown outside of the method itself during eg transaction commit.
Example:
public class UpdateService {
#Autowired
private CrudRepository<MyEntity> dao;
//throws eg ObjectOptimisticLockingFailureException.class, cannot be caught
#Async
#Transactional
public void updateEntity {
MyEntity entity = dao.findOne(..);
entity.setField(..);
}
}
I know I can catch #Async exceptions in general as follows:
#Component
public class MyHandler extends AsyncConfigurerSupport {
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
//handle
};
}
}
But I'd prefer to handle the given exception in a different way ONLY if it occurs within the UpdateService.
Question: how can I catch it inside the UpdateService?
Is the only chance: creating an additional #Service that wraps the UpdateService and has a try-catch block? Or could I do better?

You could try self-injecting your bean which should work with Spring 4.3. While self-injecting is generally not a good idea, this may be one of the use-cases which are legitimate.
#Autowired
private UpdateService self;
#Transactional
public void updateEntity() {
MyEntity entity = dao.findOne(..);
entity.setField(..);
}
#Async
public void updateEntityAsync(){
try {
self.updateEntity();
} catch (Exception e) {
// handle exception
}
}

Related

Spring transaction not rolled back after an exception is thrown

I have a service with a method, which is not annotated with #Transactional:
#Service
#RequiredArgsConstructor
public class MainService {
private final ServiceA serviceA;
public void processData() {
List<EntityA> list = serviceA.getList();
list.forEach(item -> {
try {
serviceA.doSomeDbOperations(item);
} catch(Exception e) {
// some processing here
} finally {
// some processing and DB interactions here
}
})
}
}
The goal is to roll back the changes happened in try block (serviceA.doSomeDbOperations(item)) if an exception is thrown. so I annotated this method in ServiceA with #Transactional:
#Service
public class ServiceA {
// dependencies
#Transactional
public void doSomeDbOperations(EntityA item) {
// some logic here
repositoryA.save(item)
serviceB.deleteSomething(input)
}
}
serviceB.deleteSomething(input) could throw an exception:
#Service
public class ServiceB {
// dependencies
public void deleteSomething(EntityA item) {
// some logic here
if(condition) {
Throw new RuntimeException();
}
}
}
The problem is that when an Exception is thrown, the changes in try block are not rolled back and data is non-consistent. Any idea where the problem is?
I guess in ServiceB you are using a checked Exception.
In Spring the transactions are rolled back just on unchecked exceptions by default, so try throwing for example a RuntimeException in your ServiceB.deleteSomething() method.
In case you need a checked Exception there, you can do something like this in your ServiceA.doSomeDbOperations() method:
#Transactional(rollbackFor = Throwable.class)
Some references:
https://www.baeldung.com/java-checked-unchecked-exceptions
How to make Spring #Transactional roll back on all uncaught exceptions?

How to handle global exceptions in java spring?

Currently I am working on a small system to log all the uncaught exceptions and store them into a database for further debugging / development.
To do so I am using an UncaughtExceptionHandler for a specific thread:
public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
#Autowired
private LoggedExceptionService service;
public GlobalExceptionHandler() {
}
#Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("IN UNCAUGHTEXCEPTION METHOD");
this.service.saveException(new LoggedException(e));
}
}
As you can see the field service is injected and when I catch an exception, then I get a NullPointerException because the field is null.
The main problem is the usage of the GlobalExceptionHandler. If I inject using the constructor (like in this code snippet):
private LoggedExceptionService service;
#Autowired
public GlobalExceptionHandler(LoggedExceptionService service) {
this.service = service;
}
then the field is not null, but then I can not declare it as the exception handler, because I can not autowire it to java-native methods. The call would be:
Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHandler());
Is there a possibility to autowire the handler to the thread method or what would be a good way to go?
Make it a component and set the derault exception handler in an #PostContruct method.
#Component
public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
#Autowired
private LoggedExceptionService service;
public GlobalExceptionHandler() {
}
#PostContruct
public void init(){
Thread.setDefaultUncaughtExceptionHandler(this);
}
#Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("IN UNCAUGHTEXCEPTION METHOD");
this.service.saveException(new LoggedException(e));
}
}
This allows you to automatically set the handler as methods annotated with #PostContruct in components are automatically executed on startup.
Making GlobalExceptionHandler a spring component also allows to autowire service that would never been set otherwise. Anyways, I would recommend you to usd constructor autowiring:
#Component
public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
private final LoggedExceptionService service;
#Autowired // #Autowired is actually not necessary if this is the only constructor
public GlobalExceptionHandler(LoggedExceptionService service) {
this.service=service
}
#PostContruct
public void init(){
Thread.setDefaultUncaughtExceptionHandler(this);
}
#Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("IN UNCAUGHTEXCEPTION METHOD");
this.service.saveException(new LoggedException(e));
}
}

#Transactional does not work if exception is thrown

This is my spring boot application:
#SpringBootApplication
#EnableTransactionManagement(proxyTargetClass=true)
public class WhizhopApplication {
public static void main(String[] args) {
SpringApplication.run(WhizhopApplication.class, args);
}
}
This is my service:
#Service
public class OrderService {
#Autowired
BillRepository billRepo;
#Transactional(rollbackFor = Exception.class)
public BaseDTO saveKot(BillDTO billDto) {
try {
//business logic
billRepo.save();
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}
If any exception occurs, the transaction is not rolled back.
You shouldn't catch exception if you want it to be processed with #Transactional.
Also please note that:
#Transactional works only for public methods
Spring’s #Transactional does not rollback on checked exceptions
Extract business logic into separate method annotated by #Transactional(rollbackFor = Exception.class) and remove annotation from saveKot.
public BaseDTO saveKot(BillDTO billDto) {
try {
businessLogic(billDto);
} catch (Exception e) {
// catch if it needed but manually rollback is not needed
return null;
}
}
#Transactional(rollbackFor = Exception.class)
BaseDTO businessLogic(BillDTO billDto) throws Exception {
// It will be automatically rolled back if Exception occurs
...
}
By default rollback from #Transactional is happen only for Runtime execption, if you want to change this behavior you can add rollBackFor parameter to annotation
#Transactional(rollbackFor={MyException.class})

Catching EJBTransactionRolledbackException

I have problem with understanding the EJBTransactionRolledbackException.
I have entity:
#Entity
public class MyEntity {
#Id
#GeneratedValue
private Long id;
#Size(max=5)
private String name;
//...
}
and repository which is SLSB because of ease of CMT:
#Stateless
public class ExampleRepository {
#PersistenceContext
private EntityManager em;
public void add(MyEntity me) {
em.persist(me);
}
}
now I have test Servlet, when I simulate ConstraintViolation (too long name).
#WebServlet("/example")
public class ExampleServlet extends HttpServlet {
#Inject
private ExampleRepository repo;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
MyEntity me = new MyEntity();
me.setName("TooLongName");
try {
repo.add(me);
} catch(EJBTransactionRolledbackException e) {
System.out.println("Exception caught");
}
}
}
I know that in such scenario EJB container will wrap the ConstraintViolationException so I catch the EJBTransactionRolledbackException instead. The problem is that in the console I can see my message from catch block ("Exception caught") but before that there are tons of exception logs produced (link).
I don't quite understand what happened - is this exception caught or not? How to prevent all these error messages in the console in such simple scenario?
Please look at this explanation: A clear explanation of system exception vs application exception
You have to understand that handling exceptions and handling transactions are two different things happening alongside. System exceptions unconditionally trigger transaction rollback. When you see a ConstraintViolationException, which is a system exception because it extends RuntimeException, it is not just wrapping and re-throwing. A bad thing have happened along the way - your transaction has been aborted.
So, answering the first question if the exception (ConstraintViolationException) was caught - yes, it was caught by the container. The transaction was aborted and a new exception was thrown to notify the application code about that.
You can suppress logging these messages, but then you would not know about data persistence failures.
I can suggest you two solutions:
Use bean managed transactions:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class ExampleRepository {
#PersistenceContext(synchronization = SynchronizationType.UNSYNCHRONIZED))
private EntityManager em;
#Resource
private UserTransaction tx;
public void add(MyEntity me) {
try {
tx.begin();
em.joinTransaction();
em.persist(me);
tx.commit();
} catch (ValidationException ex) {
throw new AppValidationException(ex);
}
}
}
Delegate / facade pattern:
You leave your ExampleRepository as is:
#Stateless
public class ExampleRepository {
#PersistenceContext
private EntityManager em;
public void add(MyEntity me) {
em.persist(me);
}
}
Create new EJB without transaction (with same methods as initial):
#Stateless
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class ExampleRepositoryDelegate {
#EJB
private ExampleRepository repository;
public void add(MyEntity me) {
try {
repository.add(me);
} catch (ValidationException e) {
e.printStackTrace();
}
}
}
And in servlet you use new delegate bean:
#WebServlet("/example")
public class ExampleServlet extends HttpServlet {
#Inject
private ExampleRepositoryDelegate repoDelegate;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
MyEntity me = new MyEntity();
me.setName("TooLongName");
try {
repoDelegate.add(me);
} catch(Exception e) {
System.out.println("Exception caught");
}
}
}

How is exception thrown from Hibernate DAO to Spring MVC controller?

Iam new to spring.How can I throw the exception from my Hibernate DAO calss to service and then to my controller and how to inform UI about the exception.Plese help me with exception throwing to indicate the user.
Service Class:
#Service
public class ServiceImpl implements Service {
private static Logger LOGGER = Logger.getLogger(AccessServiceImpl.class);
#Autowired
private DAO DAO;
#Override
#Transactional
public List<PrinterVO> getDetails() {
List<VO> printerList = null;
try {
LOGGER.info("Inside getAllPrinters()");
printerList = DAO.getAllPrinters();
} catch(Exception e) {
LOGGER.error("getAllPrinters() Error :", e);
}
return printerList;
}
DAO:
public class DAOImpl implements DAO {
#Autowired
#Qualifier(value="ressessionFactory")
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
#Override
public List<VO> getDetails() {
List<VO> printerList = null;
try {
LOGGER.info("Inside getAllPrinters()");
printerList = sessionFactory.getCurrentSession().createQuery("from PrinterVO").list();
} catch(HibernateException e) {
LOGGER.error("getAllPrinters() Error : ", e);
}
return printerList;
}
You're catching the exception, so it cannot propagate to your UI. You should only catch an exception if you have some action that you need to perform. Logging is not an action.
In your DAO, you should inject your Session (if you really want to depend on Hibernate) or a JPA EntityManager. If you are using dependency injection, you shouldn't be invoking factory methods.
I don't see anything in your code that would tell me how data gets to your UI, so I can't tell you how to pass an exception along to your UI either. Spring come with its own exception handling and translation facilities, so you shouldn't have to write many try/catch blocks, but rather rely on Spring's capabilities to present sensible error messages.

Categories