Custom Validator stopping spring internal validation - java

I created a validator to validate date field of any class. I autowired its instance in my controller and called it's validate() in my controller's method. Everything is working fine.
#RequestMapping(value = "/{pan}",method=RequestMethod.POST)
public ModelAndView submitIfPANpresents(#ModelAttribute("command") #Valid PortfolioBean portfolio,bindingResult result){
dateValidator.validate(portfolio, result);
if(result.hasErrors()){
System.out.println(result.getErrorCount());
return new ModelAndView("portfolioview");
}
:
}
Now I created a method in my controller to bind custom validator with WebDataBinder. I did this with hope that I would have not to call validate() explicitly.
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(dateValidator);
}
Still everything is working fine.
Now I commented call for validate() of custom validator from controller's method and tested the code. I can see errors set from custom validator only. Spring's internal validation stops working.
What wrong I am doing or missing to do? My need is, not to call validate() explicitly and to bind validator from xml as an extra validator.

When you do binder.setValidator(dateValidator);, you replace Spring's validator with your dateValidator. That's why Spring's internal validation stops working.
So if you want both validators to work, you should remove this line and keep dateValidator.validate(...). You have no choice if you want to have an extra validator.

Related

#Valid bean validation

Consider the two frameworks shown below. Here I need to validate the bean
Controller
In controller I m using #Valid and does the java validation. Works fine
#RequestMapping("")
void testIt(#Valid #RequestBody User user){
}
Normal Spring application without controller
Is there any way to do validation here. Its not a controller and #Valid doesn't work here.
Anyways to use #Valid or any similar type of validation for normal function?
void testIt(#Valid User user){
}
You can enable method validation by declaring beans of type org.springframework.validation.beanvalidation.MethodValidationPostProcessor and org.springframework.validation.beanvalidation.LocalValidatorFactoryBean and annotating the class containing testIt() with the #Validated annotation.
#Validated
#Component
public class TestIt {
public void testIt(#Valid User user) {
...
}
}
ConstraintViolationException will be thrown if validation errors occur when calling testIt(). Also, make sure you have Hibernate Validator in your classpath.

`#Transactional` not working for Spring 2 controller

I have an old controller within my app that is defined as a spring bean in xml and makes use of Spring's SimpleFormController. I've tried to make the processes within the onSubmit method of the controller transactional by adding the #Transactional annotation but it doesn't work. According to this guide the invocation of the annotation must happen "outside of the bean", does this mean that the annotation cannot be used in old Spring controllers like mine? Are there any alternatives or workarounds?
The reason I know it's not working is because 1) changes to the db are not rolled back on error (this is despite the fact that I have defined rollbackFor = Exception.class, and even in some instances used TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();, in this instances where it tries to use the latter it throws an error stating there is no transaction present. 2) I've added breakpoints to where #Transactional is instantiated within Spring and none of them get hit.
EDIT: So people are asking for reproducible examples of code. The problem doesn't lie within the business logic code, I'm looking for clarity on the usage of the annotation within a Spring 2 controller. So what I have for example is this:
public class ImportController extends SimpleFormController {
#Override
#Transactional(rollbackFor = Exception.class)
public ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception {
...
}
}
You are right. #Transactional will not work here because onSubmit is invoked by the same bean.
And in this case the call is done directly and the default spring transaction handling does not work.
See answers in this question for a detailed explanation of the options you have

Spring #initBinder method to be called only once inside a controller

I have a class that annotated with #RestController and #ControllerAdvice that has my requests mapped methods (#RequestMapping). Also in this class I added a method public void initBinder(WebDataBinder dataBinder) that is annotated with #InitBinder and responsible to register some custom editor.
Specifically, it is a propertyEditor that convert String to Enum.
I noticed that on every call to #RequestMapping method in my controller, my initBinder method is being called. Since in my opinion this editor registration should happen only once (initialization of controller), I want this to be set (called) only once.
Is there a way to do so?
Alternatively, you can use implementation of BindingInitializer for registering your custom Editor. You have to define a bean of class "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" in your app context file and set its "webBindingInitializer" property with your implementation of BindingInitializer.
In any case, scope of WebDataBinder is for a request. Hope this helps.

One Transaction for Hibernate Validation and Spring controller

I am trying to implement the registration controller for a Rest API. I have read about where to place #Transactional quite a bit. (Not at DAO level but at the services maybe orchestrated). In my use case I want not only services but also a hibernate validation to use the same transaction.
This is the code of the controller:
#Autowired
private UserService userService;
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
#Transactional
public DefaultResponse register(#Valid RegisterIO registerIO, BindingResult errors) {
DefaultResponse result = new DefaultResponse();
if (errors.hasErrors()) {
result.addErrors(errors);
} else {
userService.register(registerIO);
}
return result;
}
I have written an custom contraint annotation, which validates an attribute of the parameter registerIO. Both, this validator and userService.register(registerIO); access the database (check if the email address is already in use).
Therefore I want both methods use the same Hibernate session and transaction.
This approach results in the following exception:
org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:941)
The problem is the #Transactional annotation. When I place this annotation at the methods witch call the database everything works find but two transactions are startet. I suspect that when i place it at the register Method the hibernate validation is performed before #Transactional starts the transaction for this method.
I developed the following functional workaround but I am not happy with it. This codes does not use the #Valid annotation but calls the validator by itself:
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
#Transactional
public DefaultResponse register( RegisterIO registerIO, BindingResult errors) {
DefaultResponse result = new DefaultResponse();
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<RegisterIO>> valResult = validator.validate(registerIO);
I try to summarise my question:
Using Spring MVC and Hibernate-Validation together with #Valid and #Transactional, how is it possible to encapsulate the whole request into one transaction?
Thank you :)
Your workaround could be improved by using a single Validator and injecting it intp the controller. Have you tried:
#Autowired
private Validator validator;
This way you skip the overhead of creating the validator on each request.
You should also be careful with race conditions. While you are checking the database whether a given email exists another request can create this record, so that you still get an exception at the time you insert the data.

Migration to Spring Annotated Controllers and traditional onSubmit method

I am trying to migrate from Spring 2.0 to Spring 3.0.
Previously I defined a controller MyController inheriting from SimpleFormController and have some logic written in the onSubmit method. All my controllers having the handler methods are inherited from MyController. Thus, the logic written in onSubmit of MyController used to get executed for all requests.
Now as I migrate to annotated controller wherein my controller is a simple pojo, how do I ensure the execution of onSubmit everytime? One way is to call onSubmit from all handler methods of all the controllers. This is cumbersome.
Can anyone suggest any feasible solution. As annotating formBackingObject with #ModelAttribute ensures the invocation for all requests, isn't there an analogy for onSubmit method?
If you want to perform the same action before each invokation of any annotated controller, you could use an interceptor. You can write your own interceptor by just implementing the preHandle method.
You will then need to register this interceptor in the DefaultAnnotationHandlerMapping or whatever Handler mapping you use to dispatch to your controllers.
Registering interceptors is explained in this article:
http://www.scottmurphy.info/spring_framework_annotation_based_controller_interceptors
Annotate the method you wish to invoke. The method signature is very flexible. Take a look at the docs for #RequestMapping
#RequestMapping(value={"/foo"}, method=RequestMethod.POST)
public String myMethod(many options for parameters) {...
Ok so if i understand correctly you want inheritance to continue to play a role in the stack when a request is handled by a controller. You can extend any class in an #RequestMapping annotated POJO but you will have to define an #override method to annotate it. All you do basically is call super with the arguments in the overriding method. If you extend an annotated class and both are declared as Controller then you will get an exception since the route will be defined more then once.
it would look like this
public class Pojo{
public String someBaseMethod(){
return "";
}
}
#Controller
public class ChildController extends Pojo {
#Override
#RequestMapping("/do_it")
public String someBaseMethod() {
return super.someBaseMethod();
}
}
A good case could be made to use composition over inheritance. I even suggest that you use the filtering mechanism instead if it can apply to perform common operations. AOP could also be a good tool.

Categories

Resources