How to update one field in my Entity using JPA Repository

I have Entity with 3 fields: id, lastname and phoneNumber. I want to create method which works for update all fields or only one or two.

I use Hibernate and JPA Repository. When I try to update all fields everything works well but when for example i want to update only lastname without changing of phoneNumber I have in output null insted of old phoneNumber.

Here is my method from Controller:

@PutMapping("/students/update/{id}")
public String updateStudentById(@ModelAttribute Student student, @ModelAttribute StudentDetails studentDetails,
                                String lastname, String phoneNumber,
                                @PathVariable Long id) {

    Optional<Student> resultOptional = studentRepository.findById(id); 

    //Student result =resultOptional.get();
    resultOptional.ifPresent((Student result) -> {
           result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber());     result.getStudentDetails().setLastname(studentDetails.getLastname());
        studentRepository.save(result);
    });
    return "Student updated";
}

The class for update:

@DynamicUpdate
@Entity
public class StudentDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name="lastname")
    private String lastname;
    @Column(name="phone_number")
    private String phoneNumber;

    public StudentDetails() {
    }

    public StudentDetails(Long id, String lastname, String phoneNumber) {
        this.id = id;
        this.lastname = lastname;
        this.phoneNumber = phoneNumber;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
}

The class which has relation with StudentDetails:

@Entity
@Table(name = "student")
@DynamicUpdate
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;
    @Column(name = "email")
    private String email;

    //@OneToMany(mappedBy = "student")
    @ManyToMany
    @JoinTable(name="course_student",joinColumns = @JoinColumn(name="student_id"),
    inverseJoinColumns = @JoinColumn(name="course_id"))

    private List<Courses> courses;

    @OneToOne(cascade = CascadeType.ALL)
   // @JoinColumn(name="studen/_details_id") // with this we have dobule student_details column
    private  StudentDetails studentDetails;


    public List<Courses> getCourses() {
        return courses;
    }

    public void setCourses(List<Courses> courses) {
        this.courses = courses;
    }

    public StudentDetails getStudentDetails() {
        return studentDetails;
    }

    public void setStudentDetails(StudentDetails studentDetails) {
        this.studentDetails = studentDetails;
    }

    // Methods for StudentViewController
    public String getLastname(){
        return studentDetails.getLastname();
    }
    public String getPhoneNumber(){
        return studentDetails.getPhoneNumber();
    }

    public Student() {
    }

    public Student(String name, String email, StudentDetails studentDetails) {
       // this.id = id;
        this.name = name;
        this.email = email;
        this.studentDetails = studentDetails;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                '}';
    }

}

I was looking for solution and I added @DynamicUpdate but still it doesn't work.

Your code works properly. When you only provide lastName parameter in your request, then the phoneNumber parameter will be mapped to null so you override the phoneNumer property in your entity with this null value.

Change the code in the following way:

resultOptional.ifPresent((Student result) -> {
   if(studentDetails.getPhoneNumber()!=null) {
     result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber());     
   }
   if(studentDetails.getLastname()!=null) {
     result.getStudentDetails().setLastname(studentDetails.getLastname());
   }
   studentRepository.save(result);
});

Unfortunately it raises an other problem: How will you delete these fields? (How can you set them explicitly to null? )

A possible solution if you check for the "" (empty string) and set the property to null if the parameter is empty string.

It will be a quite messy code anyway...

You should consider using the Spring Data Rest package. It automatically creates all of the standard REST endpoints for your entities and handles all of these PUT/PATCH/POST/DELETE issues out of the box.

In the latter case, it may require too many resources to assemble an entity object just to change the value of a single field, for instance. Using Spring Data JPA, one can easily create update queries with JPQL that translate into SQL update queries. This can be done as follows.

why don't you just set the params of your request in you setters?

resultOptional.ifPresent((Student result) -> {
result.getStudentDetails().setPhoneNumber(phoneNumber);
result.getStudentDetails().setLastname(lastname);
studentRepository.save(result);
});

For entity attribute that should never modify after the entity is persisted (insert-only columns), you should use @Column(updatable = false). Calculated entity attributes The Post entity createdOn attribute is a Timestamp which we might want to print in application logs using the ISO_DATE_TIME DateTimeFormatter .

You forget set @OneToOne mapping in StudentDetails - StudentDetails also need field of type Student which will be annotated @OneToOne.

Also you have to ensure, that all of entity fields will be filled - read more about fetch types.

In this short tutorial, we'll learn how to create update queries with the Spring Data JPA @Query annotation. We'll achieve this by using the @Modifying annotation. First, we'll refresh our memory and see how to make queries using Spring Data JPA. After that, we'll deep dive into the use of @Query and @Modifying annotations. Finally, we'll see

In this tutorial, we'll see how we can map one entity that contains embedded properties to a single database table. So, for this purpose, we'll use the @Embeddable and @Embedded annotations provided by the Java Persistence API (JPA).

Here we specify the entity's class and the entity id's class, MerchandiseEntity and Long. When an instance of this repository is instantiated, the underlying logic will automatically be in place for working with our MerchandiseEntity class. So with very little code, we're already ready to start using the save() method. 4.

The first half of this tutorial introduced fundamentals of the Java Persistence API and showed you how to configure a JPA application using Hibernate 5.3.6 and Java 8. If you've read that tutorial

Comments
  • try use select and update pattern
  • Take a look at: stackoverflow.com/questions/39741102/…
  • Hey, i checked it but any of solutions helps..
  • Thanks a lot ! :)
  • I added in StudentDetalis.class: @OneToOne(fetch = FetchType.EAGER) private Student student; and in Student.class: @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) private StudentDetails studentDetails; but does not help at all.