• 【JPA 级联保存/级联删除】@OneToMany (双向) 一对多【转】


    【http://blog.sina.com.cn/s/blog_625d79410101dbdd.html】

      看过前两篇帮助文档 【JPA】 @OneToOne 单向 和 【JPA】@OneToOne 双向 想必大家对级联操作已经充满了好奇和期待吧。那么本文将会想大家介绍JPA的级联保存操作。在此之前,我希望你能先看下这篇文档,对级联注释的各个属性有一个大概的了解。传送门:【JPA】 级联标签的解释 @Cascade
          在生活中,有许多关系都是一对多的。School(学校)和 Studnet(学生) 之间的关系就是典型的一对多关系。一所学校,有多名学生。但是一个学生,只能属于一所学校。在这里,我们将这个关系设置为一个双向的关系。也就说,通过学生实体,我们可以得到他就读的学校的实体。同样,通过一个学校实体,我们也可一得到在这所学校就读的学生实体的集合。
     
    --------< 例 子 >----------------------------------------------------------------------------------------------------------
     
    //学生Model , 这个双向一对多关系的维护端。一般来说,在双向一对多关系中,"多"这一端,一般都是关系的维护端。
    @Entity
    @Table(name = "student")
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
     
        //在表中建立外键 "school_fk"
        
        @ManyToOne
        @JoinColumn(name="school_fk")
        private School school;
       
    //省略若干get / set
    }
     
     
    @Entity
    @Table(name = "school")
    public class School {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
     
        @OneToMany(mappedBy="shcool")
        //设置 :级联 保存/新建 操作 。新建 学校和学生 的时候,保存新建的学校那么新建的学生也同时被保存
        @Column(cascade={CascadeType.PERSIST})
        private List students;
     
        //手动构造添加学生的方法
        public void addStudent(Strudent stu){
                if(stu != null){
                     students.add(stu);
                }
        }
     
    //省略若干get / set
    }
    -----------------------------------------------------< 测试方法 >--------------------------------------------------------------
    public class Test(){
     
        public void testCreate(){
             School school = new School();
             school.setName("学校");
     
             Student st1 = new Student();
             st1.setName("学弟");
             st1.setSchool(school);
             studnetDAO.save(st1);
           
             Student st2 = new Student();
             st2.setName("学长");
             st2.setSchool(school);
             studnetDAO.save(st2);
     
             //添加学生
             school.addStudent(st1);
             school.addStudent(st2);
             schoolDAO.save(school);
            //以上的保存方法并不是级联保存操作。我们在学校实体上设置了,级联保存操作。
            //我们希望JPA这样做,在新建学校,新建多名学生时。只要将学生的所属学校设置完成后,保存学校,即可保存 所属该校的学生。
        }
     
        //级联保存,测试方法
        public void testPersistCreateSchool(){
                //级联保存演示:已经在学校(School)Model 中添加了 级联保存操作:cascade={CascadeType.PERSIST}
           
             School school = new School();
             school.setName("学校");
     
             Student st1 = new Student();
             st1.setName("学弟");
             st1.setSchool(school);
             //  不需要显示保存学生 st1
             //studnetDAO.save(st1); 
           
             Student st2 = new Student();
             st2.setName("学长");
             st2.setSchool(school);
             //  不需要显示保存学生 st2
             //studnetDAO.save(st2);
     
             //添加学生
             school.addStudent(st1);
             school.addStudent(st2);
             schoolDAO.save(school);// 保存学校,学校内新建的学生也[st1、st2]被保存了。(级联保存)
        }
     
        //测试删除
        public void testDeleteSchool(){
                //革命第一步,创建学校。
                testCreate();
                schoolDAO.delete(school);//后台报错
                //avax.persistence.RollbackException: Error while commiting the transaction Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint...
                  
                 //之前我们已经知道,想要删除关系的被维护端,必须先从维护端解除关系。才能正常删除。所以我们要做的就是,先从 学生端,解除关系。
        
            List<Strudnet> students = school.getStudentList();
            for(int i=0;i<students.size();i++){
                    Student stu = students.get(i);
                    stu.setSchool(null);//手动从学生实体解除挂系
            }
            schoolDAO.delete(school);//现在,学校被正常的删除了。该校的两名学生信息依然保存在数据库中。只是,他们没有所属学校了。
     
            //看到这里,你一定会责备我。JPA不是很强大么?不是很灵活多变么?不是很神奇么?
            //为什么删除一个实体,还需要循环解除关系,这么繁琐的操作?!
            //其实,我们有方法可以直接删除学校。并且,不需要进行这种循环从学生实体解除关系的操作。但是,相信我,这种方法你可能不会接受。下面,就让我们来看看JPA的级联删除。
            //修改学校的Model 中,在校生字段()的注释:@Column(cascade={CascadeType.PERSIST})
            //改为:@Column(cascade={CascadeType.PERSIST,CascadeType.REMOVE}) 添加级联删除操作。那么,现在你就可以直接调用删除学校的方法了。
            schoolDAO.delete(school);//现在,学校也被正常删除了。但是,接下来的一幕,你可能不愿意看到。
            Stduent stu = studentDAO.findByName(学长");
            //打印的结果为: null   是的,你没看错。该校所有的学生也被级联删除了...
            //此时,你应该对JPA的级联删除有所理解了。它删除的对象是实体。级联指的是级联实体。如果,在学生的Model 中添加了一个级联删除的操作,那么删除一个学生,那这个学生所属的学校也会被删除掉。
            //所以,在这里,我要提醒你。级联的删除操作要慎用。因为,他的破坏性太大了。级联删除的操作,并不是针对某个字段,而是它所管理的实体。
    }
           
            //测试删除
            public void testDeleteStudent(){
            //创建学校和学生
            testCreate();
            studentDAO.delete(st1);//此时,st1(创建时,名字叫“学弟”的学生)已经被从数据库中删除了。但是,他所属的学校并没受影响。只不过,st1已经从 学校的 学生集合里删除了。这都是由JPA来完成的。
     
            //现在,我们假设:我们没有执行刚才那部。st1没有被删除。学校依然拥有两名学生。
            //在,Stuent的Model 中添加级联删除的操作。@JoinColumn(name="school_fk",cascade={CascadeType.REMOVE})   并且,School 的 Model中也添加级联删除的操作。
            //此时,数据库中,只有一所学校和两名学生的信息。
            studentDAO.delete(st1);
            //哇,全世界都干净了。如果这个时候查看数据库。你会发现,所有的数据都没了。为什么会这样呢?
            //让我们来看看JPA都干了什么:
            1:删除 st1
            2:school 被 st1 级联删除
            3:st2 被 school 级联删除
            //所以,你才会发现,数据库中所有的信息都消失了。当然,如果之前,数据库中还有别的学校和别的学校所属学生。那么它们的信息是不受影响的。
           //罗嗦了这么多,我感到很抱歉。我只是想提醒你,一定要慎用这个级联的删除操作。千万不用在建立Model 的时候,顺手添加一个级联保存 删除之类的操作。一定要根据自己的需求。和操作的实际效果,仔细斟酌之后,再付诸于行动。否则,你直接来个CascadeType.ALL 大多数情况下,它带给你的都是痛苦的回忆。
     
           }
    //省略main 方法
    }

     

  • 相关阅读:
    POJ 1315 Don't Get Rooked
    POJ 2051 Argus
    POJ 1942 Paths on a Grid
    POJ 2151 Check the difficulty of problems
    POJ 3349 Snowflake Snow Snowflakes
    POJ 1753 Flip Game
    POJ 2392 Space Elevator
    POJ 2385 Apple Catching
    POJ 2356 Find a multiple
    POJ 2355 Railway tickets
  • 原文地址:https://www.cnblogs.com/dixinyunpan/p/5990601.html
Copyright © 2020-2023  润新知