简体   繁体   中英

Spring JPA repository delete method doesn't work

I can't get Spring JPA to delete bar entity with jpa repository delete method.
Can anyone see where the problem is?

doesn't work:

barRepository.delete(bar.id);

Jpa repository:

public interface BarRepository extends JpaRepository<Bar, Integer>

Hibernate entity mappings (only relevant parts):

@Entity(name = "foo")

@OneToMany(fetch = FetchType.EAGER, mappedBy = "foo", cascade = CascadeType.ALL, orphanRemoval = true)
@Fetch(FetchMode.SELECT)
private List<Bar> bars = new ArrayList<>();

@Entity(name = "bar")

@ManyToOne(optional = false)
@JoinColumn(name = "foo_id")
@Fetch(FetchMode.SELECT)
private Foo      foo;

EDIT : git repo with minimal reproducible example. HibernateDeleteApplicationTests.java contains test case.

https://github.com/matijaivanus/hibernate-delete-problem

The "problem" is your mapping. Your collection is retrieved eagerly. Now why would that be an issue. The deleteById in Spring Data JPA first does a findById which in your case loads the Bar and the Foo and eagerly the collection inside Foo .

Now the Bar is attempted to be deleted but due to it being still attached and referenced by another entity it would be persisted again, hence the delete is cancelled.

To solve you have 3 options

  1. Delete using a query and write your own query method for this
  2. Properly remove the Bar from Foo , set the relation to null and then persist Foo or delete Bar .
  3. Mark either side of the assocation lazy (this still can fail due to 1).

Either of these will work. The first one because it bypasses the JPA caching mechanisms, the second because now the association has been cut. The third can work but if the objects have already been loaded run into the same issue as 1.

What if you use

barRepository.delete(bar);

or

barRepository.deleteById(bar.id);

I can see that you try to pass bar.id as a parameter of delete method but it should be entity: void delete(T entity)

[EDIT]

It happens because you have bidirectional relationship and you need to update both sides of the relationship if two sides are persisted - attached to the current session

How to fix it?

You can add @PreRemoved annotaded method to the Bar class to synchronize both sides just before Bar is removed:

@PreRemove
public void preRemove() {
    this.foo.getBars().clear(); //of course this is just an example and probably you should have more complicated logic here
    this.foo = null;
}

Second solution is to create @Transactional method to save Foo and Bar is a separate transaction:

@Service
public class FooService {

    @Transactional
    public Foo save() {
        Foo foo = new Foo();
        foo.setTitle("aaa");

        Bar bar = new Bar();
        bar.setName("fas");
        bar.setFoo(foo);

        foo.getBars().add(bar);

        return fooRepository.save(foo);
    }
} 

And now use it to save your entities

@BeforeEach
public void before() {      
    this.foo = fooRepository.save(foo);
}

U can try using: barRepository.deleteById(bar.id)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM