22

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.

5
  • You don't need to implement the persistable interface. Just use the inherited save and Spring Data will handle it for you. Commented Dec 28, 2014 at 5:07
  • @Rana_S how did you solve this issue? I have a quite similar issue: why saveAll() always inserts data instead of update it? Commented Dec 6, 2020 at 18:30
  • @catch23 I had version column in DB and @Version marked on entity field, which was missing when persisting. So had to populate the version field and worked. Saw your post, try adding version column and see how it reacts. Also, would suggest implementing better equals and hashCode instead of just id. Commented Dec 11, 2020 at 5:57
  • @Rana_S thanks for this response. Tried this approach. The same exception is thrown. However, version param is always null - because it is fetched from remote and this field is not presented. Thus, the entity is always treated like "new" Commented Dec 11, 2020 at 14:57
  • @catch23 So you’re saying you have version column? If so then Spring will evaluate this. Check this out saving Entities Strategies. If you are not using version then removing would do the work. Commented Dec 12, 2020 at 17:41

2 Answers 2

56

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!

Sign up to request clarification or add additional context in comments.

5 Comments

Yeah you are right. I had the Version field in the entity. I never knew Spring Data JPA would look for that field.
Thanks for this answer I came across the same issue!
In regards to the issue with @Version, I got around that by adding the Java Validation @Null annotation, assigning a low default value to the version field, and removing the setter for the version field. That way the field is never null. I am using an Instant class as the @Version field.
Thanks for the answer I put this column and I forget to set a default value when i insert from SQL, it took me one day to solve. Thanks mate.
I believe this exact behaviour is documented here: docs.spring.io/spring-data/jpa/reference/jpa/…
3

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.

2 Comments

Thank you @njjnex. But when you use the JpaRepository interface and call the save method from the Service class, you see that internally Spring Data JPA check if the entity is new or not. In this way when an entity implements Spring's Persistable interface, it overrides two methods: 1. Boolean isNew() and 2. Long getId(). But it's not working.
As I said I am getting into Spring Data Jpa and previously used own custom dao layers. I did the way you mentioned as above by passing the entityManager in dao implementation. That works fine but my question is why it doesn't work in the context of Spring data Jpa.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.