i hav a home controller within which i hav 2 methods one is
#RequestMapping(value = "/mypage.te", method = RequestMethod.GET)
public String mypage1(Locale locale, Model model){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName(); //get logged in username
model.addAttribute("username", name);
model.addAttribute("customGroup",grpDao.fetchCustomGroup());
model.addAttribute("serverTime", formattedDate);
model.addAttribute("username", name);
return "mypage";
}
here in this method actually i call grpDao.fetchCustomGroup() method from a Dao class which performs a native query and fetches data and returns and it is saved in customGroup.
now the same fetchcustomGroup() method is to be used in another method i.e
#RequestMapping(value = "/manageGrps.te", method = RequestMethod.GET)
public String man_grp_connections(#RequestParam("id") Integer groupId,#RequestParam("name") String groupName, Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
System.out.println("I am in the fetchCustomGroup controller");
int profileid=grpDao.getProfileId(name);
//model.addAttribute("customGroup",grpDao.fetchCustomGroup());
model.addAttribute("memberList",grpDao.fetchGroupMembers(groupId,profileid));
model.addAttribute("groupid",groupId);
model.addAttribute("profileid",profileid);
model.addAttribute("groupName",groupName);
System.out.println("groupid="+groupId);
System.out.println("groupName="+groupName);
return "manageGrps";
}
so instead of calling the fetchCustomGroup() in both the methods i just want to call in only one method and use the result in both the methods in the home controller.
so how can i use customGroup in another method to use the result of the fetchCustomGroup()
I think that what you want is to avoid executing the query twice. This can be done in different ways. The easiest way would be to assign the response to a variable in your controller and then use a getter instead of the dao. Controllers are singleton by default. Something like :
private Foo customGroup;
private synchronized Foo getCustomGroup() {
if(customGroup == null) {
customGroup = grpDao.fetchCustomGroup();
}
return customGroup;
}
And then use getCustomGroup() instead of grpDao.fetchCustomGroup()
I don't know what you are using for your persistence but using cache would also be a good idea to avoid executing the query twice.
Related
I think the fault comes from ulr introduced in the RedirectView parameter, but I tried different URL's and I can't make it work.
This is my project estructure:
And this is the get method from Spring MVC in controller package
#GetMapping("/planetForm")
#ResponseBody
public RedirectView toPlanetForm(Model model, #RequestParam(value = "idPlanet", required = false) String idPlanet) {
if (idPlanet != null){
Planet planet = planetService.getById(Integer.parseInt(idPlanet));
model.addAttribute("planet", planet);
}
List<Satellite> satellites = satelliteService.findAll();
model.addAttribute("satellites", satellites);
return new RedirectView("./addPlanet");
}
As you can see I am trying to do the RedirectView to the JSP called addPlanet.jsp.
I have also tried this url (among others): "../webapp/WEB-INF/jsp/addPlanet.jsp", I am convinced that it is a rookie nonsense, but I am unable to see the error
I have also tried to create another servlet and retrieve the object I need through the #ModelAtribute, but the object added in the first servlet is lost in the process, this is the code:
#GetMapping("/updatePlanet")
#ResponseBody
String update(Model model, #ModelAttribute("planet") Planet planet){
model.addAttribute("planet",planet);
return "addPlanet";
}
This wat sends the user to the addPlanet.jstl, but the object obtained by this method is an object just created by the constructor with attributes with the value of null or 0
Finally I found the solution, here it is:
#GetMapping("/planetForm/{idPlanet}")
public String toPlanetForm(Model model, #PathVariable(value = "idPlanet", required = false) String idPlanet) {
if (idPlanet != null){
Planet planet = planetService.getById(Integer.parseInt(idPlanet));
model.addAttribute("planet", planet);
}
List<Satellite> satellites = satelliteService.findAll();
model.addAttribute("satellites", satellites);
return "addPlanet";
}
As you can see I had to change the #GetMapping for a new URL, now the function returns a String which is the name of the JSP without extension. In addition to the parameter of the function #RequestParam I have had to change it to#PathVariable, which its value is the same as the mustache expression of #GetMapping
The below API is called from the UI, as well as it wants to be redirected to, by other APIs.
#RequestMapping(value = "/showAbc", method = RequestMethod.POST)
public ModelAndView showAbc(#RequestBody Abc abcInstance) {
//doSomething
}
The below API is also called from the UI end, but it fetches an instance of Abc class using a repository call and now wants to call the above API using redirect, and also wants to pass the instance as an argument.
#RequestMapping(value = "/showBcd", method = RequestMethod.POST)
public ModelAndView showBcd(#RequestParam String bcdId){
Abc abc = abcRepository.findByBcdId(bcdId);
/* How to pass the instance of Abc, when redirecting to /showAbc */
return new ModelAndView("redirect:/showAbc");
}
Now, in the above redirect I also want to pass the instance of Abc, when redirecting to /showAbc from /showBcd.
I wouldn't recommend calling one API to another if it's in the same application. This way you are tightly coupling API to API. Either leave the API contract to be called to UI or handle the delegation via plain java service calls.
#RequestMapping(value = "/showAbc", method = RequestMethod.POST)
public ModelAndView showAbc(#RequestBody Abc abcInstance) {
return new ModelAndView("abc",abcService.get());
}
#RequestMapping(value = "/showBcd", method = RequestMethod.POST)
public ModelAndView showBcd(#RequestParam String bcdId){
// You should move this repo call to bcdService.java
Abc abc = abcRepository.findByBcdId(bcdId);
//Use a normal service call to get the instance instead of a API call
return new ModelAndView("abc",abcService.getViaInstance(abc));
}
However, to have a model passed to the view(Forward the request), you need to add to the model as :
ModeAndView m = new ModelAndView("forward:/showAbc");
Abc abc = getAbcInstance();
m.addObject("abc",abc);
return m;
Please try this
ModeAndView m = new ModelAndView("redirect:/showAbc");
Abc abc = abcRepository.findByBcdId(bcdId);
m.addObject("abc",abc);
return m;
Question about Spring MVC #ModelAttribute methods, Setting model attributes in a controller #RequestMapping method verses setting attribute individually with #ModelAttribute methods, which one is considered better and is more used?
From design point of view which approach is considered better from the following:
Approach 1
#ModelAttribute("message")
public String addMessage(#PathVariable("userName") String userName, ModelMap model) {
LOGGER.info("addMessage - " + userName);
return "Spring 3 MVC Hello World - " + userName;
}
#RequestMapping(value="/welcome/{userName}", method = RequestMethod.GET)
public String printWelcome(#PathVariable("userName") String userName, ModelMap model) {
LOGGER.info("printWelcome - " + userName);
return "hello";
}
Approach 2
#RequestMapping(value="/welcome/{userName}", method = RequestMethod.GET)
public String printWelcome(#PathVariable("userName") String userName, ModelMap model) {
LOGGER.info("printWelcome - " + userName);
model.addAttribute("message", "Spring 3 MVC Hello World - " + userName);
return "hello";
}
The #ModelAttribute annotation serves two purposes depending on how it is used:
At Method level
Use #ModelAttribute at the method level to provide reference data for the model. #ModelAttribute annotated methods are executed before the chosen #RequestMapping annotated handler method. They effectively pre-populate the implicit model with specific attributes, often loaded from a database. Such an attribute can then already be accessed through #ModelAttribute annotated handler method parameters in the chosen handler method, potentially with binding and validation applied to it.
In other words; a method annotated with #ModelAttribute will populate the specified “key” in the model. This happens BEFORE the #RequestMapping
At Method Parameter level
At Method Parameter level
When you place #ModelAttribute on a method parameter, #ModelAttribute maps a model attribute to the specific, annotated method parameter. This is how the controller gets a reference to the object holding the data entered in the form.
Examples
Method Level
#Controller
public class MyController {
#ModelAttribute("productsList")
public Collection<Product> populateProducts() {
return this.productsService.getProducts();
}
}
So, in the above example, “productsList” in the Model is populated before the the #RequestMapping is performed.
Method parameter level
#Controller
public class MyController {
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(#ModelAttribute("product") Product myProduct, BindingResult result, SessionStatus status) {
new ProductValidator().validate(myProduct, result);
if (result.hasErrors()) {
return "productForm";
}
else {
this.productsService.saveProduct(myProduct);
status.setComplete();
return "productSaved";
}
}
}
Look here for detailed information with examples.
One is not better then the other. They both serve another purpose.
Method: If you need the model for a particular controller to be always populated with certain attributes the method level #ModelAttribute makes more sense.
Parameter: Use it on a parameter when you want to bind data from the request and add it to the model implicitly.
To answer your question on the better approach
I would say approach 2 is better since the data is specific to that handler.
I am using Spring 3.2.0 MVC. In that I have to store one object to session.
Currently I am using HttpSession set and get attribute to store and retrieve the value.
It returns only the String not Object. I want to use #SessionAttribute when I tried it sets the object in session but I could not retrieve the session object
#RequestMapping(value = "/sample-login", method = RequestMethod.POST)
public String getLoginClient(HttpServletRequest request,ModelMap modelMap) {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
User user = sample.createClient(userName, password);
modelMap.addAttribute("userObject", user);
return "user";
}
#RequestMapping(value = "/user-byName", method = RequestMethod.GET)
public
#ResponseBody
String getUserByName(HttpServletRequest request,#ModelAttribute User user) {
String fas= user.toString();
return fas;
}
Both methods are in same controller. How would I use this to retrieve the object?
#SessionAttributes annotation are used on the class level to :
Mark a model attribute should be persisted to HttpSession after handler methods are executed
Populate your model with previously saved object from HttpSession before handler methods are executed -- if one do exists
So you can use it alongside your #ModelAttribute annotation like in this example:
#Controller
#RequestMapping("/counter")
#SessionAttributes("mycounter")
public class CounterController {
// Checks if there's a model attribute 'mycounter', if not create a new one.
// Since 'mycounter' is labelled as session attribute it will be persisted to
// HttpSession
#RequestMapping(method = GET)
public String get(Model model) {
if(!model.containsAttribute("mycounter")) {
model.addAttribute("mycounter", new MyCounter(0));
}
return "counter";
}
// Obtain 'mycounter' object for this user's session and increment it
#RequestMapping(method = POST)
public String post(#ModelAttribute("mycounter") MyCounter myCounter) {
myCounter.increment();
return "redirect:/counter";
}
}
Also don't forget common noobie pitfall: make sure you make your session objects Serializable.
I have a session attribute : user, and I have a url that I want to be viewed by both logged in users and publically by people not logged in as a user.
So what I want to do is this :
#Controller("myController")
#SessionAttributes({"user"})
public class MyController {
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id) {
return modelandview1;
}
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, #ModelAttribute User user){
return modelandview2;
}
However, I have a feeling its not going to work ... suggestions very welcome.
You only need the second method, the one that takes the User agument as well. When it's called without request attributes available to populate the User model, you'll just get a User instance with all null (or all default) field values, then in the body of the method you treat each situation accordingly
I don't think it's a right case for #SessionAttributes. This annotation is usually used to keep original instance of a form-backing object, to avoid passing irrelevant parts of its state via hidden form fields.
Your sceanrio is completely different, thus it would be better to use HttpSession explicitly:
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, HttpSession session) {
User user = (User) session.getAttribute(...);
if (user != null) {
...
} else {
...
}
}
Also note that #ModelAttribute is a subject to data binding - user can change its fields by passing request parameters. You definitely don't want it in this case.