zoukankan      html  css  js  c++  java
  • Hibernate之关联关系

    时间:2017-1-20 16:28

    ——一对多配置

    1、第一步:创建实体类
        *   客户实体
        *   订单实体

        示例代码:
            /**

             * 客户实体
             * @author WYC
             *
             */
            public class Customer {
                private Integer cid;
                private String name;
     
                // 一个客户有多个订单
                // 拥有哪些订单 
                private Set<Order> orders = new HashSet<Order>();
                set() / get()... 
            }

            /**
             * 订单实体
             * @author WYC
             *
             */
            public class Order {
                private Integer oid;
                private String addr;
     
                // 订单属于某一个客户
                private Customer customer;
                set() / get()...
            }


    2、第二步:建立映射

        1)Customer的映射

            <hibernate-mapping>
                <class name="com.wyc.hibernate3.demo2.Customer" table="customer">
                    <id name="cid" column="cid">
                        <generator class="native"/>
                    </id>
     
                    <property name="cname" column="cname" length="20"/>
     
     
                    <!-- 配置映射 -->
                    <!-- 因为属性是一个集合,所以需要配置集合 -->
                    <!-- name:Customer对象中关联对象的属性名称 -->
                    <set name="orders">
                        <!-- key标签中的column用来描述一对多关系中多的一方的外键的名称 -->
                        <key column="cid"></key>
                        <!-- 配置一个<one-to-many>,用来指定多的一方的实体类 -->
                        <one-to-many class="com.wyc.hibernate3.demo2.Order"/>
                    </set>
                </class>
            </hibernate-mapping>


        2)Order的映射

            <hibernate-mapping>
                <class name="com.wyc.hibernate3.demo2.Order" table="order_table">
                    <id name="oid" column="oid">
                        <generator class="native"/>
                    </id>
     
                    <property name="addr" column="addr" length="50"/>
     
                    <!-- 配置映射 -->
                    <!-- <many-to-one>标签
                        name:关联对象的属性名称
                        column:外键的名称
                        class:关联对象类的全路径
                     -->
                    <many-to-one name="customer" column="cid" class="com.wyc.hibernate3.demo2.Customer" />
                </class>
            </hibernate-mapping>


        one-to-many和many-to-one中的column必须一致,否则会创建两个外键。

    3、第三步:将映射放到核心配置文件中



    ——一对多级联保存

    级联:操作当前对象的时候,关联的对象如何处理?

    1、通常情况下如果想要向数据库插入数据,需要每个对象都要save一次,例如:

        public class HibernateDemo2 {
            @Test
            // 向客户表插入一个客户,在订单表中插入两个订单
            public void fun1() {
                Session session = HibernateUtils.openSession();
     
                Transaction tx = session.beginTransaction();
     
                /*
                 * 定义一个客户
                 */
                Customer customer = new Customer();
                customer.setCname("张三");
     
                /*
                 * 定义两个订单
                 */
                Order order1 = new Order();
                order1.setAddr("奎文区");
                Order order2 = new Order();
                order2.setAddr("高新区");
     
                /*
                 * 建立关系
                 */
                order1.setCustomer(customer);
                order2.setCustomer(customer);
     
                customer.getOrders().add(order1);
                customer.getOrders().add(order2);
     
                session.save(customer);
                session.save(order1);
                session.save(order2);
     
                tx.commit();
                session.close();
            }
        }



    2、能否只保存其中一个?从而完成保存两条数据
        可以使用级联保存。

        级联保存的方向性:
            *   保存客户时选择级联订单(在Customer中配置级联)
            *   保存订单的时候选择级联客户。(在Order中配置级联)

        不能直接save()其中一个持久态对象,否则会抛出异常:org.hibernate.TransientObjectException

    3、级联保存示例代码

        /*
         * 保存订单,同时级联客户
         * 当保存订单时,需要关联Customer对象,所以需要在many-to-one上进行配置一个属性:
         *  cascade="save-update",表示在保存或更新时会级联操作
         */
        public void fun4(){
            Session session = HibernateUtils.openSession();
     
            Transaction tx = session.beginTransaction();
     
            /*
             * 定义一个客户
             */
            Customer customer = new Customer();
            customer.setCname("张三");
     
            /*
             * 定义一个订单
             */
            Order order1 = new Order();
            order1.setAddr("奎文区");
     
            /*
             * 建立关系
             */
            order1.setCustomer(customer);
            customer.getOrders().add(order1);
     
            /*
             * 只保存一方
             */
            session.save(order1);
     
            tx.commit();
            session.close();
        }
     
     
        /*
         * 保存客户,同时级联订单
         * 当保存客户时,需要关联Set集合,所以需要在Set集合上进行配置一个属性:
         *  cascade="save-update",表示在保存或更新时会级联操作
         */
        public void fun3(){
            Session session = HibernateUtils.openSession();
     
            Transaction tx = session.beginTransaction();
     
            /*
             * 定义一个客户
             */
            Customer customer = new Customer();
            customer.setCname("张三");
     
            /*
             * 定义一个订单
             */
            Order order1 = new Order();
            order1.setAddr("奎文区");
     
            /*
             * 建立关系
             */
            order1.setCustomer(customer);
            customer.getOrders().add(order1);
     
            /*
             * 只保存一方
             */
            session.save(customer);
     
            tx.commit();
            session.close();
        }


    ——一对多级联删除

    1、不配置级联删除的情况下:
        示例代码:

            /*
             * 如果不配置级联删除,会将外键置为null,然后再删除记录
             */
            public void fun5(){
                Session session = HibernateUtils.openSession();
                Transaction tx = session.beginTransaction();
     
                /*
                 * 删除客户
                 * 如果想要级联删除,则必须先查询再删除
                 */
                Customer customer = (Customer) session.get(Customer.class, 1);
     
                session.delete(customer);
     
                tx.commit();
                session.close();
            }


    2、配置级联删除的情况下,删除客户的时候级联删除订单:

        需要在Customer.hbm.xml的<set>标签上配置cascade="delete",如果cascade有多个值,可以用逗号隔开。


        示例代码:

            /*
             * 级联删除
             */
            public void fun6(){
                Session session = HibernateUtils.openSession();
                Transaction tx = session.beginTransaction();
     
                /*
                 * 级联删除:先查询,再删除
                 */
     
                /*
                 * 删除客户时级联删除订单
                 * 在<set>标签中配置cascade="delete"
                 */
                Customer customer = (Customer) session.get(Customer.class, 1);
                session.delete(customer);
     
                /*
                 * 删除订单时级联删除客户
                 * 需要在Order.hbm.xml中的many-to-one中配置cascade="delete"
                 */
                Order order = (Order) session.get(Order.class, 1);
                session.delete(order);
     
                tx.commit();
                session.close();
            }



    ——cascade取值

    1、none
        不使用级联,是默认值。

    2、save-update
        保存或更新时级联。

    3、delete
        删除时级联。

    4、all
        除了孤儿删除以外所有的操作都会级联。

    5、delete-orphan
        孤儿删除(孤子删除)
        仅限于一对多,因为只有一对多的时候才有主从(父子)关系存在。
        一的一方是主(父)方。
        多的一方是从(子)方。

        当一个客户与某个订单解除了关系时,相当于把订单的外键置为null,订单就没有了所属客户了,那么就会将这种记录删除。

        删除没有外键的记录。

    6、all-delete-orphan
        包含了孤儿删除的所有级联操作。

    7、孤儿删除示例代码:
        

        /*
         * 孤儿删除
         * 在Customer.hbm.xml中<set>标签上配置cascade="delete-orphan"
         */
        public void fun7(){
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
     
            /*
             * 让2号客户与2号订单解除关系
             */
            Customer customer = (Customer) session.get(Customer.class, 2);
            Order order = (Order) session.get(Order.class, 2);
     
            // 将该客户对应的其中某一订单删除后,该订单的外键就是null了
            customer.getOrders().remove(order);
     
     
            tx.commit();
            session.close();
        }


    ——双向维护 - 产生多余SQL

    配置inverse="true",在哪一端配置,配置的那一端就放弃了外键的维护权。

    示例代码:

        /*
         * 双向维护:自动更新数据库,会产生多余的SQL
         * 双方都有维护外键的能力,要想不产生多余SQL,必须让其中一方放弃外间的维护权:<set inverse="true">
         * 一般情况下都是“多”的一方维护外键关系
         */
        public void fun8(){
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
     
            /*
             * 将3号客户的订单改为1号客户的订单
             */
            Customer customer = (Customer) session.get(Customer.class, 1);
     
            Order order = (Order) session.get(Order.class, 3);
     
            /*
             * 持久态对象如果发生变化会自动更新数据库
             * 会发送两条update语句
             */
     
            customer.getOrders().add(order);
            order.setCustomer(customer);
     
            tx.commit();
            session.close();
        }



    ——cascade和inverse的区别

    cascade:操作关联对象
    inverse:控制外键的维护

    示例代码:

        /*

         * 区分cascade和inverse
         * 
         * 在Customer.hbm.xml中配置:<set name="orders" cascade="save-update" inverse="true" >
         */
        public void fun9(){
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
     
            Customer customer = new Customer();
            customer.setCname("张三");
     
            Order order = new Order();
            order.setAddr("奎文");
     
            // 客户关联订单
            customer.getOrders().add(order);
     
            /*
             * 客户是否保存到数据库:保存
             * 订单是否保存到数据库:保存,因为设置了cascade="save-update",但是订单的外键是null
             * 因为在Customer中设置了inverse="true",说明外键不属于Customer来维护
             * 只有在插入订单时,才会产生外键 
             */
            session.save(customer);
     
            tx.commit();
            session.close();
        }


    ——多对多的配置

    1、学生实体类

        public class Student {
            private Integer sid;
            private String sname;
            private Set<Course> courses = new HashSet<Course>();
     
            @Override
            public String toString() {
                return "Student [sid=" + sid + ", sname=" + sname + ", courses=" + courses + "]";
            }
     
            public Set<Course> getCourses() {
                return courses;
            }
     
            public void setCourses(Set<Course> courses) {
                this.courses = courses;
            }
     
            public Integer getSid() {
                return sid;
            }
     
            public void setSid(Integer sid) {
                this.sid = sid;
            }
     
            public String getSname() {
                return sname;
            }
     
            public void setSname(String sname) {
                this.sname = sname;
            }
    }


    2、课程实体类

        public class Course {
            private Integer cid;
            private String cname;
            private Set<Student> students = new HashSet<Student>();
     
            public Integer getCid() {
                return cid;
            }
     
            public void setCid(Integer cid) {
                this.cid = cid;
            }
     
            public String getCname() {
                return cname;
            }
     
            public void setCname(String cname) {
                this.cname = cname;
            }
     
            public Set<Student> getStudents() {
                return students;
            }
     
            public void setStudents(Set<Student> students) {
                this.students = students;
            }
    }
     

    3、Student.hbm.xml
        <?xml version="1.0" encoding="UTF-8"?>

        <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     
        <hibernate-mapping>
            <class name="com.wyc.hibernate3.demo3.Student" table="student">
                <id name="sid" column="sid">
                    <generator class="native"></generator>
                </id>
     
                <property name="sname" column="sname" length="20" />
     
                <!-- 配置关联映射 -->
                <!-- 多对多时存在中间表,table属性写中间表的名称,两个类中的table必须一致,否则生成多个中间表 -->
                <set name="courses" table="stu_cour">
                    <!-- key标签中的column写本类在中间表中的外键字段 -->
                    <key column="sid" />
                    <!-- many-to-many中的class是另一方类的全路径,column表示另一方类在中间表中外键的名称 -->
                    <many-to-many class="com.wyc.hibernate3.demo3.Course" column="cid"></many-to-many>
                </set>
     
            </class>
        </hibernate-mapping>


    4、Course.hbm.xml
        <?xml version="1.0" encoding="UTF-8"?>

        <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     
        <hibernate-mapping>
            <class name="com.wyc.hibernate3.demo3.Course" table="course">
                <id name="cid" column="cid">
                    <generator class="native"></generator>
                </id>
     
                <property name="cname" column="cname" length="20" />
     
                <!-- 配置与学生关联的映射 -->
                <set name="students" table="stu_cour" inverse="true" >
                    <key column="cid" />
                    <many-to-many class="com.wyc.hibernate3.demo3.Student" column="sid"/>
                </set>
            </class>
         </hibernate-mapping>


    5、将映射配置文件添加到核心配置文件中


    ——多对多的保存操作

    如果在多对多的情况下进行保存,则必须有一方放弃主键维护,不然会导致主键重复。

    通常是主动方来维护主键,比如学生选课,学生来维护主键。

    需要在Course.hbm.xml中添加<set inverse="true">来指定Course放弃维护主键。

    public class HibernateDemo3 {
        @Test
        /*
         * 保存学生和课程 为学生选择一些课程
         */
        public void fun1() {
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
     
            // 创建学生
            Student student1 = new Student();
            student1.setSname("张三");
     
            Student student2 = new Student();
            student2.setSname("李四");
     
            // 创建课程
            Course course1 = new Course();
            course1.setCname("Java");
     
            Course course2 = new Course();
            course2.setCname("Android");
     
            // 张三选择1号课程和2号课程
            student1.getCourses().add(course1);
            student1.getCourses().add(course2);
     
            course1.getStudents().add(student1);
            course2.getStudents().add(student1);
     
            // 李四选1号课程
            student2.getCourses().add(course1);
            course1.getStudents().add(student2);
     
            // 执行保存
            session.save(student1);
            session.save(student2);
     
            session.save(course1);
            session.save(course2);
     
            tx.commit();
            session.close();
        }
    }



    ——多对多级联保存

    /*
     * 级联操作:保存学生,关联课程
     * 在Student.hbm.xml中配置cascade="save-update"
     */
    public void fun2() {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
     
        // 创建学生
        Student student = new Student();
        student.setSname("王五");
     
        // 创建课程
        Course course = new Course();
        course.setCname("PHP");
     
        student.getCourses().add(course);
        course.getStudents().add(student);
     
     
        session.save(student);
     
        tx.commit();
        session.close();
    }


    ——删除中间表信息

    获取中间表后将记录从集合中删除即可。

    /*
     * 删除1号学生的选课信息
     */
    public void fun3() {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
     
        // 查询1号学生
        Student student = (Student) session.get(Student.class, 1);
     
        // 查询1号课程
        Course course = (Course) session.get(Course.class, 1);
     
        // 删除选课信息
        student.getCourses().remove(course);
     
        tx.commit();
        session.close();
    }


    ——总结

    1、一对一的配置
        *   在多的一方:<many-to-one>
        *   在一的一方:<set> <key /> <one-to-many> </set>
    2、多对多的配置
        *   <set> <key /> <many-to-many /> </set>
        在多对多关系的情况下,需要有一方放弃外键维护权。
    3、级联操作
        *    save-update
        *   delete
        *   all
        *   delete-orphan(孤儿删除)
        *   all-delete-orphan
    4、inverse
        放弃外键维护权
        通常在一的一方放弃。

  • 相关阅读:
    java时间戳转换日期 和 日期转换时间戳
    通过blob文件导出下载成Excel文件
    三元表达式进化
    Vue切换组件实现返回后不重置数据,保留历史设置操作
    vue 下载文件
    ide打断点,跑到某一行代码,再执行的方法
    Java操作终端的方法
    前端下载本地文件的方法
    java 读取本地json文件
    js 时间戳转换
  • 原文地址:https://www.cnblogs.com/wwwwyc/p/6375461.html
Copyright © 2011-2022 走看看