I am using the JbpmContext for transaction which is implemented in filter. I have servlet controller which is calling service methods and then DAO methods to persist objects. Servlet controller is handling rollback operation if any exception occurs. But in some cases, when exception fires still I want to persist particular field of particular object at service layer. I tried with session.flush(), but it was not working at all.
Let me give you an example
class servletController {
public void doGet(request, response) throws ServletException, IOException{
try {
testService.test(object);
} catch (Exception e) {
// rollback
}
}
}
class TestService {
public void test(object) throws Exception {
try {
// Business logic. Here is the codes which throws some exception
} catch(Exception e) {
object.setFailed(true); // Any how, I want to persist this field
session.save(object);
session.flush();
throw e;
}
}
}
I have one solution like I can do it in seprate transaction but I don't want that.
So is there any other way to persist object before transaction ends ? I want to do it only at service layer.
Related
I have a service class with #Async method and If it's calling method throwing any exception then the #ControllerAdvice will not call for global exception handling. But for other classes and services it will call advice and sending email properly.
#Service
public class FileScanServiceImpl implements FileScanService {
#Override
#Async
public void scanFileScheduler() throws MQException {
try{
messageProducer.putFileNameToMQ(fileName);
} catch (Exception e) {
ExceptionUtility.handleException(e, currentFile);
}
}
The ExceptionUtility is used for checking instance on exception and doing some functionality there and throwing custom exception.
public static void handleException(Exception e throws MQException {
String errMsg = "";
if (e instanceof MQException) {
// some functionality
throw new MQException(subject, errMsg);
}
}
And this is my #ControlleAdvice
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(MQException.class)
#ResponseBody
public void handleMQException(HttpServletRequest request, MQException ex) {
// send email
}
}
It there any solution for #Async which will call #ControllerAdvice for global exception, also the existing functionality will not break.
#ExceptionHandler was created to catch only "synchronous exceptions". If it had the ability to catch exceptions from asynchronous threads, then when several threads start and if any of them fail, the request to the server would be interrupted completely and the system could remain in an inconsistent state (due to many other active threads generated by this request)
For handling asynchronous exceptions Spring has the AsyncUncaughtExceptionHandler interface:
public class YourAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// Your exception handling logic
}
}
More information can be found here in the Exceptions section: https://www.baeldung.com/spring-async
I'm trying to build a small REST service using Quarkus. I'm using Hibernate and a PostgreSQL database. It works pretty well in all good cases. But when there are Hibernate exceptions like ConstraintViolationException I'm not able to catch them in a normal way. The exceptions are wrapped with to other exception ArcUndeclaredThrowableException and RollbackException. So the exceptions can just be catched by using
catch (ArcUndeclaredThrowableException e) {
...
}
Repository
#Dependent
public class UserRepository {
#Transactional
public void createUser(User user) {
getEntityManager().persist(user); //<- the constraint violation happens at commit, so when transaction will be closed
}
}
Resource
#Override
public Response createUser(#Valid CreateUserDTO createUserDTO, UriInfo uriInfo) {
...
try {
userRepository.createUser(user);
} catch (ArcUndeclaredThrowableException e) { //<- here the hibernate exception should be catchable
log.error(e.getMessage());
throw e;
}
return Response.ok().build();
}
Because of this issue it's also not possible to add an ExceptionMapper for HibernateExceptions.
Does anybody had similar problems or is there a general problem with my code? I'm using Java11.
I would do it this way :
try {
getEntityManager().persist(user);
getEntityManager().flush();
} catch(ConstraintViolationException e) {
throw new MyCustomException(e);
}
And create Exception mapper for MyCustomException.
You can flush the Hibernate session this should triggers exceptions like ConstraintViolationException without commiting the transaction.
In your case this should be something like
#Dependent
public class UserRepository {
#Transactional
public void createUser(User user) {
getEntityManager().persist(user);
getEntityManager().flush();// should triger ConstraintViolationException
}
}
I had the same problem today, and found a workaround.
The problem, as far as I can understand is, that Arc (the cdi implemtation of quarkus) sometimes needs to generate classes.
Checked exceptions (like javax.transaction.RollbackExcpetion) need to be somehow propegated to the user. The checked Exception is therefore wrapped inside the ArcUndeclaredThrowableException. This only needs to be done however if you do not explicitly handle the exception.
For example, you can just declare the exception:
#Dependent
public class UserRepository {
#Transactional
public void createUser(User user) throws RollbackException{
getEntityManager().persist(user);
}
}
In your Resource, you can then catch the RollbackException
#Override
public Response createUser(#Valid CreateUserDTO createUserDTO, UriInfo uriInfo) {
...
try {
userRepository.createUser(user);
} catch (RollbackException e) {
log.error(e.getMessage());
throw e;
}
return Response.ok().build();
}
I am building a REST API with Spring boot and DAO layer is implemented in Hibernate.I need to understand the correct way of throwing and handling Exception in the Application.Currently I am doing it in this way
#Repository
public class UserDaoImpl
{
public getAllUsers() throws Exception
{
//get All Users from DB
}
}
#Service
public class UserServiceImpl
{
public getAllUsers throws MyCustomException
{ try{
userDaoImpl.getAllUsers();
}
catch(Exception e)
{
throw MyCustomException();
}
}
}
and In Exception Mapper
#ControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({MyCustomException.class})
#ResponseBody
public ResponseEntity<?> handleCustomException(Exception e) {
log.error("", e);
Map<String, String> error = new HashMap<String, String>();
error.put("message", e.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_ACCEPTABLE, MessageResource.getLogMessage("BAD_REQUEST_EXCEPTION"));
}
}
public class MyCustomException extends RuntimeException
{
///// ....
}
So I have added throws clause (throws Exception) in DAO layer and catch at service layer and wrap it in Custom Exception(unchecked exception) and do not propogate the exception at controller layer.
Is this correct ? or there is some better way?
I'd recommend you to have general #ExceptionHandler({Exception.class}) for all cases that you don't want to handle specifically.
Also it's okay to create separate exception classes for situations that require custom handling.
It depends on what do you want to achieve.
About your case. Exception in DAO layer does not necessary mean that request was wrong or did not provide correct parameters. It could be mapping problems, DB access problems and etc. So I would not wrap it to my custom exception, or at lest wrap in to general DataAccessException, make good logging around that and return some general error code to the client.
I have dao, service and action classes in my spring mvc application.
I am throwing Exception in Dao and Service classes. Now in Action, normally I have to write try catch block and in case exception occurs in dao and service, it will be thrown from there and it will go in catch block in action.
I have a error jsp which will be displayed.
Problem is I need to write same catch block in all action methods.
Is it possible to throw it again in action methods too and handle it from a single point rather than writing same code everywhere.
Please suggest.
You can also have a look at Spring Integration. It provides the use of gateways, filters and channels. Each can have a Request, Response and Error channel assigned. Or there is even a default error handler. In case all data flows through a specific channel, having a custom error handler is as simple as follows:
#MessageEndpoint
public class MyErrorHandler {
#ServiceActivator(inputChannel = "errorChannel")
public String handle(String messsage) {
// do whatever you like
}
}
The Integration framework offers lots of usefull stuff for general handling.
I think you are looking for cross-cutting exception handling and good news, you are working with Spring MVC yes you can use this feature.
All you need to do, is throw your CustomExcptions or whatever other Exceptions that are from your services to your action methods.
Let's say here is your service:
#Service
public class MyService {
public void someMethod throws RuntimeException {
...
}
}
In your controller method:
#Controller
public class MyController {
#Autowired
MyService service;
#RequestMapping("/someuri"){
try {
service.someMethod();
} catch {
throw new RuntimeException();
}
}
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleException(RuntimeException ex) {
ModelAndView model = new ModelAndView("errorpage");
return model;
}
}
The handleException method annotated with ExceptionHandler is your advice method for exception handling and it will be called anytime a RuntimeException is throw inside your controller and you can keep up like this for all other exceptions.
I tried this:
#RequestMapping(method = RequestMethod.GET, value = "/getmainsubjects")
#ResponseBody
public JSONArray getMainSubjects( #RequestParam("id") int id) {
List <Mainsubjects> mains = database.getMainSubjects(id, Localization.getLanguage());
JSONArray json = JSONArray.fromObject(mains);
return json;
}
When calling getmainsubjects.html?id=1 I get the error:
net.sf.json.JSONException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: fi.utu.tuha.domain.Mainsubjects.aiForms, no session or session was closed
How to fix?
The problem is,
your model object Mainsubjects had some associations (built by OneToMany, ManyToOne, etc.), Lists (PersistentBags), Sets or something (Collection) like this which're initialized lazily. It means, after initialization of result set, Mainsubjects doesn't point to an actual collection object, instead proxies. While rendering, accessing this collections, hibernate tries to get the values from Database using proxies. But at this point there's no session open. For that reason you get this exception.
You can either set your fetching strategy to EAGER (if you use annotations) like this:
#OneToMany(fetch=FetchType.EAGER)
In this method you must be aware, that you can not allow more than one PersistentBag initialized eagerly.
or you can use OpenSessionInView pattern, which's a servlet filter opens a new session before your request's handeled by controller, and closes before your web application responses:
public class DBSessionFilter implements Filter {
private static final Logger log = Logger.getLogger(DBSessionFilter.class);
private SessionFactory sf;
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
log.debug("Starting a database transaction");
sf.getCurrentSession().beginTransaction();
// Call the next filter (continue request processing)
chain.doFilter(request, response);
// Commit and cleanup
log.debug("Committing the database transaction");
sf.getCurrentSession().getTransaction().commit();
} catch (StaleObjectStateException staleEx) {
log.error("This interceptor does not implement optimistic concurrency control!");
log.error("Your application will not work until you add compensation actions!");
// Rollback, close everything, possibly compensate for any permanent changes
// during the conversation, and finally restart business conversation. Maybe
// give the user of the application a chance to merge some of his work with
// fresh data... what you do here depends on your applications design.
throw staleEx;
} catch (Throwable ex) {
// Rollback only
ex.printStackTrace();
try {
if (sf.getCurrentSession().getTransaction().isActive()) {
log.debug("Trying to rollback database transaction after exception");
sf.getCurrentSession().getTransaction().rollback();
}
} catch (Throwable rbEx) {
log.error("Could not rollback transaction after exception!", rbEx);
}
// Let others handle it... maybe another interceptor for exceptions?
throw new ServletException(ex);
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
log.debug("Initializing filter...");
log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
sf = HibernateUtils.getSessionFactory();
}