I am new to using Spring boot framework.
I want to create a #GetMapping where based on what user enters in the parameter either Property1 Name(String) or Protery2 Designation(String) or Property3 Salary(Integer) the method should be able to get the List of employees based on one or more properties.
I can create individual methods but I do not want to do that.
I want to do something like this:
#GetMapping("/employee")
public List<Employee> getEmployee(Params parameters)
{
// Filter the list based on parameters provided and return the list
}
Also, I am not understanding how to handle parameters
for example, if it is an integer there is only one column but if the user enters string there are two columns.
If the user does not specify the parameter name I have to handle that.
You can use #RequestParam Map<String, String> params to bind all parameters to one variable
E.g.
#RequestMapping(value="/params", method = RequestMethod.GET)
public ResponseEntity getParams(#RequestParam Map<String, String> params ) {
System.out.println(params.keySet());
System.out.println(params.values());
return new ResponseEntity<String>("ok", HttpStatus.OK);
}
You can define the three parameters using the #RequestParam annotation and check which one is non-empty:
#GetMapping("/employee")
public List<Employee> getEmployee(#RequestParam(defaultValue = "empty") String name, #RequestParam(defaultValue = "empty") String designation, ....
{
// check which one is not empty and perform logic
if (!name.equals("empty")) {
// do something
}
}
Regarding which parameter the user chooses: you can make a drop-down menu or a simple-radio selection, where the user chooses the search criteria himself (and where each criterion is mapped by a request parameter). For example:
Related
Desired request:
http://localhost:8080/values?input=[["aaa","bbb","ccc","ddd"],["abcd","abcd","abcd","abcd"]]
I would love to be able to pass the URL in some way using brackets.
However if it is not possible, how do I still pass the list of lists ?
My controller path:
#GetMapping(path = "/values/")
public String getValues(#RequestParam List<List<#Size(min=4, max=4)String>> input) {
return input.get(0).get(2);
}
which should return ccc.
This is not a good way to do this job. Instead you can pass them as request parameter and and receive them from hashMap. But since you are passing list of lists, so it would be convenient to pass them in request body. To do that you have to create a request POJO class following these steps:
public class ListRequest{
private List<List<String>> inputList;
//generate getter, setter for it
}
Now, change in your controller, replace GET method with POST:
#PostMapping(path = "/values/")
public String getValues(#RequestBody ListRequest input) {
List<List<String>> yourData=input;
System.out.println(yourData);
//operate on your data as you wish
return input.get(0).get(2);
}
Create a Pojo class with a list of list as field.
Send the values in the request body and use a post method.
In the controller method, use this Pojo object to retrieve those values.
I want to implement some simple endpoint in spring, trying to be as much Restful as possible and reduce the number of URL to use. Here are the GET url I want to call: (this is a simplified version)
GET /users
GET /users?id=123
GET /users?username=xyz
I used this controller:
#GetMapping()
public #ResponseBody
OutputUserDTO getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && !id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && !username.isEmpty()) {
return userService.getUserByUsername(username);
}
throw new MissingParameterException("...some message...");
}
#GetMapping()
public #ResponseBody
List<OutputUserDTO> getUsers() {
return userService.getUsers();
}
Of course I get an error, that is Ambiguous mapping.
I thought to always return a List so that I can merge the 2 endpoints and, in case you pass some parameters, return a Singleton... even though I don't know if it's a correct practice.
Or else, create one endpoint for each parameter, GET /users/{userId}, GET /users/{username}, ... but I don't like it neither (If I have 10 different way to get a user then I'll have to implement 10 endpoints :S)
What are some good practices in this case??
Thanks.
Replace MissingParameterException with return userService.getUsers();, and get rid of the other method, you know, the one with exactly the same mapping as the first method.
To make that work, you'd have to change return type to Object, which is not going to be a problem, since it's the actual object returned that controls the effect of #ResponseBody, not the declared type.
#GetMapping()
#ResponseBody
public Object getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && ! id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && ! username.isEmpty()) {
return userService.getUserByUsername(username);
}
return userService.getUsers();
}
FYI: #ResponseBody is a method-level annotation, so it should be listed before any keyword modifiers.
The Java Language Specification, section 8.3.1. Field Modifiers, says:
FieldModifier:
(one of)
Annotation public protected private
static final transient volatile
[...]
If two or more (distinct) field modifiers appear in a field declaration, it is customary, though not required, that they appear in the order consistent with that shown above in the production for FieldModifier.
It should be like #GetMapping("/users") on respective method
http://www.appsdeveloperblog.com/pathvariable-spring-mvc/
I suppose that the reason for that is, in getUserByParameter, both parameters are optional but if both the parameters are not passed it will conflict with your second getMapping.
more over, what is returned changes in the three scenarios. scenario 1 returns a list of DTOs while scenarios 2 & 3 return a single DTO
i dont think you can handle all three scenarios using your request path /users unless you want to wrap even a single DTO in a list, in which case you can simply merge your two methods. call getUsers() when both parameters are missing, in other cases, do what you currently do but wrap the response in a list.
if you want to keep them separate and return DTO or List, you should probably separate them out into /users and /user by specifying #GetMapping("/user") on method one and #GetMapping("/users") on method two
hope this helps
in my method I have let's say UserDetails object which has some defined value like id, phone etc.
User changing only one value through form, it's email. Is the way how I could use method
userDetailsForm.bindFromRequest(new String[]{"email"}).get()
to not 'loose' previous values? The above example give me new userDetail object with only defined email field. Of course I know that I can use DynamicForm class, or just
userDetailsForm.bindFromRequest().get().getEmail()
but it would be helpfull to have method which does all this binding in one line.
=========EDIT
DynamicForm dynamicForm = Form.form().bindFromRequest();
String email = dynamicForm.get("email");
isn't that what I'm looking for.
=========EDIT======================================
In other words, I want to divide my form to 3 steps, but after every step I am doing update on DB. So for example when I am POSTing step2 I have object with values from previous step:
User [userId=8, createdById=12, name=null, active=false, country=EN]
so now when I am doing:
static Form<User> userForm = Form.form(User.class);
User user = User.find(8);
User user2 = (User) userForm.fill(user).bindFromRequest("name").get();
I am geting new object with empty fields:
User [userId=0, createdById=0, name="value from step 2", active=false, country=null]
I will be very greatfull for any advise.
Try this
Form<UserDetail> submittedForm = form(UserDetail.class).bindFromRequest();
String emailID = submittedForm.data().get("email");
data() will hold name and value pair like following Map<String, String>, further retrieve value by providing its key name inside get() will return you the desired value.
Ok guys, I've figured out how to solve problem.
Here is discussion about this:
https://groups.google.com/forum/#!searchin/play-framework/Form$20bind/play-framework/MtjBV5YNQ3E/QumAmLbMl5sJ
one of possible solution is here:
https://gist.github.com/nraychaudhuri/10590943
import com.fasterxml.jackson.databind.ObjectMapper;
private static Form<Computer> editableForm(final Computer obj) {
ObjectMapper mapper = new ObjectMapper();
Form<Computer> form = Form.form(Computer.class);
Map<String,String> data = mapper.convertValue(obj, Map.class);
Map<String, String> submittedData = form.bindFromRequest().data();
data.putAll(submittedData);
return form.bind(data);
}
and my solution is below:
public T bind(T target, Map<String, String> newValues) {
DataBinder binder = new DataBinder(target);
binder.setAllowedFields(getAllowedFields());
binder.bind(new MutablePropertyValues(newValues));
return target;
}
=================EDIT
Here is important disscussion about security issue: https://groups.google.com/forum/#!searchin/play-framework/Form$20bind/play-framework/uGrSlJMo48c/QnVjzP4ovqcJ
Form.bindFromRequest
without arguments. However, as probably most of you know, invoking
that method without parameters will bind all fields of the
corresponding model object to like-named request parameters, including
fields holding internal state that must never be set from the outside
(e.g. boolean isAuthenticated). That is, an attacker may set any field
and circumvent security if they only know the name of the
corresponding model object field. This is of course a catastrophic
security vulnerability (similar to PHP's notorious and deprecated
register_globals option:
http://www.php.net/manual/en/security.globals.php).
My understanding so far is on our controller request mapping method we can specify RedirectAttributes parameter and populate it with attributes for when the request gets redirected.
Example:
#RequestMapping(value="/hello", method=GET)
public String hello(RedirectAttributes redirAttr)
{
// should I use redirAttr.addAttribute() or redirAttr.addFlashAttribute() here ?
// ...
return "redirect:/somewhere";
}
The redirect attributes will then be available on the target page where it redirects to.
However RedirectAttributes class has two methods:
addAttribute()
addFlashAttribute()
Have been reading Spring documentation for a while but I'm a bit lost. What is the fundamental difference between those two, and how should I choose which one to use?
Here is the difference:
addFlashAttribute() actually stores the attributes in a flashmap
(which is internally maintained in the users session and removed
once the next redirected request gets fulfilled)
addAttribute() essentially constructs request parameters out of
your attributes and redirects to the desired page with the request
parameters.
So the advantage of addFlashAttribute() will be that you can store pretty much any object in your flash attribute (as it is not serialized into request params at all, but maintained as an object), whereas with addAttribute() since the object that you add gets transformed to a normal request param, you are pretty limited to the object types like String or primitives.
Assume you have 2 controllers.If you redirect from one controller to
another controller the values in model object won't be available in the
other controller. So if you want to share the model object values
then you have to say in first controller
addFlashAttribute("modelkey", "modelvalue");
Then second controller's model contains now the above key value pair..
Second question ? What is difference between addAttribute and addFlashAttribute in RedirectAttributes class
addAttribute will pass the values as requestparameters instead of model,so when you add some using addAttribute you can access those values from request.getParameter
Here is the code.I have used to find out what is going on :
#RequestMapping(value = "/rm1", method = RequestMethod.POST)
public String rm1(Model model,RedirectAttributes rm) {
System.out.println("Entered rm1 method ");
rm.addFlashAttribute("modelkey", "modelvalue");
rm.addAttribute("nonflash", "nonflashvalue");
model.addAttribute("modelkey", "modelvalue");
return "redirect:/rm2.htm";
}
#RequestMapping(value = "/rm2", method = RequestMethod.GET)
public String rm2(Model model,HttpServletRequest request) {
System.out.println("Entered rm2 method ");
Map md = model.asMap();
for (Object modelKey : md.keySet()) {
Object modelValue = md.get(modelKey);
System.out.println(modelKey + " -- " + modelValue);
}
System.out.println("=== Request data ===");
java.util.Enumeration<String> reqEnum = request.getParameterNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
System.out.println(s);
System.out.println("==" + request.getParameter(s));
}
return "controller2output";
}
Javadoc description:
"A FlashMap provides a way for one request to store attributes intended for use in another. This is most commonly needed when redirecting from one URL to another -- e.g. the Post/Redirect/Get pattern. A FlashMap is saved before the redirect (typically in the session) and is made available after the redirect and removed immediately."
I have been trying for last 3 days still i am not able to solve my problem
I have Person Class
#SuppressWarnings("rawtypes")
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
#JoinColumn(name="person_id")
public Set<Book> books = new HashSet<Book>();
class Book
book_id
person_id
In my JSP form i have
<c:forEach items="${BookList}" var="var1" varStatus="counter">
<input type="checkbox" name="books[${counter.index}].book_id" value="${var1.book_id}" >${var1.book_name}</input>
</c:forEach>
I am inserting the books in table depending upon the check boxes
The book list is populated from refrenceData model.
COntroller
#RequestMapping(value = "/persons/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Create new Person and add to model
// This is the formBackingOBject
model.addAttribute("personAttribute", new Person());
// This will resolve to /WEB-INF/jsp/addpage.jsp
return "hibernate/addpage";
}
#RequestMapping(value = "/persons/add", method = RequestMethod.POST)
public String add(#Valid #ModelAttribute("personAttribute") Person person, BindingResult result) {
logger.debug("Received request to add new person");
if (result.hasErrors())
return "hibernate/addpage";
else
personService.add(person);
// This will resolve to /WEB-INF/jsp/addedpage.jsp
return "hibernate/addedpage";
}
Now if i have single Book object then this works ok and data is entered in DB but if i have set then it says invalid property book[1]
After searching a lot on SO and Google i leart that i have two option
PropertyEditor
AutoPopulatingList
I don't know how to use them in my case. Can anyone help me , where do i have to use them and how to use it
Look at this question Bind objects in a Set collection
You need to use another type of Collection. I'd recommend to use a List instead of a Map. When you send from the form a parameter with a name like:
name="books[0].book_id"
SpringMVC will look in the property called books (which is a Set for you) and then it will try to get the first element by doing books.get(0). Set don't have a get because Set has not an order.
For the implementation of the list you can use AutoPopulatingList. It is an implementation of a lazy List which will create an object if it doesn't exist. For example if you invoke books[0].id and you haven't added a book in the position 0 of the list it will throw a NullPointerException, but if you use AutoPopulatingList it will create a new Book and addd it in that position if that position is empty.
public List<Book> books = new AutoPopulatingList<Book>(new ElementFactory<Book>() {
#Override
public Book createElement(final int index) throws ElementInstantiationException {
//call the constructor as you need
return new Book();
}
});
if you you are going to instanciate it with the default constructor of Book (that is Book()), you can use a syntax like this one:
public List<Book> books = new AutoPopulatingList<Book>(Book.class);
When I have such complicated form i honestly prefer to use JSON and submit it using AJAX.
{"person":{"id":1,"books":[{"person_id":2,"book_id":3},{"person_id":2,"book_id":6},{"person_id":3,"book_id":4}]}
#RequestMapping(value = "/persons/add", method = RequestMethod.POST)
#ResponseBody
public String add(#RequestBody Person person){
//ad your business logic
}
Your code will be validate by de-serializer and you will be able to save it.
You can reed more about that in this post:
http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/
Binding of Set isn't possible with Spring MVC as Sets do not have indexes to work with. Although you can iterate through sets in JSP and show the results.
The solutions might be -
Use another type of collection like List.
Wrap your Set in a POJO, use your Set for showing its containing values in JSP. Once you want to post the form containing your selection, add new property in your POJO which is String(or similar) and provide this property as your PATH in JSP tag, which will get the selection data from JSP. Then in backend code, fill your set with this value.
In case, your POJO is the also an Entity for your database creation using Hibernate, simply put #Transient on top of it. Hibernate will ignore this property while creating table.