Insert instead of update with Spring Data JPA

I have a simple model. Entity A has one-to-many relation with entity B. They are linked using an association table. My use case is fetching/inserting/updating entity in simple web application:

  1. Fetch entity A as JSON.
  2. Put entity A (even unchanged) to save.
  3. Constraint violation: hibernate tries to insert already existing row into association table.

Why is this happening? Hibernate has everything what is needed to determine if merge or persist should be done on parent and its children. Why it doesn't check if the children are already linked with the parent?

Parent.java:

package com.example;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;

import lombok.EqualsAndHashCode;

@Entity
@EqualsAndHashCode(exclude = { "id" })
public class Parent {

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

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Child> children = new HashSet<>();

    public Long getId() {
        return id;
    }

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

    public Set<Child> getChildren() {
        return children;
    }

    public void setChildren(Set<Child> children) {
        this.children = children;
    }

}

Child.java

package com.example;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

import lombok.EqualsAndHashCode;

@Entity
@EqualsAndHashCode(exclude = { "id" })
public class Child {

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

    private Long value;

    public Long getId() {
        return id;
    }

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

    public Long getValue() {
        return value;
    }

    public void setValue(Long value) {
        this.value = value;
    }

}

ParentService.java:

package com.example;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

    @Service
    public class ParentService {

        @Autowired
        private ParentRepository parentRepository;

        @Transactional
        public Parent save(Parent parent) {
            return parentRepository.save(parent);
        }

    }

ParentRepository.java:

package com.example;

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

@Repository
public interface ParentRepository extends JpaRepository<Parent, Long>{

}

What is interesting when I add @Version properties then everything works, but I cannot find explanation why. The other solution is to fetch oldParent by id, just for the sake of fetching - then it also works, even without @Version, but when I will get oldParent children and initialize it using size() then again, undesired inserts are coming. Why those things even work? It is something related to Hibernate or Spring Data JPA repositories?

Answers


You wrote : 'They are linked using an association table'

Are you shure ?

Default mapping for ManyToOne is an additonal foreign key in the many side. In your case the table child should have an additional field 'parent_id' and there is no additional association table. Every child can be associated exactly to one parent.

Maybe you can post the Exception of 'hibernate tries to insert already existing row into association table' ?


Need Your Help

What is going on with my PHP class variable?

php

I made this stupidly simple PHP file containing