12

I am using spring data-jpa. I want update only one column.

My repository is;

public interface UserRepository extends JpaRepository<User,Long> {
}

my Service is;

public User save(User user) {
    return userRepository.save(user);
}

my Entity;

@Entity
@DynamicUpdate(true)
public class User implements Serializable {
    // column definitions, etc.
}

How can I update only one column in User?

Mohsen
  • 4,536
  • 2
  • 27
  • 49
Ravi Jangid
  • 291
  • 1
  • 4
  • 15

2 Answers2

20

First of all I want to explain why @DynamicUpdate is not working for you. So I should notice how @DynamicUpdate works:

The @DynamicUpdate annotation is used to specify that the UPDATE SQL statement should be generated whenever an entity is modified. By default, Hibernate uses a cached UPDATE statement that sets all table columns. When the entity is annotated with the @DynamicUpdate annotation, the PreparedStatement is going to include only the columns whose values have been changed.(more details)

For example assume that User has a property called name.

So if for the first time you saved user with a defined name, now you are passing null as the value @DynamicUpdate will assume it as a change and trying to update name to null.

First solution:

As the first solution, if you want @DynamicUpdate works, first you should fill all other User properties with old values, then you can save it.

Pros: The generated SQL query just updates the property you want.

Cons: Hibernate has to generate the corresponding SQL string each time and there is thus a performance cost on the Hibernate side.

Second solution:

You can update User with custom query:

@Modifying
@Query("UPDATE User SET name=(:name) WHERE id=(:id)")
public void updateName(@Param("name")String name, @Param("id")Long id);

Third solution:

As the last solution I can suggest you to use updatable = false. This will fill the property on the very first moment the entity inserted.

  @Column(name = "create_date", nullable = false, updatable = false)
    private Instant createDate;
Mohsen
  • 4,536
  • 2
  • 27
  • 49
11

Your problem is due to your passing of an entirely new User entity hence Hibernate cannot use a cached version that has been already fetched from database and decide which columns to update dynamically.

So, try to do the following to confirm the correct behaviour of @DynamicUpdate;

In service;

@Transactional
public User save(User newUser) {
    User currentUser = userRepository.get(newUser.getId());
    // handle merging of updated columns onto currentUser manually or via some mapping tool
    currentUser.setName(newUser.getName());
    return userRepository.save(currentUser);
}

With the above logic, together with dynamic update annotation, you should be able to see the update of only name column, unless you have some audit enabled, or using @Version'ed column.

If the updated columns are always same, then it is better to use updatable = false for the columns that are not target for the update in their @Column definitions, for using @DynamicUpdate is very inefficient since it generates each sql anew, never utilizing cached sqls. But beware using this feature, since you will be unable to update these columns at all.

I don't recommend using @Query unless you have a case where native JPA/Hibernate is insufficient, but if you have a use-case to update a target set of columns only, it is the best choice, and the most efficient.

If the updated columns are many and can vary greatly, either define a manual mapping logic between newUser to the currentUser, or utilize some mapping tools such as Orika, I have a similar automated structure where hundreds of entities are patched through these mappers, allowing an extremely generic way to handle many CRUD operations.

buræquete
  • 14,226
  • 4
  • 44
  • 89
  • 1
    I want to use dynamic update for update profile of user,if user has many fields then its difficult to set all the values. – Ravi Jangid Nov 14 '18 at 03:53
  • @RaviDeveloper you don't need to set other fields, after you do a `repository.get()`, you will have the existing `User` from db, just update its `profile` column & do a `save()`, be aware all of this should be in a `@Transactional` method, as it is in my answer. With `@DynamicUpdate` it should work, and update only the changed column; `profile` – buræquete Nov 14 '18 at 03:56
  • @RaviDeveloper but if this is a use-case, meaning, you will always update "only the profile of a User", then it is better to use a `@Query`, since `@DynamicUpdate` is comparatively slower, but should still be OK – buræquete Nov 14 '18 at 04:03
  • @RaviDeveloper it should be annotated on your `Entity`, you use it correctly in your example, though no need to pass boolean value to it, since by default it is `true`. If it does not work, please show what kind of SQL is being executed with your current implementation, maybe there is another issue with hibernate config etc – buræquete Nov 14 '18 at 04:06