Hibernate not updating SessionAttribute parameter.
I populated an entity object and put it in SessionAttribute with a name editGroup. I changed it on jsp page and sent to the controller. In controller I just call service method and give him object from session(editGroup). But entity object not updating.
Controller
#Controller
#SessionAttributes(value = "editGroup")
#RequestMapping(value = "/group")
public class GroupController {
...
#RequestMapping(value = "/{id}/edit", method = RequestMethod.GET)
public String groupEdit(#PathVariable Long id, Model model) {
if (!model.containsAttribute("editGroup")) {
model.addAttribute("editGroup", groupService.findGroup(id));
}
return "group/editGroup";
}
#RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
public String groupUpdate(#ModelAttribute("editGroup") Group editGroup,
#PathVariable Long id, SessionStatus status) {
groupService.updateGroup(editGroup);
status.setComplete();
return "redirect:/group/" + id;
}
}
Service #Transactional
public void updateGroup(Group obj) {
groupDAO.update(group);
}
DAO
public void update(T obj) {
getSession().merge(obj);
}
Hibernate even not sends request. Help me please.
I found answer on my issue.
I had the <tx:annotation-driven/> in a spring configuration. After specify the transaction-manager parameter in this one, program running properly.
<tx:annotation-driven transaction-manager="transactionManager"/>
Related
I am using Spring Framework with restful web services, and I am trying to create an API with restful service and use a get method. I have created a method and I'm trying to have it return a string, but instead I get a 404 error - requested resources not found. Please see my code below:
#RestController
#RequestMapping("/test")
public class AreaController {
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return "list";
}
}
I am using: localhosr:8080/MyProject/wangdu
This error occurs because you forgot to add
#RequestMapping(value = "/{name}", method = RequestMethod.GET) before your find method:
#RestController
#RequestMapping("/test")
public class AreaController {
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return "list";
}
}
Please make sure about this:
The value that the find method is returning is a String with the value "list" and the find method declaration is waiting for a RestResponse object
For example if I have a RestResponse object like this:
public class RestResponse {
private String value;
public RestResponse(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
}
Then try to return the value in this way:
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return new RestResponse("list");
}
Verify that the method has #RequestMapping annotation with the value that your expect from the url
#RequestMapping(method = RequestMethod.GET, value = "/{name}")
By default the proper way to call the rest resource is by the #RequestMapping value that you set at the #RestController level (#RequestMapping("/test")), in this case could be: http://localhost:8080/test/myValue
If you need to use a different context path then you can change it on the application.properties (for spring boot)
server.contextPath=/MyProject/wangdu
In that case you can call the api like this:
http://localhost:8080/MyProject/wangdu/test/myValue
Here is the complete code for this alternative:
#RestController
#RequestMapping("/test")
public class AreaController {
#RequestMapping(method = RequestMethod.GET, value = "/{name}")
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return new RestResponse("list");
}
I'm learning to make Bean Validation works in Spring MVC with Thymeleaf as default view. Every valid data can be saved properly. But when I tried an invalid data passed, Tomcat just showed HTTP Status 400 Error page. In Tomcat console showed something like validation but just became logging text in Tomcat console. Here is the controller that saves data (item).
#Controller
#RequestMapping("/item")
#SessionAttributes("item")
public class ItemController {
#Autowired
private ItemService itemService;
#Autowired
private ColorService colorService;
#ModelAttribute("allColors")
public List<Color> populateColors() {
return colorService.findAll();
}
#ModelAttribute("allItems")
public List<Item> populateItems() {
return itemService.findAll();
}
#RequestMapping(value = {"/image/{id}", "image/{id}"})
#ResponseBody
public byte[] showImage(#PathVariable("id") String id) {
return itemService.getItem(id).getImage();
}
#RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
public String showAllItems() {
return "itemList";
}
#RequestMapping(value = {"add", "/add"}, method = RequestMethod.GET)
public String showItemAddForm(Model model) {
model.addAttribute("item", new Item());
return "itemAddForm";
}
#RequestMapping(value = {"add", "/add"}, method = RequestMethod.POST)
public String processAddItem(
#ModelAttribute("item") #Valid Item item,
RedirectAttributes model,
BindingResult errors,
SessionStatus session) {
if (errors.hasErrors()) {
return "itemAddForm";
}
itemService.saveItem(item);
session.setComplete();
model.addFlashAttribute("message", "Item has been added");
return "redirect:/item";
}
}
Is any wrong with the controller? How should I to make Bean Validation works with Spring and Thymeleaf?
Example:
public String getStudentResult(#RequestParam(value = "regNo", required = true) String regNo, ModelMap model){
How can I use #valid for the regNo parameter here?
Late answer. I encounter this problem recently and find a solution. You can do it as follows,
Firstly register a bean of MethodValidationPostProcessor:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
and then add the #Validated to the type level of your controller:
#RestController
#Validated
public class FooController {
#RequestMapping("/email")
public Map<String, Object> validate(#Email(message="请输入合法的email地址") #RequestParam String email){
Map<String, Object> result = new HashMap<String, Object>();
result.put("email", email);
return result;
}
}
And if user requested with a invalid email address, the ConstraintViolationException will be thrown. And you can catch it with:
#ControllerAdvice
public class AmazonExceptionHandler {
#ExceptionHandler(ConstraintViolationException.class)
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleValidationException(ConstraintViolationException e){
for(ConstraintViolation<?> s:e.getConstraintViolations()){
return s.getInvalidValue()+": "+s.getMessage();
}
return "请求参数不合法";
}
}
You can check out my demo here
#Valid can be used to validate beans. I have'nt seen it used on single string parameters. Also it requires a validator to be configured.
The #Valid annotation is part of the standard JSR-303 Bean Validation API, and is not a Spring-specific construct.
Spring MVC will validate a #Valid object after binding so-long as an appropriate Validator has been configured.
Reference : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
one way to do it is to write a Wrapper Bean like the following :
public class RegWrapperBean{
#NotNull
String regNo ;
public String getRegNo(){
return regNo ;
}
public void setRegNo(String str){
this.regNo=str;
}
}
and your handler method will be like the following :
#RequestMapping(value="/getStudentResult", method=RequestMethod.POST)
public String getStudentResult(#Valid #ModelAttribute RegWrapperBean bean,
BindingResult validationResult, Model model) {
}
and please refer to these answers here and here .
Hope that Helps .
I have this problem where I call API with get and it works fine while it gives empty object in case of POST. Below are the code snippets.
#Controller
#RequestMapping("/demo")
public class DemoController {
#RequestMapping(value = "/create")
public ModelAndView createUser(#ModelAttribute User user) {
...
...
}
}
GET: localhost:8080/demo/create.json?name=test&title=this works fine
POST using form-data is not working. I am getting empty object.
public class User implements Serializable {
private String name;
private String title;
...
}
I would suggest the following code.
#RequestMapping(value = "/create", method = RequestMethod.GET)
public ModelAndView createUser() {
ModelAndView modelAndView = new ModelAndView("/createuser.jsp");
modelAndView.addObject("user", new User());
return modelAndView;
}
#RequestMapping(value = "/create", method = RequestMethod.POST)
public ModelAndView createUserProcess(#ModelAttribute("user") User user,) {
// save user to db
}
Just out of curiosity. Shouldn't you be displaying the form with createUser and processing it with createUserProcess? Also make sure your form has the following:
<form:form commandName="user" modelAttribute="user">
</form:form>
Thanks for your kind replies. I was using Postman to validate my controller. I found I need to add multipart resolver.
Below is the line that fixed it.
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
Thanks!
For a simple RESTful JSON api implemented in Spring MVC, can I use Bean Validation (JSR-303) to validate the path variables passed into the handler method?
For example:
#RequestMapping(value = "/number/{customerNumber}")
#ResponseBody
public ResponseObject searchByNumber(#PathVariable("customerNumber") String customerNumber) {
...
}
Here, I need to validate the customerNumber variables's length using Bean validation. Is this possible with Spring MVC v3.x.x? If not, what's the best approach for this type of validations?
Thanks.
Spring does not support #javax.validation.Valid on #PathVariable annotated parameters in handler methods. There was an Improvement request, but it is still unresolved.
Your best bet is to just do your custom validation in the handler method body or consider using org.springframework.validation.annotation.Validated as suggested in other answers.
You can use like this:
use org.springframework.validation.annotation.Validated to valid RequestParam or PathVariable.
*
* Variant of JSR-303's {#link javax.validation.Valid}, supporting the
* specification of validation groups. Designed for convenient use with
* Spring's JSR-303 support but not JSR-303 specific.
*
step.1 init ValidationConfig
#Configuration
public class ValidationConfig {
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
return processor;
}
}
step.2 Add #Validated to your controller handler class, Like:
#RequestMapping(value = "poo/foo")
#Validated
public class FooController {
...
}
step.3 Add validators to your handler method:
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public ResponseEntity<Foo> delete(
#PathVariable("id") #Size(min = 1) #CustomerValidator int id) throws RestException {
// do something
return new ResponseEntity(HttpStatus.OK);
}
final step. Add exception resolver to your context:
#Component
public class BindExceptionResolver implements HandlerExceptionResolver {
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (ex.getClass().equals(BindException.class)) {
BindException exception = (BindException) ex;
List<FieldError> fieldErrors = exception.getFieldErrors();
return new ModelAndView(new MappingJackson2JsonView(), buildErrorModel(request, response, fieldErrors));
}
}
}
The solution is simple:
#GetMapping(value = {"/", "/{hash:[a-fA-F0-9]{40}}"})
public String request(#PathVariable(value = "hash", required = false) String historyHash)
{
// Accepted requests: either "/" or "/{40 character long hash}"
}
And yes, PathVariables are ment to be validated, like any user input.
Instead of using #PathVariable, you can take advantage of Spring MVC ability to map path variables into a bean:
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/{id}")
public void get(#Valid GetDto dto) {
// dto.getId() is the path variable
}
}
And the bean contains the actual validation rules:
#Data
public class GetDto {
#Min(1) #Max(99)
private long id;
}
Make sure that your path variables ({id}) correspond to the bean fields (id);
#PathVariable is not meant to be validated in order to send back a readable message to the user. As principle a pathVariable should never be invalid. If a pathVariable is invalid the reason can be:
a bug generated a bad url (an href in jsp for example). No #Valid is
needed and no message is needed, just fix the code;
"the user" is manipulating the url.
Again, no #Valid is needed, no meaningful message to the user should
be given.
In both cases just leave an exception bubble up until it is catched by
the usual Spring ExceptionHandlers in order to generate a nice
error page or a meaningful json response indicating the error. In
order to get this result you can do some validation using custom editors.
Create a CustomerNumber class, possibly as immutable (implementing a CharSequence is not needed but allows you to use it basically as if it were a String)
public class CustomerNumber implements CharSequence {
private String customerNumber;
public CustomerNumber(String customerNumber) {
this.customerNumber = customerNumber;
}
#Override
public String toString() {
return customerNumber == null ? null : customerNumber.toString();
}
#Override
public int length() {
return customerNumber.length();
}
#Override
public char charAt(int index) {
return customerNumber.charAt(index);
}
#Override
public CharSequence subSequence(int start, int end) {
return customerNumber.subSequence(start, end);
}
#Override
public boolean equals(Object obj) {
return customerNumber.equals(obj);
}
#Override
public int hashCode() {
return customerNumber.hashCode();
}
}
Create an editor implementing your validation logic (in this case no whitespaces and fixed length, just as an example)
public class CustomerNumberEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.hasText(text) && !StringUtils.containsWhitespace(text) && text.length() == YOUR_LENGTH) {
setValue(new CustomerNumber(text));
} else {
throw new IllegalArgumentException();
// you could also subclass and throw IllegalArgumentException
// in order to manage a more detailed error message
}
}
#Override
public String getAsText() {
return ((CustomerNumber) this.getValue()).toString();
}
}
Register the editor in the Controller
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(CustomerNumber.class, new CustomerNumberEditor());
// ... other editors
}
Change the signature of your controller method accepting CustomerNumber instead of String (whatever your ResponseObject is ...)
#RequestMapping(value = "/number/{customerNumber}")
#ResponseBody
public ResponseObject searchByNumber(#PathVariable("customerNumber") CustomerNumber customerNumber) {
...
}
You can create the answer you want by using the fields in the ConstraintViolationException with the following method;
#ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handlePathVariableError(final ConstraintViolationException exception) {
log.error(exception.getMessage(), exception);
final List<SisSubError> subErrors = new ArrayList<>();
exception.getConstraintViolations().forEach(constraintViolation -> subErrors.add(generateSubError(constraintViolation)));
final SisError error = generateErrorWithSubErrors(VALIDATION_ERROR, HttpStatus.BAD_REQUEST, subErrors);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
You need to added an #Validated annotation to Controller class and any validation annotation before path variable field
Path variable may not be linked with any bean in your system. What do you want to annotate with JSR-303 annotations?
To validate path variable you should use this approach Problem validating #PathVariable url on spring 3 mvc
Actually there is a very simple solution to this. Add or override the same controller method with its request mapping not having the placeholder for the path variable and throw ResponseStatusException from it. Code given below
#RequestMapping(value = "/number")
#ResponseBody
public ResponseObject searchByNumber() {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"customer number missing")
}