Issue
I am developing a simple application by spring boot. I need to restrict the user to be able to only update the name, not all the filed that related to user data but unfortunately, my code has a problem that if someone sends a data in Json format and changes the age or any other field it will be updated but as I told I need the user to be able to change the only name not any other field. I have to mention I am using JPA repository and spring data
my controller
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
StudentRepository repository;
// method i user to only update the name field
@PatchMapping("/pattt/{id}")
public ResponseEntity partialUpdateName(
@RequestBody Student partialUpdate,
@PathVariable("id") String id
){
Student.save(partialUpdate, id);
return ResponseEntity.ok(repository.save(partialUpdate));
};
}
JPA repository
@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {}
Student class
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue
private int id;
private String name;
private int age;
private String emailAddress;
public Student() { }
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public Student(int id, String name, int age, String emailAddress) {
this.id = id;
this.name = name;
this.age = age;
this.emailAddress = emailAddress;
}
public static void save(Student partialUpdate, String id) {
partialUpdate.setName(id);
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getEmailAddress() {
return emailAddress;
}
}
Solution
The best solution for the future is to add a DTO layer to your application and use it to map to your object. See example below.
public class StudentDto {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Then you can map this to your model by Mapstruct:
@Mapper
public abstract class StudentMapper {
public static final StudentMapper INSTANCE =
Mappers.getMapper(StudentMapper.class);
@Mapping
Student studentDtoToStudent(StudentDto studentDto);
}
Mapstruct dependencies:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.0.Final</version>
</dependency>
You will be able to hide your internal structure from outside world.
In your conroller:
public ResponseEntity partialUpdateName(
@RequestBody StudentDto partialUpdate,
@PathVariable("id") String id)
{
Student student =
StudentMapper.INSTANCE.studentDtoToStudent(partialUpdate);
}
The last line will give you a safe student model which you can then save
- Quick solution In your controller:
public ResponseEntity partialUpdateName(
@RequestBody Student partialUpdate,
@PathVariable("id") String id)
{
Optional<Student> optionalStudent = repository.findById(id);
if(optionalStudent.isPresent() && partialUpdate!=null) {
Student current=optional.get();
current.setName(partialUpdate.getName());
return ResponseEntity.ok(repository.save(current));
}
/* return an error */
}
Answered By - Oktay Alizada