JAVAEE框架学习——Hibernate——一对多|多对多关联关系映射操作

表关系的分析

数据库中多表之间存在着三种关系:


表关系

表与表的三种关系:

一对多|多对一

建表原则:在多的一方创建外键指向一的一方的主键

一对多

表中的表达

表中表达

实体中的表达

实体中的表达

orm元数据表达

  • 多对一配置
 <!--表示多对一-->
        <many-to-one name="customer"
                     column="lkm_cust_id"
                     class="Customer">

        </many-to-one>

这里的这个配置表示的是:当前的这个配置对象存在多对一的情况,其中name表示实体中多对一的一的名字,column表示外键的列名 class表示的是多个对一个的一的实体类名

  • 一对多配置
 <!--配置集合 一对多的关系-->
        <set name="linkMens">
            <!--指定外键列名-->
            <key column="lkm_cust_id"></key>
            <!--指定一对多还是多对多
                class指定是与哪个类型一对多
            -->
            <one-to-many class="LinkMan"/>
        </set>

这里的这个配置表示的是:当前的这个配置对象存在多对一的情况,其中name表示实体中多个的set集合,key column表示外键的列名 class表示的是一对多的多的实体类名

 /**
     *保存客户以及客户下的联系人
     */
    @Test
    public void fun1() {
        //获得session
        Session session = HibernateUtils.openSession();
        //开启事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作数据
        Customer customer = new Customer();
        customer.setCust_name("xxx");
        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("aaa1");
        LinkMan lm2 = new LinkMan();
        lm2.setLkm_name("aaa2");
        //表达一对多关系 客户下有多个联系人
        customer.getLinkMens().add(lm1);
        customer.getLinkMens().add(lm2);
        //表达多对一 联系人属于哪个客户
        lm1.setCustomer(customer);
        lm2.setCustomer(customer);

        //瞬时状态对象---->持久化状态
        session.save(customer);
        session.save(lm1);
        session.save(lm2);
        //----------------------------
        //提交事物
        tx.commit();
        //关闭资源
        session.close();
    }
/**
     * 为客户添加联系人
     */
    @Test
    public void fun2() {
        //获得session
        Session session = HibernateUtils.openSession();
        //开启事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作数据
        //获得要操作的客户对象
        Customer customer = session.get(Customer.class, 1l);

        //创建联系人
        LinkMan lm1 = new LinkMan();
        //将联系人添加到客户,将客户设置到联系人中
        customer.getLinkMens().add(lm1);
        lm1.setCustomer(customer);
        //执行保存
        session.save(customer);
        session.save(lm1);
        //----------------------------
        //提交事物
        tx.commit();
        //关闭资源
        session.close();
    }
 /**
     * 从客户中删除某个联系人
     */
    @Test
    public void fun3() {
        //获得session
        Session session = HibernateUtils.openSession();
        //开启事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作数据
        //获得客户
        Customer c = session.get(Customer.class, 1l);
        LinkMan lm = session.get(LinkMan.class, 3l);
        //获得客户的联系人
        //将要移除的联系人从客户的联系人集合中移除
        c.getLinkMens().remove(lm);
        //清空联系人的客户
        lm.setCustomer(null);
        //----------------------------
        //提交事物
        tx.commit();
        //关闭资源
        session.close();
    }

上面的代码再删除某个联系人时由于数据都是查询出来的,都是出于持久态的对象,所以再事务进行提交时Hibernate会查看数据是否发生变化,发生变化后会将数据更新,所以不需要执行session.save()方法进行保存数据

操作进阶

在上面的操作中,我们在操作保存客户的联系人的时候,我们需要执行多条保存的代码,这样显然很麻烦,我们希望再保存客户的时候,将客户的联系人也一起保存。所以我们需要进行一些别的配置

  • 级联操作
 <!--级联操作:cascade
                save-update:级联保存更新
                delete:级联删除
                all: save-update delete 级联保存更新和级联删除
            级联操作:简化操作

        -->
        <set name="linkMens" cascade="all">
            <!--配置集合 一对多的关系-->
            <!--指定外键列名-->
            <key column="lkm_cust_id"></key>
            <!--指定一对多还是多对多
                class指定是与哪个类型一对多
            -->
            <one-to-many class="LinkMan"/>

        </set>

级联操作应用在一对多的多的时候,也可以实现保存联系人的时候将客户保存

  • linkman.hbm.xml 配置级联操作
 <!--表示多对一-->
        <many-to-one name="customer"
                     column="lkm_cust_id"
                     class="Customer"
                     cascade="all"
        >

操作代码

/**
     * 级联操作 保存联系人 级联保存客户
     */
    @Test
    public void fun5() {
        //获得session
        Session session = HibernateUtils.openSession();
        //开启事物
        Transaction tx = session.beginTransaction();
        //----------------------------
        //操作数据
        //创建客户
        Customer customer = new Customer();
        customer.setCust_name("google company");

        //创建联系人
        LinkMan lm = new LinkMan();
        lm.setLkm_name("劈柴哥");
        //为客户添加联系人
        customer.getLinkMens().add(lm);
        //为联系人指定客户
        lm.setCustomer(customer);
        //保存客户----> 只保存联系人 通过级联操作保存客户
        session.saveOrUpdate(lm);

        //----------------------------
        //提交事物
        tx.commit();
        //关闭资源
        session.close();
    }

在平时开发中,建议使用save-update 不建议使用delete 级联删除会删除相关的所有数据

  • 关系维护
    我们查看操作时Hibernate产生的SQL语句时发现了如下的语句
    Hibernate执行的语句.png

    我们发现。在保存时,客户和联系人两房都会维护外键的关系,关系会维护两次,这样冗余了。多余的维护关系语句。显然是客户这一端在维护关系。所以我们需要优化这种情况 这就引入了Hibernate在实体类配置的时候提供的配置 inverse
    inverse是为了提高效率
 <!--
            inverse属性:配置关系是否维护
            配置当前的Customer是否维护与LinkMan的关系
            inverse true 的意思就是将关系维护的工作完全交给对方 作为配置方不进行维护关系
                    * 不维护关系无法在删除的时候将外键置为空
                    false(默认值) 意思是将关系维护的工作由配置方来维护
                    原则:无论关系方如何放弃关系,总有一方必须要维护关系
                    一对多关系当中,也只能是一的一方放弃维护,多的一方不能放弃
                
        -->
        <set name="linkMens" inverse="true">
            <key column="lkm_cust_id"></key>
                class指定是与哪个类型一对多   
            <one-to-many class="LinkMan"/>
        </set>

tips:在开发中的删除数据,结合inverse 和cascade 实现不同的数据删除策略。以这个demo为例,一个客户可以由多个联系人。在这种情况下 相当于客户和联系人维持着某种关系。我们在有需求删除客户的时候对联系人的删除有两种。
第一种:删除Customer的时候,将LinkMan的外键置为空,LinkMan数据保留
这种情况下,如果Customer不维护数据的话是无法操作LinkMan的外键的,所以在这种情况下我们需要让Customer inverse配置为false 让customer维护关系,在这种情况下删除Customer的时候会删除LinkMan的关系。
第二种:删除Customer的时候,级联操作将LinkMan的数据删除。
在这种情况下,将Customer inverse设置为true 并且将cascade指定为delete 这样在删除Customer的同时就会删除LinkMan子表中的数据。

多对多关联关系映射操作

建表原则:创建一个中间表,中间表中至少两个字段作为外键分别指向多对多双方的主键

多对多

表中关系表达

表中表达

实体中的表达

实体中的表达

orm元数据表达

  • User表配置
  <set name="roles" table="sys_user_role">
            <!--
                key标签
                    column:当前对象在中间表中的外键名称
            -->
            <key column="user_id"></key>
            <!--
                many-to-many标签:
                class:关联另一方的类的全类名
                column 关联的另一方在中间表的外键名称
            -->
            <many-to-many class="Role" column="role_id"></many-to-many>
        </set>
  • role表配置
  <!--Role多对多配置-->
        <set name="users" table="sys_user_role">
            <key column="role_id"/>
            <many-to-many class="User"
                          column="user_id"/>
        </set>

多对多操作

我们使用案例:保存员工以及角色

在多对多关系中一定要有一方放弃关系的维护,否则会出现主键重复错误
放弃关系维护在配置文件中使用 inverse=“true” 表示放弃操作

  • 配置文件
  <!-- inverse 属性 使得一方放弃维护外键关系-->
        <set name="users" table="sys_user_role" inverse="true">
            <key column="role_id"/>
            <many-to-many class="User"
                          column="user_id"/>
        </set>
  • 实例代码
 //从工厂获得session
        Session session = HibernateUtil.openSession();
        //开启事物
        Transaction tx = session.beginTransaction();
        //操作数据
        //-------------------------------
        //创建两个User
        User user1 = new User();
        User user2 = new User();
        user1.setUser_name("blackman");
        user2.setUser_name("whiteman");

        //创建两个role
        Role role1 = new Role();
        Role role2 = new Role();
        Role role3 = new Role();
        role1.setRole_name("vp");
        role2.setRole_name("cto");
        role3.setRole_name("security");


        //用户表达关系
        user1.getRoles().add(role1);
        user1.getRoles().add(role2);
        user2.getRoles().add(role1);
        user2.getRoles().add(role3);
        //角色表达关系
        role1.getUsers().add(user1);
        role1.getUsers().add(user2);
        role2.getUsers().add(user1);
        role3.getUsers().add(user2);
        //保存到数据库
        session.save(user1);
        session.save(user2);
        session.save(role1);
        session.save(role2);
        session.save(role3);
        //-------------------------------
        //提交事务
        tx.commit();
        //关闭资源
        session.close();

 @Test
    /**
     * 添加角色到某个用户中
     */
    public void addRoleToUser() {
        Session session = HibernateUtil.openSession();
        Transaction tx = session.beginTransaction();
        //操作
        //------------
        //获得要添加的用户
        User user = session.get(User.class, 1l);
        //创建公关角色
        Role role = new Role();
        role.setRole_name("公关");
        //将角色添加到用户
        user.getRoles().add(role);
        //将角色转换为持久化
        session.save(role);
        //------------
        tx.commit();
        session.close();
    }

/**
* 从指定角色删除
*/
  @Test
    public void delRoleFromUser(){
        //获取session
        Session session = HibernateUtil.openSession();
        //开启事物
        Transaction tx = session.beginTransaction();
        //操作数据
        //-------------
        //获得要删除的数据
        User user = session.get(User.class, 3l);
        //获得要删除的角色数据
        Role role = session.get(Role.class, 7l);
        user.getRoles().remove(role);
        //-------------
        //提交事务
        tx.commit();
        //关闭资源
        session.close();
    }


进阶操作

  • 级联操作
    cascade: 级联操作
    在User.hbm.xml中配置cascade后
        <set name="roles" table="sys_user_role" cascade="save-update">

可以进行级联保存 上面的操作代码不再需要

session.save(role)

一对一

  • 建表原则:
    • 1 唯一外键对应:假设一对一种的任意一方为多,在多的一方创建外键指向一的一方的主键,然后将外键设置为唯一。
    • 2 主键对应:一方的主键作为另一方的主键


      一对一
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容