Find the updated fields on update request - spring

I have a user entity as
#Entity
public class User {
#Id
private Long id;
private String name;
private String address;
// getter / setter
}
And controller method like:
#PutMapping(value = "/user")
public ResponseEntity<?> updateUser(#RequestBody User user) {
userRepository.save(user);
// ...
}
Now What I am trying to do is finding the field that is being updated.
Example:
If the only name is present I need some message like "Updated field is name".
Is there any better way other than comparing the fields one by one with the database stored values.

you need getUser method. for example: userRepository.getUser(user.id)
then you return the result

Related

Spring Data MongoRepository save causing Duplicate Key error

Here is the Entity:
#Document
#Data
public class ApplicationUser {
private String name;
#Indexed(unique = true)
private String email;
private String organization = null;
// other fields
}
I fetch this user using their email and then change their name. I use the autowired instance of ApplicationUserRepository.
ApplicationUser applicationUser = applicationUserRepository.findByEmail("abc#gmail.com");
applicationUser.setName("John Doe 2");
Then I try to update this entity again in the database:
applicationUserRepository.save(applicationUser);
I get a duplicate key error on the field email. Why is this happening?
As far as I get from the documentation, the save method updates the same document if the ObjectId is the same. Since I haven't changed the objectId then why is it trying to create a new ApplicationUser during saving?
I got the solution.
When creating the entity, I have to explicitly declare the Id.
Here is the Entity:
#Document
#Data
public class ApplicationUser {
#Id
private ObjectId _id;
private String name;
#Indexed(unique = true)
private String email;
private String organization = null;
// other fields
}
I had similar issue where I was retrieving by id and then trying to update the retrieved POJO and then save it back with MongoRepository.save() call. It was on MongoDB 4.x with Spring Boot 2.1.0. I added the #Transactional annotation to my service method and everything worked like a charm. The duplicate key exception on id field was resolved.

Spring - How to use #RequestParam parameter of a Controller in a Custom Validator class?

I've got a problem about validation in Spring MVC with Hibernate.
I want a validator that valid user input, but the validation must be done out of the controller, so, in a separate validation class.
The situation: this is the head of my controller in which I want to do the validation. I need that id to retrieve a list of Booking of a specific car.
#PostMapping(value = "/rent")
public ModelAndView vehicleRent(#ModelAttribute("newBooking") Booking booking, BindingResult bindingResult, #RequestParam("id") long id) {
But if i want to separate the logic out of this controller creating a custom validator, i have this result:
public class BookingValidator implements Validator {
#Autowired
VehicleBO vehicleBo;
#Override
public boolean supports(Class<?> type) {
return Booking.class.isAssignableFrom(type);
}
#Override
public void validate(Object o, Errors errors) {
Booking booking = (Booking) o;
//other code
rejectIfBookingExists(booking, 0, errors, "validation.booking.startdate.exists");
}
}
public boolean rejectIfBookingExists(Booking booking, long id, Errors errors, String key){
boolean exists = false;
List<Booking> vehicleBookings = vehicleBo.getVehicleBookings(id);
if (booking != null || booking.getStartDate() != null || booking.getFinishDate() != null) {
for (Booking b : vehicleBookings) {
if (booking.getStartDate().before((b.getFinishDate())) || booking.getStartDate().equals(b.getFinishDate())) {
errors.rejectValue("startDate", key);
exists = true;
break;
}
}
}
return exists;
}
}
In this way I cannot retrieve the list because i don't have the required id, could you explain me how to do that? Or,there are other ways to solve this problem?
Thanks!
EDIT:
This is the Booking class, as you can see it has a Vehicle object mapped inside
#Entity
public class Booking implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne
#JoinTable(name="user_booking", joinColumns={#JoinColumn(name ="booking_id", referencedColumnName ="id")},
inverseJoinColumns={#JoinColumn(name ="user_id", referencedColumnName ="id")})
private User user;
#ManyToOne
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
#DateTimeFormat(pattern = "dd/MM/yyyy")
private Date startDate;
#DateTimeFormat(pattern = "dd/MM/yyyy")
private Date finishDate;
public Booking() {
//getter and setter and other code
}
Any ideas?
Why don't you simply map the vehicle id as booking.vehicle.id in your form? Provided Vehicle has a no-arg constructor (which it probably does, being an entity), the Booking should come back in the POST request handler with an instantiated Vehicle, along with its id property set. You should then be able to access booking.vehicle.id from wihtin the validator.
You can use an input[type=hidden] for the booking.vehicle.id field. In your GET request for the view with the form, simply inject the vehicle id as a #PathVariable and copy it to your model, so that you could reference the value inside the form.

Values of #PathVariable and #ModelAttribute overlapping

I have an User object stored in the session with #SessionAttributes. And a straight-forward method decorated with #ModelAttribute in order to initialize it whenever the session's value is null.
User class:
#Entity
#Table( name="USER")
public class User implements java.io.Serializable {
private Long id;
private String username;
private String password;
....
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name ="ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
...
Controller:
#RequestMapping("/item")
#Controller
#SessionAttributes({"user"})
public class MyController {
#ModelAttribute method:
#ModelAttribute("user")
public User createUser(Principal principal) {
return userService.findByUsername(principal.getName());
}
It all seems to work as expected except in this particular method:
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String showItem(#PathVariable("id") Long id, #ModelAttribute("user") User user,
Model uiModel) {
...
}
The problem is that User.id is being set with #PathVariable("id"). I believe I ran into this with #RequestParam too. I'm assuming that's because both have the same name and type. After reading Spring's documentation (see below) I'm assuming this is expected behavior:
The next step is data binding. The WebDataBinder class matches request parameter names — including query string parameters and form fields — to model attribute fields by name. Matching fields are populated after type conversion (from String to the target field type) has been applied where necessary.
However, I would think this scenario is fairly common, how are other people handling this? If my findings are correct and this is expected behavior (or bug), this seems to be very error prone.
Possible solutions:
Change #PathVariable("id") to #PathVariable("somethingElse"). Works but it's not as straightforward with #RequestParam (e.g. I don't know how to change jqgrid's request parameter id to something else but this is another issue).
Change #PathVariable("id") type from Long to Int. This will make User.id and id types differ but the cast to Long looks ugly :)
Don't use #ModelAttribute here and query the DB for User again. Not consistent with other methods and involves redundant DB calls.
Any suggestions?
How about this approach -
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String showItem(#PathVariable("id") Long id,
Model uiModel) {
User user = (User)uiModel.asMap().get("user");
...
}
use #SessionAttribute
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String showItem(#PathVariable("id") Long id, #SessionAttribute("user") User user,
Model uiModel) {
...
}

spring data mongodb MongoRepository.save(T entity) method not working?

The code is listed below:
#Document
#XmlRootElement
public class User {
#Indexed(unique=true)
private String username;
private String firstName;
private String lastName;
private String password;
...... omit setters and getters
}
public interface UserRepo extends MongoRepository<User, String>{
}
public User update(User user) {
User existingUser = userRepo.findByUsername(user.getUsername());
if (existingUser == null) {
return null;
}
existingUser.setFirstName(user.getFirstName());
existingUser.setLastName(user.getLastName());
return userRepo.save(existingUser);
}
when update method invoked, the finds the user based on username and finishes without any exceptions, the returned User obj has all updated value but the underlying mongodb document is not changed! Can anyone help? Thanks.
you need an Id field with #Id annotation

How to avoid binding of some fields?

Using the validation from PlayFramework and the data binding, is it possible, via (maybe) a decorator, to avoid binding some fields?
For example, I have this model :
class User extends Model {
#Required
#Email
public String email;
// I'd like to avoid setting this
public String password;
}
and in my model :
Store store = new Store();
Binder.bindBean(params.getRootParamNode(), store, null);
validation.valid(store);
If the user POST email AND password, password will be also set, but I don't want to.
How can I do that?
If you don't want to persist the data, but you want it to be bound as part of the automatic binding, then you can use the #Transient annotation...
Example
class User extends Model {
#Required
#Email
public String email;
// I'd like to avoid setting this
#Transient
public String password;
}
If you don't want it to be bound at all, then use the NoBinding annotation
#play.data.binding.NoBinding
Example
public class User extends Model {
#NoBinding("profile") public boolean isAdmin;
#As("dd, MM yyyy") Date birthDate;
public String name;
}

Resources