Spring Data JPA: insert or update

Tom Liu
3 min readFeb 8, 2023

If you are reading this article, you must have noticed that by default, the save() method from JpaRepository interface saves the DB entity regardless whether the row exists or not.

This is very convenient, but it also cause a problem in some scenarios. Sometimes, when we insert a new row more than once, we want to see something like DataIntegrityViolationException if the row already exists in the table.

Here, I present you 2 approaches to solve the problem. The first one is the easy and preferred one. The second is to give you a taste of nativeQuery

Simple and Easy Approach

The entity implements Persistable. When we want to save the entity byinsert, we keep the isNew field as true; when we want to save the entity by update , we set the isNew as false.

import org.springframework.data.domain.Persistable;

import javax.persistence.*;

@Entity
@Table(name="my_entity")
public class MyEntity implements Persistable<Long> {
@Id
@Column
private long id;

@Column
private String value;

@Transient
private boolean isNew = true;

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

public void setIsNew(boolean isNew) { this.isNew = isNew; }
public boolean isNew() { return isNew; }

public void setValue(String value) { this.value = value; }
public String getValue() { return this.value; }
}

In the DAO, you do nothing.

import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;

@Repository
public interface MyEntityRepo extends JpaRepository<MyEntity, Long> {}

When using DAO, here is what we do.

@Autowired
private MyEntityRepo repo;

...
MyEntity me = new MyEntity();
me.setId(0);
me.setValue("my value");
// here, the isNew is true by default. An insert statement is issued.
// if you save twice, you got DataIntegrityViolationException
repo.save(me);

...
me.setIsNew(false);
// here, the isNew is false. An update statement is issued.
// if you save twice, you are OKey
repo.save(me);

...
MyEntity me = new MyEntity(0);
// if you don't set it to false, delete statement won't be issued at all
me.setIsNew(false);
repo.delete(me);

Important: when using the Persistable interface, if we want to delete an entity, we have to set isNew to false too, otherwise, the delete operation is ignored. When isNew is true, Spring Data JPA framework regards this entity as a new entity, which is an entity that is not managed by the EntityManager i.e. it is a detached entity.

A Taste of Native Query

This is presented here to give you a taste of nativeQuery. Sometime, we need to customize the DAO that implements JpaRepository interface. Allow me to use this as an example of custom query - add an insert() function to the DAO interface.

Suppose for some mysterious reason, you don’t want that the MyEntity class implements Persistable. You want to have an insert() method in MyEntityRepo to do insertion.

Here you go…

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
public interface MyEntityRepo extends JpaRepository<MyEntity, Long> {
@Modifying
@Transactional
@Query(value = "INSERT INTO MyEntity VALUES " +
"( " +
":#{#me.id}, " +
":#{#me.value} "
")", nativeQuery = true)
public abstract void insert(@Param("me") MyEntity me);
}

Several things to highlight:

  • The table name matches the entity nameMyEntity
  • The method’s signature has an abstract keyword. That is right, we are using an abstract method and it works
  • The method parameter has a Param annotation, whose value is the one used in the Query
  • Spring Data JPA use :#{#...} to get the value of the entity and use it in the Query. However, I think :-{} is better, because it visualized my face when I saw the unbelievable :#{#...}

Okey, now, in where you want to use the DAO, do whatever you want to do.

@Autowired
private MyEntityRepo repo;
...
MyEntity me = new MyEntity();
me.setId(0);
me.setValue("my value");
// insert it, if you insert twice, you got ConstraintViolationException
repo.insert(me);
...

// save it if you don't care to update an existing one
me.setValue("new value");
repo.save(me);

...
// delete it. This works just fine
repo.delete(me);

Have fun!

--

--