为了账号安全,请及时绑定邮箱和手机立即绑定

支持读取和删除但不支持插入的一对多关系

支持读取和删除但不支持插入的一对多关系

哆啦的时光机 2022-05-21 20:39:59
我想扩展之前文章中提到的要求以支持删除。我们有两个数据模型对象 - 组织和部门共享一对多关系。通过下面的映射,我可以从组织对象中读取部门列表。我没有添加级联 ALL 属性来限制创建组织时添加部门。我应该如何修改@OneToMany 注释(可能还有@ManyToOne)以限制部门的插入但级联删除操作,以便在删除组织对象时删除所有关联的部门?@Entity@Table(name="ORGANIZATIONS")public class Organization{    @Id    @GeneratedValue    Private long id;    @Column(unique=true)    Private String name;    @OneToMany(mappedBy = "organization", fetch = FetchType.EAGER)    private List<Department> departments;}@Entity@Table(name="DEPARTMENTS")Public class Department{   @Id   @GeneratedValue   Private long id;   @Column(unique=true)   Private String name;   @ManyToOne(fetch = FetchType.EAGER)   private Organization organization;}删除组织的代码就是一行organizationRepository.deleteById(orgId);验证这一点的测试用例如下@RunWith(SpringJUnit4ClassRunner.class)@DataJpaTest@Transactionalpublic class OrganizationRepositoryTests {    @Autowired    private OrganizationRepository organizationRepository;    @Autowired    private DepartmentRepository departmentRepository;    @Test    public void testDeleteOrganization() {        final organization organization = organizationRepository.findByName(organizationName).get(); //precondition        Department d1 = new Department();        d1.setName("d1");        d1.setorganization(organization);        Department d2 = new Department();        d2.setName("d2");        d2.setorganization(organization);        departmentRepository.save(d1);        departmentRepository.save(d2);//        assertEquals(2, organizationRepository.getOne(organization.getId()).getDepartments().size()); //this assert is failing. For some reason organizations does not have a list of departments        organizationRepository.deleteById(organization.getId());        assertFalse(organizationRepository.findByName(organizationName).isPresent());        assertEquals(0, departmentRepository.findAll().size()); //no departments should be found    }}
查看完整描述

3 回答

?
白猪掌柜的

TA贡献1893条经验 获得超10个赞

请参阅有关失败原因的代码注释:


@RunWith(SpringJUnit4ClassRunner.class)

@DataJpaTest

@Transactional

public class OrganizationRepositoryTests {


    @Autowired

    private OrganizationRepository organizationRepository;


    @Autowired

    private DepartmentRepository departmentRepository;


    @PersistenceContext

    private Entitymanager em;


    @Test

    public void testDeleteOrganization() {

        Organization organization = 

                organizationRepository.findByName(organizationName).get(); 


        Department d1 = new Department();

        d1.setName("d1");

        d1.setOrganization(organization);


        Department d2 = new Department();

        d2.setName("d2");

        d2.setOrganization(organization);


        departmentRepository.save(d1);

        departmentRepository.save(d2);


        // this fails because there is no trip to the database as Organization 

        // (the one loaded in the first line)

        // already exists in the current entityManager - and you have not 

        // updated its list of departments.

        // uncommenting the following line will trigger a reload and prove 

        // this to be the case: however it is not a fix for the issue.


        // em.clear();


         assertEquals(2,

             organizationRepository.getOne(

               organization.getId()).getDepartments().size()); 


        //similary this will execute without error with the em.clear() 

        //statement uncommented

        //however without that Hibernate knows nothing about the cascacding 

        //delete as there are no departments

        //associated with organisation as you have not added them to the list.

        organizationRepository.deleteById(organization.getId());


        assertFalse(organizationRepository.findByName(organizationName).isPresent());

        assertEquals(0, departmentRepository.findAll().size()); 

    }

}

正确的解决方法是通过封装添加/删除/设置操作并防止直接访问集合来确保始终正确维护内存模型。例如


public class Department(){

    public void setOrganisation(Organisation organisation){

        this.organisation = organisation;


        if(! organisation.getDepartments().contains(department)){

            organisation.addDepartment(department);

        }

    }

}


public class Organisation(){


    public List<Department> getDepartments(){

        return Collections.unmodifiableList(departments);

    }


    public void addDepartment(Department departmenmt){

        departments.add(department);


        if(department.getOrganisation() != this){

            department.setOrganisation(this);

        }

    }

}


查看完整回答
反对 回复 2022-05-21
?
qq_笑_17

TA贡献1818条经验 获得超7个赞

试试这个代码,


    @OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL)

    @JoinColumn(name = "organisation_id", referencedColumnName = "id")

    private List<Department> departments;


    @ManyToOne(fetch = FetchType.EAGER,ascade = CascadeType.REFRESH,mappedBy = "departments")

    private Organization organization;

如果有任何问题通知


查看完整回答
反对 回复 2022-05-21
?
Helenr

TA贡献1780条经验 获得超4个赞

您可以尝试添加以限制级联删除操作仅从组织到部门:

@OneToMany(mappedBy = "organization", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Department> departments;

请注意,如果您对部门实体有依赖/外键约束,那么您还需要将删除操作级联到这些依赖实体。

您可以阅读本指南,它很好地解释了级联操作: https ://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/


查看完整回答
反对 回复 2022-05-21
  • 3 回答
  • 0 关注
  • 113 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号