Update one attribute of an Entity with ModelAttribute - spring

How can I update just one or a few atributes of an Entity with spring form and controller?
Lets say it is User Entity and has id, status, name, address etc...
I want to update just name, and address. But when I try to save ather values is null. ı dont want to show all attributes in form logically ( Id, status )

You can use hidden input element to propagate users ID to your view, e.g.
<input type="hidden" name="user-id" value="${editUserForm.id}">
Put it in a form - when a form is submitted, users ID will also be submitted with it (remember to add ID to your form model). Then retrieve user from database using this ID, set fields you want to set and update it.
EDIT:
Example:
your model:
#Entity
public class User{
private Long id;
private String name;
private String surname;
//getters & setters
}
form you use to edit some of the fields (no surname):
public class UserForm{
private Long id;
private String name;
//getters & setters, constructor
}
Controller:
#GetMapping(value="/editUser/{userId}")
public ModelAndView editUser(#PathVariable Long userId){
ModelAndView modelAndView = new ModelAndView("editUser");
User user = // retrieve user from database using userId
modelAndView.addObject("editUserForm", new UserForm(user));
return modelAndView;
}
#PostMapping(value="/editUser")
public ModelAndView postEditUser(#ModelAttribute("editUserForm") UserForm editUserForm){
User userToEdit = //retrive user from database using editUserForm.getId()
userToEdit.setName(editUserForm.getName());
//save user to database
//redirect
}
Of course logic I presented in controllers should be located in service layer, I just want to give you an idea on what to do.

Related

How to handle post and put request data validation

I have following user details model that is used in POST & PUT controllers of /user resource.
public class UserDetails {
#NotBlank
private String username;
#NotBlank
private String password;
#NotBlank
private String firstName;
#NotBlank
private String lastName;
#NotBlank
private String nic;
#NotNull
private Integer roleId;
// constructor & getters setters
}
#PostMapping("/org/employee")
public void createEmployee(#RequestBody EmployeeDetailsModel empDetails) {
employeeService.createUser(empDetails);
}
#PutMapping("/org/employee")
public void updateEmployee(#RequestBody EmployeeDetailsModel empDetails) {
employeeService.updateUser(empDetails);
}
Here, UserDetails has #NotNull & #NotBlank validations. POST would work fine because to create a user, all details are mandatory. But when updating with PUT, I don't need all properties of UserDetails to be filled.
So my questions are,
How this kind of scenarios are handled? Do we usually force clients to send all those details whether they are changed or not?
Is it possible to disable request body validation just for a particular endpoint or do I have to create separate model that looks the same but without validations?
Seeing your post I can infer that you are interested in modifying the resource
Well to do this you should to use PATCH method instead of PUT.
In PUT you need to send the entire data since it is intended for replacing the resource which is not in the case of the PATCH.
Well in case of the PUT or PATCH we need to ensure that we have an existing resource. Hence before saving it is necessary that we get the original resource from the data store. Then we can modify it with the help of the validation rules on the Entity itself.
so your code should be like.
Considering you have a repository class named as
EmployeeRepository
#PutMapping("/org/employee/{id}")
public void updateEmployee(#RequestBody EmployeeDetailsModel empDetails, #PathVariable("id") int id) {
Optional<Employee> emp = employeeRepo.findById(id);
if (emp.isPresent()) {
// update the new values using setters
// Finally update the resource.
employeeService.updateUser(empDetails);
} else {
throw new ResourceNotFoundException("Your custom msg");
}
}
The repository code should be placed inside the service method ie updateUser but I have placed it here just for demonstration.

Advantage of assigning the returned savedEntity in Spring Data

I see in most of the coders save data(using spring data) as:
savedEntity = repo.save(savedEntity);
Long id = savedEntity.getId();
I am confused about why most of them assign back the returned value to the saved Entity while the following code also works exact(I have tested myself):
repo.save(savedEntity);
Long id = savedEntity.getId();
Did I miss some benefit of assigning back?
for example, let the entity be:
#Entity
public class SavedEntity {
#Id
private int id;
private String name;
//getter, setters, all arg-constructor, and no-arg constructor
}
Consider the object of SavedEntity is
SavedEntity entity = new SavedEntity(1,"abcd");
now for your first question,
SavedUser entity1 = repo.save(entity);
Long id = entity1.getId();
this entity1 object is the return object getting from the database, which means the above entity is saved in the database succesfully.
for the Second Question,
repo.save(entity);
Long id = entity.getId();//which you got it from SavedEntity entity = new SavedEntity(1,"abcd");
here the value of id is the integer you mentioned in place of id(the raw value).
Most of the time the id (primary key) is generated automatically while storing the entity to the database using strategies like AUTO, Sequence etc. So as to fetch those id's or autogenerated primary key values we assign back the saved entity.
For example:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
}
In this case you'll not pass the id externally but it will create a value for it automatically while storing the data to DB.

Transient and Persistent fields not being sent to JSP page

When sending an Object to a JSP page the persistent and transient fields are left out. I can see on the Java side these variables are filled in with data, but once it gets to the JSP page some of the values are missing, specifically every field that is not mapped to a column.
Group Entity
#Entity
#Table(name="groups")
#XmlRootElement
public class Groups {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="groupsSeqGen")
//TODO: I dont think H2 is having the sequences auto generated. Need to add these manually.
#SequenceGenerator(name="groupsSeqGen",sequenceName="groups_sequence", initialValue = 10, allocationSize = 100)
private Long id;
#Column(name="name")
private String name;
#Column(name="create_date")
private Date createDate;
#Column(name="owner_user")
private String ownerUser;
#Column(name="is_public")
private Boolean isPublic;
#Column(name="description")
private String description;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "ownerGroup")
private List<Books> books;
}
Request Mapping
#RequestMapping("/Mygroups")
public ModelAndView getMyGroup() {
ModelAndView mav=new ModelAndView();
mav.addObject("groups", appservice.findMyGroups()); //This returns the groups!
mav.setViewName("myGroups");
return mav;
}
My JSP page can read the groups. Just for an idea here is the console output when I print the object.
Groups [id=1, name=Club 1, createDate=2019-08-01 00:00:00.0, description=Club 1 desc, isPublic=true, ownerUser=user1]
What i've tried.
Adding #transient and #XMLTransient tags.
Joining columns differently.
Changing the Fetch type (This doesnt matter im just changing random things at this point)
The other Odd Part is when I write to the object with a form I can set these fields fine! Maybe its because Javascript is just setting the fields regardless of if it matches and when Java reads it in when it does match it works correctly?
I'm an idiot....
It didn't show up in my JavaScript console output because I skipped adding it to the ToString in my Groups class. Once I added it there. I realized I was just referencing it incorrectly in JavaScript.I think I was referencing it as "books" instead of "group.books" and didn't realize because I was printing the object and it wasn't there.

Spring MongoDB + QueryDSL query by #DBRef related object

I am using spring-data-mongodb and querydsl-mongodb to perform more flexible queries.
My application has users and orders.
An user can have multiple orders, so my models looks like this:
public class User {
#Id
private String id;
private String username;
//getters and setters
}
public class Order {
#Id
private String id;
#DBRef
private User user;
//getters and setters
}
As you can see, there is an has-many relationship between users and orders.
Each order is assigned to an user, and the user is stored in #DBRef public User user attribute.
Now, lets say that an user has 10,000 orders.
How can i make the query to get all orders that belongs to an specific user ?
I have the OrderRepository:
public interface OrderRepository extends MongoRepository<Order, String>,
QueryDslPredicateExecutor<Order> {
}
I tried this solution but it doesnt return anything:
QOrder order = new QOrder("order");
Pageable pageable = new PageRequest(0, 100);
return userRepository.findAll(order.user.id.eq(anUserId), pageable);
I need to use querydsl because i want to build a service that can query orders by more many prameters than userid. For example i want to get all orders that belongs to user with specific username.
No need for QueryDSL when searching by ID. In OrderRepository, create an interface method:
public List<Order> findByUser(String userId);
Request example: curl http://localhost:8080/orders/search/findByUser?userId=5a950ea0a0deb42729b570c0
* I'm stuck on how to query orders, for example, of all users from certain city (some mongo join with user and address).

Spring #PathVariable returns HTTP Status 404

What I'm trying to do retrieve a user by their id and also retrieve the modules assigned to them on a page. I have mapped the one to many relationship in the models.
User Model
#Entity
#Table(name = "user")
#Component
public class User implements Serializable
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="user")
private Set<Module> sModule = new HashSet<Module>();
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="manager")
private Set<Module> cModule = new HashSet<Module>();
Module model
#Entity
#Table(name = "modules")
#Component
public class Module implements Serializable
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id", insertable=false, updatable=false)
private User user;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="manager_id", insertable=false, updatable=false)
private User manager;
Controller for it -
#RequestMapping(value="/home/user_page/{userId}", method =
RequestMethod.GET)
public String showUserModules(#PathVariable("userId") String userId, ModelMap
map, HttpServletRequest request) {
map.addAttribute("cp", request.getContextPath());
map.addAttribute("user", userService.getWithModules(userId));
return "/home/user_page";}
When I try to open the user_page it returns an error showing:
The requested resource is not available
So how do I get the user and the required modules for them when I go their user page.
Edit: Stacktrace
WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP
request with URI [/professional/home/user_page] in DispatcherServlet
with name 'servlet-context'
I think the problem in your case coused by similar mapping for two methods in controllers. As I understood one of the methods has mapping like "/home/user/setting_page" and another one "/home/user/{userId}". That's why after you have added userId path variable controller tries to parse "setting_page" as userId, and you get this error because such user doesn't exists. Try to change one of this URL in order to make them unique.
EDIT:
Ok. As I see from your updates the reason of the problem is that Spring tries to find mapping for url "home/user_page" but it can't because you don't have method with such mapping in controllers. If you want to display JSP (or html) as a result of this method call then you should define InternalViewResolver in your config file and return name of the page. Here is tutorial where among other described how to use InternalViewResolver.

Resources