Spring Data JPA inserting instead of Update

Hi I am new to Spring Data JPA and I am wondering even though I pass the Id to the entity, the Spring data jpa is inserting instead of merge. I thought when I implement the Persistable interface and implement the two methods:

public Long getId();
public Boolean isNew();

It will automatically merge instead of persist.

I have an entity class called User like:

@Entity
@Table(name = "T_USER")
public class User implements Serializable, Persistable<Long> {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "USER_ID")
   private Long id;

   @Column(name = "CREATION_TIME", nullable = false)
   private Date creationTime;

   @Column(name = "FIRST_NAME", nullable = false)
   private String firstName;

   @Column(name = "LAST_NAME", nullable = false)
   private String lastName;

   @Column(name = "MODIFICATION_TIME", nullable = false)
   private Date modificationTime;

And have another class

@Entity
@Table(name = "T_USER_ROLE")
public class UserRole implements Serializable, Persistable<Long> {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long roleId;

   @Column(name = "ROLE_NAME")
   private String userRole;
}

I have a custom repository called UserRepostory extending JpaReopistory. I am hitting the save for merge and persist as I see the implementation demonstrate that Spring Data Jpa uses above two methods to either update or insert.

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

I have been trying to figure out but didn't get any clue. Maybe you guys can help.


Solution 1:

I ran into this issue, tried to implement Persistable to no avail, and then looked into the Spring Data JPA source. I don't necessarily see this in your example code, but I have a @Version field in my entity. If there is a @Version field Spring Data will test that value to determine if the entity is new or not. If the @Version field is not a primitive and is null then the entity is considered new.

This threw me for a long time in my tests because I was not setting the version field in my representation but only on the persisted entity. I also don't see this documented in the otherwise helpful Spring Data docs (which is another issue...).

Hope that helps someone!

Solution 2:

By default Spring Data JPA inspects the identifier property of the given entity. If the identifier property is null, then the entity will be assumed as new, otherwise as not new. It's Id-Property inspection Reference

If you are using Spring JPA with EntityManager calling .merge() will update your entity and .persist() will insert.

@PersistenceContext
private EntityManager em;

@Override
@Transactional
public User save(User user) {

    if (user.getId() == null) {
        em.persist(user);
        return user;
    } else {
        return em.merge(user);
    }
}

There is no need to implement the Persistable interface.