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();
}
Related
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 had implemented AuthenticationFailureListener for fail login by using ApplicationListener<AuthenticationFailureBadCredentialsEvent> and all my Bad Credentials event are handeled in the same class, very convenient, I have tried to add a Listener to ExpiredJwtException or SignatureException but I could not figure which event is triggered, I have tried -
#Component
public class ApplicationEventListener implements ApplicationListener<ApplicationEvent>{
#Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println(event.toString()); //not printed when ExpiredJwtException thrown
}
}
to catch all ApplicationEvent but when one of this exception happens the method onApplicationEvent does not fired.
I can catch this Exceptions but I want to handle them globally like BadCredentialsException is handled by AuthenticationFailureBadCredentialsEvent.
Tried AuthenticationFailureExpiredEvent -
#Component
public class ApplicationEventListener implements ApplicationListener<AuthenticationFailureExpiredEvent>{
#Override
public void onApplicationEvent(AuthenticationFailureExpiredEvent event) {
System.out.println("Expired!!"); //same result
}
}
but still not working.
I think the easiest way to publish an ApplicationEvent when catching exceptions is by using the ApplicationEventPublisher. There is no need to implement ApplicationEvent using this method as it wraps any object into a PayloadApplicationEvent that you can use in your ApplicationEventListener and act on it. This works from Spring 4.2 and here is the official link: https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2
I'm not sure about this, I would have to check the source, but: it could be that an application event is not emitted for ExpiredJwtException or SignatureException.
Possible solutions are:
Publish the event yourself: as you mentioned you are able to catch these exceptions, so a simple solution would be to catch them and then emit the desired event. You just need to autowire the ApplicationEventPublisher and then call publishEvent(event).
Use a Filter to catch and handle the exceptions in a single place.
Example of solution 2:
public class AuthFailureFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (ExpiredJwtException | SignatureException exception) {
handle(exception);
}
}
}
I have a controller advice that handle all validation exception thrown by my application as below.
#RestControllerAdvice
public class RestApiExceptionController {
#ExceptionHandler(ValidationErrorException.class)
public ResponseEntity<?> appNotFoundException(ValidationErrorException exception) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(exception.getErrorCode(), exception.getMessage()));
}
}
In my way, I would like to create a filter that will make validation to every request and throw custom exception when necessary. But the thing is that I cannot throw custom exception as show below.
public class ValidationFilter implements Filter {
...
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
throw new ValidationErrorException(); // This is impossible
}
...
}
How can I throw the ValidationErrorException in this case or there are other better ways to handle such task.
The validations are generally done on the request objects which are in general available in Controller layer after they are transformed from request format to server processing format. e.g. JSON to Java object.
So the validation should be performed or triggered on Controller layer once the request is reached by completing your entire filter chaining.
Any validation exception thrown then later on can be handled in your following handler,
#RestControllerAdvice
public class RestApiExceptionController {
#ExceptionHandler(ValidationErrorException.class)
public ResponseEntity<?> appNotFoundException(ValidationErrorException exception) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(exception.getErrorCode(), exception.getMessage()));
}
}
The very much one of the purpose of filters is,
To intercept requests from a client before they access a resource at
back end.
So filters in real don't have the actual resource yet to be validated. They are available once the control reaches to correct component and in your case it is Controller.
So better approach is not to do any resource based validations at filter components.
What is the significance of return type in spring controllers which are used for download. Please consider the following use case:
public ModelAndView execute(final HttpServletRequest request, final HttpServletResponse response) {
try {
//some code.
} catch {
//handle the exception and build a error model and view. This model and view
//gives a lot of freedom for error handling in case of download fails on the
//same page without change in URL(enabling refresh of the same page again
//and again)
return modelAndView;
}
return null;
}
but generally I have seen controllers which has void return types which would look like the one below
public void execute(final HttpServletRequest request, final HttpServletResponse response) {
try {
//some code.
} catch {
//handle the exception but you cannot display the error with out leaving the same page. Error embedding is not possible without changing the URL.
}
}
I have two question here:
a) Are their any disadvantages of one approach over other. I see first serves more use cases than second.
b)Is there any disadvantage of returning null instead of ModelAndView.
References:
Downloading a file from spring controllers
Error handling by redirection in spring download file controller
Nothing bad as for marking method as void. You are handling download action via HttpServletResponse.
There are suggestions that FileSystemResource is cleaner but take into account that for e.g. there are cases that you need to forward your data to some other place in order to compose the report in the other place.
Also Spring lets you easily handle exceptions even when your return type in the controller is void:
#RequestMapping(value = "/pdf-report/{id}.pdf", method = RequestMethod.GET)
public void downloadPdfReport(#PathVariable String id, HttpServletRequest req, HttpServletResponse resp) throws Exception {
//supposed logic here
//if we are failing here then
throw new UserFriendlyException("Cannot produce data");
}
Then ControllerAdvice plays its role:
#ControllerAdvice
public class ExceptionControllerAdvice {
#ExceptionHandler(UserFriendlyException.class)
public ModelAndView handleUserFriendlyException(UserFriendlyException ex) {
//handle here your custom error page
}
}
More info on that from the Spring resources
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.