Spring Data JPA
CRUDRepository
Update vs SaveOrUpdate
Java Persistence
Data Management Options

Update or SaveorUpdate in CRUDRespository, Is there any options available

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

CrudRepository does not have separate update() or saveOrUpdate() methods like older Hibernate-focused APIs. In Spring Data, the normal entry point is save(), and what actually happens depends on the entity state, the identifier, and whether you are modifying a managed entity inside a transaction.

How save() Behaves

For CrudRepository, save() is the standard method for both inserts and updates.

java
public interface UserRepository extends CrudRepository<User, Long> {
}
java
1@Entity
2public class User {
3
4    @Id
5    @GeneratedValue(strategy = GenerationType.IDENTITY)
6    private Long id;
7
8    private String email;
9
10    protected User() {
11    }
12
13    public User(String email) {
14        this.email = email;
15    }
16
17    public Long getId() {
18        return id;
19    }
20
21    public String getEmail() {
22        return email;
23    }
24
25    public void setEmail(String email) {
26        this.email = email;
27    }
28}
java
1User created = userRepository.save(new User("[email protected]"));
2
3created.setEmail("[email protected]");
4userRepository.save(created);

If the entity is new, JPA persists it. If the entity already exists, Spring Data delegates to the persistence provider and the row is updated. That is the closest equivalent to a "save or update" operation in this abstraction.

Managed Entities and Dirty Checking

In many cases you do not need to call save() for every update if the entity is already managed in the current transaction. JPA dirty checking will detect field changes and flush them automatically at commit time.

java
1@Service
2public class UserService {
3
4    private final UserRepository userRepository;
5
6    public UserService(UserRepository userRepository) {
7        this.userRepository = userRepository;
8    }
9
10    @Transactional
11    public void changeEmail(Long id, String newEmail) {
12        User user = userRepository.findById(id)
13                .orElseThrow();
14
15        user.setEmail(newEmail);
16    }
17}

This is still an update, even though save() is never called. Once the entity is managed by the persistence context, changing its state inside a transaction is enough.

That distinction matters because many developers coming from Hibernate's saveOrUpdate() think they must call a repository method after every field assignment. With JPA, that is often unnecessary.

When a Custom Update Query Is Better

Sometimes you do not want to load the entity first. Maybe you need to update one column on many rows, or you want to avoid fetching an object only to change a single value. In those cases, declare a custom modifying query.

java
1public interface UserRepository extends CrudRepository<User, Long> {
2
3    @Modifying
4    @Query("update User u set u.email = :email where u.id = :id")
5    int updateEmailById(Long id, String email);
6}
java
1@Transactional
2public void fastUpdate(Long id, String email) {
3    int updated = userRepository.updateEmailById(id, email);
4    if (updated == 0) {
5        throw new IllegalArgumentException("User not found");
6    }
7}

This is the closest thing to an explicit repository-level update operation.

save() Versus Hibernate saveOrUpdate()

Older Hibernate APIs exposed methods such as saveOrUpdate() directly on the session. Spring Data intentionally presents a smaller abstraction surface. Instead of mirroring every Hibernate method, it gives you repository methods and lets the JPA provider handle persistence state under the hood.

That means the practical options are:

  • 'save() for create or detached-entity update flows'
  • transactional dirty checking for managed entities
  • '@Modifying queries for direct database updates'

Those three patterns cover almost every common use case.

Common Pitfalls

One mistake is assuming save() always issues an SQL update immediately. It does not. Depending on the transaction and flush mode, the SQL may be delayed until flush or commit.

Another issue is updating detached entities without understanding what fields are populated. If you create a partially filled object with an existing identifier and call save(), you can accidentally overwrite columns with null values. Load the existing entity first unless you are sure the object is complete.

Developers also sometimes forget @Transactional on methods that rely on dirty checking. Without a proper transaction, the persistence context may not behave the way you expect.

Finally, custom update queries bypass normal entity state tracking. If the same entity is already loaded in the persistence context, its in-memory state can become stale unless you clear or refresh it.

Summary

  • 'CrudRepository does not provide separate update() or saveOrUpdate() methods.'
  • Use save() for standard create and update flows.
  • Let JPA dirty checking handle updates to managed entities inside a transaction.
  • Use @Modifying queries when you need direct SQL-style updates without loading the entity.
  • Be careful with detached entities and partial data, because save() can overwrite existing values.

Course illustration
Course illustration

All Rights Reserved.