简体   繁体   中英

JPA bidirectional OneToMany relashionship - cant delete child

In a bidirectional OneToMany relationship, how can I delete the child without calling ALL the children the parent has?

Since my Application is a bit to big I created a sample project to recreate the problem. I Have 2 entities, a Parent and a Child.

Parent:

package com.example.entity;

import javax.persistence.*;
import java.util.List;

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(
        mappedBy      = "parent",
        cascade       = CascadeType.ALL,
        fetch         = FetchType.LAZY,
        orphanRemoval = true
    )
    private List<Child> children;
}

Getters and Setter not shown!

Child:

package com.example.entity;

import javax.persistence.*;

@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "parent_id")
    private Parent parent;
}

Getters and Setter not shown!

I also have 2 Repositorys:

ChildRepository:

package com.example.dao;

import com.example.entity.Child;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ChildRepository extends JpaRepository<Child, Long> {}

ParentRepsitory: Looks exactly the same.

Now I created a test to reproduce the problem:

RepositoryTest:

package com.example;

import com.example.dao.ChildRepository;
import com.example.dao.ParentRepository;
import com.example.entity.Child;
import com.example.entity.Parent;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.transaction.Transactional;
import java.util.Arrays;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RepositoryTest {
    @Autowired
    private ChildRepository childRepository;

    @Autowired
    private ParentRepository parentRepository;

    @Test
    @Transactional
    public void test() {
        Parent parent = new Parent();
        Child  child1 = new Child();
        Child  child2 = new Child();

        parent.setChildren(Arrays.asList(child1, child2));

        Assert.assertEquals(0, parentRepository.count());
        Assert.assertEquals(0, childRepository.count());

        parentRepository.save(parent);

        Assert.assertEquals(1, parentRepository.count());
        Assert.assertEquals(2, childRepository.count());

        childRepository.delete(child1);

        Assert.assertEquals(1, parentRepository.count());
        Assert.assertEquals(1, childRepository.count());
    }
}

However the Child will never be deleted and the count will still be 2! I've read many questions about more or less the same problem, the answer was always the same:

parent.getChildren().remove(child1);
parentRepository.save(parent);

That cannot be the way to do it? If my parent has thousands of children I have to get ALL of them from the database to delete one? And the most awkward thing of them all, why the hell is no error thrown whatsoever if the entity is not getting deleted? Whats the right way to do it then? Remove the list from the parent completly and always call the children manually so there will be no foreign key? Im trying to solve this since breakfast now and i am moving in circles.

Edit: added hibernate log:

Hibernate: select count(*) as col_0_0_ from parent parent0_
Hibernate: select count(*) as col_0_0_ from child child0_
Hibernate: insert into parent (id) values (default)
Hibernate: insert into child (id, parent_id) values (default, ?)
Hibernate: insert into child (id, parent_id) values (default, ?)
Hibernate: select count(*) as col_0_0_ from parent parent0_
Hibernate: select count(*) as col_0_0_ from child child0_
Hibernate: select count(*) as col_0_0_ from parent parent0_
Hibernate: select count(*) as col_0_0_ from child child0_

That child is detached instance(not persistent object), which if you debug, it should not have its id.

My question is why did you create new object just to delete it? if you can determine which child will be deleted before persist it, why not exclude it from the add( Arrays.asList(child1, child2) )?

Or if you can only determine after it persist, that means you have a way to get that persistent instance of that child1 so you can delete this object without ecounter this kind of problem.

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