Hibernate 的一对多关联映射

之前在学习 Hibernate 的时候,其实都是单表的操作。在实际的开发当中,比如做一个商城,就需要好多张数据库表,表与表之间是有关系的。之前些做一些关联查询或者是一些基本的查询操作的时候,都是写 SQL 语句去关联两张表,这样子稍微有一点麻烦。那么有了 Hibernate  之后,它就可以解决这类问题了。在这之前,先了解一些数据库 表之间的关系,它是有三种关系的。

1、数据库表与表之间的关系

也就是说我们的一个系统,抽取出来的那些实体,因为表是根据实体来的 ,那么它会根据实体分成 这三类的关系。也就是说你所有的系统里边,所有抽取出来的实体,也只有这三种关系。

① 一对多的关系

  什么关系属于一对多?

    一个部门对于多个员工,一个员工只能属于一个部门。

    一个客户可以对应多个联系人,一个联系人只能属于某一个客户(个人认为是错误的)。

  一对多的创表原则

② 多对多的关系

  什么样的关系属于多对多?

    一个学生可以选择多门课程,一门课程也可以被多个学生选择。

    一个用户可以选择多个角色,一个角色也可以被多个用户选择。

  多对多建表原则

③ 一对一的关系(了解,实际开发过程中应用很少)

  在实际开发过程中,一对一的关系可以建成一张表,除非 是有特殊的需求需要将表分开。

  唯一外键对应

    在一张表添加外键字段,并作唯一约束。

  主键对应

    需要将两张表关联。

一对多怎么表示?一个客户应该有多个联系人,一个联系人只能属于一个客户。

  现在要通过 ORM 的方式表示:一个联系人只能属于某一个客户。开发语言是面向对象的 ,怎么表示一个联系人属于一个客户呢?放置一个客户的对象,所有以后创表的时候会创建外键,但是创建实体的时候要放的是“一”的一方的一个对象。所以以后碰到外键的时候不要写外键的名称了,把外键改成“一”的一个对象,并生成相应的get & set。

  还是通过 ORM 的方式表示 :一个客户对应多个联系人。放置的是“多”的一方的集合。需要注意的是,hibernate 默认使用的是 set 集合,不是 list 集合。当然,它里面也可以配置 list/map,list 集合应为在 hibernate 中需要对 list 数据进行有序排列。如果配置 list 集合,hibernate 需要在数据库表中多建一列,这一列就是用于 hibernate 排序的,它会用0、1、2、3、4、5...列这个顺序,那么表会多出来一列。所以一般是使用的都是 set 集合。

  现在做的是双向关联,就是从客户这边查联系人可以查,从联系人这边查客户也可以查。如果只做了单向关联,只在联系人那边放客户对象了,客户这边没有放联系人的集合,那你在查询的时候,只能从联系人查到客户,就不能从客户查联系人了。当然,两边都做好了,想从那边去查都没问题(也可以做出单项的)。

Hibernate 的一对多关联映射

(一)、Hibernate 的一对多关联映射

1、创建一个项目,引入相应 jar 包

2、创建数据库和表

CREATE TABLE `cst_customer` (`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;CREATE TABLE `cst_linkman` (`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',`lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id',`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',PRIMARY KEY (`lkm_id`),KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

  在 MySQL 数据库中,将两张表拖拽到“架构设计器”选项卡就可以看到两张表之间的关系。

3、创建实体

① “一”的一方的实体

② “多的一方的实体”

4、创建映射文件

① 多的一方的映射的创建(联系人表的 hibernate 配置文件   LinkMan.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.itheima.hibernate.domain.LinkMan" table="cst_linkman"><!-- 建立OID与主键映射 --><id name="lkm_id" column="lkm_id"><generator class="native"/></id><!-- 建立普通属性与表字段映射 --><property name="lkm_name"/><property name="lkm_gender"/><property name="lkm_phone"/><property name="lkm_mobile"/><property name="lkm_email"/><property name="lkm_qq"/><property name="lkm_position"/><property name="lkm_memo"/><!-- 配置多对一的关系:放置的是一的一方的对象 --><!-- many-to-one标签* name        :一的一方的对象的属性名称。* class        :一的一方的类的全路径。* column    :在多的一方的表的外键的名称。       操作 column 就相当与操作数据库中表的外键--><many-to-one name="customer" class="com.itheima.hibernate.domain.Customer" column="lkm_cust_id"/></class>
</hibernate-mapping>

② “一”的一方的映射的创建(客户表的 hibernate 配置文件   Customer.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.itheima.hibernate.domain.Customer" table="cst_customer"><!-- 建立OID与主键映射 --><id name="cust_id" column="cust_id"><generator class="native"/></id><!-- 建立普通属性与数据库表字段映射 --><property name="cust_name" column="cust_name" /><property name="cust_source" column="cust_source"/><property name="cust_industry" column="cust_industry"/><property name="cust_level" column="cust_level"/><property name="cust_phone" column="cust_phone"/><property name="cust_mobile" column="cust_mobile"/><!-- 配置一对多的映射:放置的多的一方的集合 --><!-- set标签 :* name    :多的一方的对象集合的属性名称。* cascade:级联* inverse:放弃外键维护权。--><set name="linkMans"><!--key标签* column:多的一方的外键的名称。--><key column="lkm_cust_id"/><!-- one-to-many标签* class    :多的一方的类的全路径--><one-to-many class="com.itheima.hibernate.domain.LinkMan"/></set></class>
</hibernate-mapping>

5、创建核心配置文件(hibernate.cfg.xml)

  将要连接的数据库名称改一下,还有要引入的映射文件,这里要引入两个映射文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration><session-factory><!-- 连接数据库的基本参数 --><property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property><property name="hibernate.connection.url">jdbc:mysql:///hibernate_day03</property><property name="hibernate.connection.username">root</property><property name="hibernate.connection.password">abc</property><!-- 配置Hibernate的方言 --><property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property><!-- 可选配置================ --><!-- 打印SQL --><property name="hibernate.show_sql">true</property><!-- 格式化SQL --><property name="hibernate.format_sql">true</property><!-- 自动创建表 --><property name="hibernate.hbm2ddl.auto">update</property><!-- 配置C3P0连接池 --><property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property><!--在连接池中可用的数据库连接的最少数目 --><property name="c3p0.min_size">5</property><!--在连接池中所有数据库连接的最大数目  --><property name="c3p0.max_size">20</property><!--设定数据库连接的过期时间,以秒为单位,如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 --><property name="c3p0.timeout">120</property><!--每3000秒检查所有连接池中的空闲连接 以秒为单位--><property name="c3p0.idle_test_period">3000</property><!-- 设置事务隔离级别 --><property name="hibernate.connection.isolation">4</property><!-- 配置当前线程绑定的Session --><property name="hibernate.current_session_context_class">thread</property><!-- 引入映射 --><!-- <mapping resource="com/itheima/hibernate/domain/Customer.hbm.xml"/><mapping resource="com/itheima/hibernate/domain/LinkMan.hbm.xml"/> --><mapping resource="com/itheima/hibernate/domain/User.hbm.xml"/><mapping resource="com/itheima/hibernate/domain/Role.hbm.xml"/></session-factory>
</hibernate-configuration>

7、创建测试类

package com.itheima.hibernate.demo1;import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;import com.itheima.hibernate.domain.Customer;
import com.itheima.hibernate.domain.LinkMan;
import com.itheima.hibernate.utils.HibernateUtils;/*** 一对多的测试类* @author jt**/
public class HibernateDemo1 {@Test// 保存2个客户  和 3个联系人  并且建立好关系public void demo1(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 创建两个客户Customer customer1 = new Customer();customer1.setCust_name("王东");Customer customer2 = new Customer();customer2.setCust_name("赵洪");// 创建三个联系人LinkMan linkMan1 = new LinkMan();linkMan1.setLkm_name("凤姐");LinkMan linkMan2 = new LinkMan();linkMan2.setLkm_name("如花");LinkMan linkMan3 = new LinkMan();linkMan3.setLkm_name("旺财");// 设置关系:
        linkMan1.setCustomer(customer1);linkMan2.setCustomer(customer1);linkMan3.setCustomer(customer2);customer1.getLinkMans().add(linkMan1);customer1.getLinkMans().add(linkMan2);customer2.getLinkMans().add(linkMan3);// 保存数据:
        session.save(linkMan1);session.save(linkMan2);session.save(linkMan3);session.save(customer1);session.save(customer2);tx.commit();}
@Test/*** 区分cascade和inverse的区别*/public void demo9(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();Customer customer = new Customer();customer.setCust_name("李兵");LinkMan linkMan = new LinkMan();linkMan.setLkm_name("凤姐");customer.getLinkMans().add(linkMan);// 条件在Customer.hbm.xml上的set中配置了cascade="save-update" inverse="true"session.save(customer); // 客户会插入到数据库,联系人也会插入到数据库,但是外键为null
        tx.commit();}
}

(二)、Hibernate 的一对多相关操作

1、一对多关系只保存一边是否可行

将数据做了双向关连以后,是否可以 只保存一边的 数据呢?不可以,回报一个瞬时对象异常的错误。什么是瞬时对象异常啊,就是说持久态的对象关联了一个瞬时态的对象。客户对象本来是瞬时态的对象 ,一保存就变成持久态的对象了,之前和联系人对象做了关联,但联系人对象 还是瞬时态的对象,所以会报错。

   @Test// 一对多关系只保存一边是否可以public void demo2(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();Customer customer = new Customer();customer.setCust_name("赵洪");LinkMan linkMan = new LinkMan();linkMan.setLkm_name("如花");customer.getLinkMans().add(linkMan);linkMan.setCustomer(customer);// 只保存一边是否可以:不可以,报一个瞬时对象异常:持久态对象关联了一个瞬时态对象。// session.save(customer);
        session.save(linkMan);tx.commit();}

2、一对多的级联操作

  如果想只保存一边,那么要通过一个配置,叫做级联操作。

  级联操作包含有常用的两种级联操作,一种叫级联保存或更新,一种叫级联删除。

    什么叫做级联

      级联指的是,操作一个对象的时候,是否会同时操作其关联的对象。

     级联是有方向性

      操作一的一方的时候,是否操作到多的一方

      操作多的一方的时候,是否操作到一的一方

  之前保存一边是会报异常的,报的是瞬时对象异常。现在要通过配置完成一个级联操作,要看你操作的主体是什么,我们现在操作的主体是 客户对象,需要在客户实体的映射文件中进行配置 。

① 级联保存或更新

  保存客户级联联系人(保存“一”的一方,级联“多”的一方)

    映射文件的配置

        <!-- set标签 :* name    :多的一方的对象集合的属性名称。* cascade:级联* inverse:放弃外键维护权。--><set name="linkMans" cascade="save-update">

    测试类

    @Test/***  级联保存或更新操作:*  * 保存客户级联联系人,操作的主体是客户对象,需要在Customer.hbm.xml中进行配置*  * <set name="linkMans" cascade="save-update">*/public void demo3(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();Customer customer = new Customer();customer.setCust_name("赵洪");LinkMan linkMan = new LinkMan();linkMan.setLkm_name("如花");customer.getLinkMans().add(linkMan);linkMan.setCustomer(customer);session.save(customer);tx.commit();}

  保存联系人级联客户(保存“多”的一方,级联“一”的一方)

    映射文件的配置

     <!-- many-to-one标签* name        :一的一方的对象的属性名称。* class        :一的一方的类的全路径。* column    :在多的一方的表的外键的名称。操作 column 就相当与操作数据库中表的外键--><many-to-one name="customer" cascade="save-update" class="com.itheima.hibernate.domain.Customer" column="lkm_cust_id"/>

    测试类

@Test/***  级联保存或更新操作:*  * 保存联系人级联客户,操作的主体是联系人对象,需要在LinkMan.hbm.xml中进行配置*  * <many-to-one name="customer" cascade="save-update" class="com.itheima.hibernate.domain.Customer" column="lkm_cust_id"/>*/public void demo4(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();Customer customer = new Customer();customer.setCust_name("李兵");LinkMan linkMan = new LinkMan();linkMan.setLkm_name("凤姐");customer.getLinkMans().add(linkMan);linkMan.setCustomer(customer);session.save(linkMan);tx.commit();}

3、测试对象的导航

  我们需要注意的是,现在做的属于双向关联,就是客户关联联系人,联系人也关联了客户,其实这种也叫作对象的导航。对象导航是怎么去设置的,怎么往数据库中保存或更新的。

  没有关联,外键默认是 none。

  对象的导航,一定要搞清楚关联关系的设置,那边配置了 cascade 对另一半的影响。

    @Test/*** 测试对象的导航* * 前提:一对多的双方都设置cascade="save-update"*/public void demo5(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();Customer customer = new Customer();customer.setCust_name("李兵");LinkMan linkMan1 = new LinkMan();linkMan1.setLkm_name("凤姐");LinkMan linkMan2 = new LinkMan();linkMan2.setLkm_name("如花");LinkMan linkMan3 = new LinkMan();linkMan3.setLkm_name("芙蓉");linkMan1.setCustomer(customer);customer.getLinkMans().add(linkMan2);customer.getLinkMans().add(linkMan3);// 双方都设置了cascade// session.save(linkMan1); // 发送几条insert语句  4条    linkMAn1  custome linkMan2 linkMan2// session.save(customer); // 发送几条insert语句  3条    customer  linkMan2  linkman3session.save(linkMan2); // 发送几条insert语句  1条    linkMan1
        tx.commit();}

5、级联删除

  什么是级联删除?

    删除一边的,同时将另一方的数据也一并删除。

  在 JDBC 情况,要想做级联删除,除非先把要删除的那个客户(“一”)所关联的联系人(“多”)删除干净,才能继续删除客户。但是 Hibernate 是可以直接级联你删除的。

  Hibernate 情况将要删除的外键设置为空,再删除数据,只删除了一方。

  Hibernate 删除数据需要先查询,再删除。如果创建对象后直接删除,这是对象所对应的集合还是空的;先查询,数据将自动添加到对象所对应的集合,再删除。

删除客户级联删除联系人

    @Test/*** 级联删除:* * 删除客户级联删除联系人,删除的主体是客户,需要在Customer.hbm.xml中配置   * * 多个操作之间用逗号隔开* * <set name="linkMans" cascade="save-update,delete">*/public void demo6(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 没有设置级联删除,默认情况:修改了联系人的外键,删除客户/*Customer customer = session.get(Customer.class, 1l);session.delete(customer);*/// 删除客户,同时删除联系人Customer customer = session.get(Customer.class, 1l);session.delete(customer);tx.commit();}

删除联系人级联删除客户(基本不用)

    @Test/*** 级联删除:* * 删除联系人级联删除客户,删除的主体是联系人,需要在LinkMan.hbm.xml中配置* * <many-to-one name="customer" cascade="delete">*/public void demo7(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 删除客户,同时删除联系人LinkMan linkMan = session.get(LinkMan.class, 3l);session.delete(linkMan);tx.commit();}

6、一对多设置了双向关联产生多余的 SQL 语句

  在一对多 的操作中,如果设置了双向关联,会产生一些多余的 SQL 语句。

  怎么避免掉这个问题呢?在这里举一个例子。

  首先先让它重新建一下数据库表,

  接下来运行 demo1 的程序,往数据表中插入两个客户和三个联系人,让它重新建议下表。建好以后,再将配置给位 update。

public void demo1(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 创建两个客户Customer customer1 = new Customer();customer1.setCust_name("王东");Customer customer2 = new Customer();customer2.setCust_name("赵洪");// 创建三个联系人LinkMan linkMan1 = new LinkMan();linkMan1.setLkm_name("凤姐");LinkMan linkMan2 = new LinkMan();linkMan2.setLkm_name("如花");LinkMan linkMan3 = new LinkMan();linkMan3.setLkm_name("旺财");// 设置关系:
        linkMan1.setCustomer(customer1);linkMan2.setCustomer(customer1);linkMan3.setCustomer(customer2);customer1.getLinkMans().add(linkMan1);customer1.getLinkMans().add(linkMan2);customer2.getLinkMans().add(linkMan3);// 保存数据:
        session.save(linkMan1);session.save(linkMan2);session.save(linkMan3);session.save(customer1);session.save(customer2);tx.commit();}

现在可以在数据库中看到,两个客户和三个联系人。

原来2号联系人属于1号客户,现在要把它改成属于2号客户(没有用 update 方法,因为两个对象都是持久态对象,发生改变会自动更新数据可库)。

    @Test/*** 将2号联系人原来归1号客户,现在改为2号客户*/public void demo8(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 查询2号联系人LinkMan linkMan = session.get(LinkMan.class, 2l);// 查询2号客户Customer customer = session.get(Customer.class, 2l);// 双向的关联
        linkMan.setCustomer(customer);customer.getLinkMans().add(linkMan);tx.commit();}

看发送的数据库语句,数据库中已经修改成功,发了两条update语句,两次都修改了外键,为什么会改两回呢?

当你进行执行的时候,Hibernate 会有一级缓存区,有一块是缓存区,有一块是快照区。当你执行 get 查询的时候,它会将数据放到缓存区一份,并对这份数据进行快照。当事务提交的时候,Hibernate 会比对一级缓存区域和快照区的数据,如果不一致,会更新数据库。

都会进行更新外键,其实只要更新一次外键就可以了,所以在这里会产生多余的 SQL 语句

解决多余的 SQL语句

  单向维护,就用一边去做,比如只让客户关联联系人,或是只让联系人关联客户,这么做是可以的。但是这种方法在当前条件下做是可以的,但是有的地方还是不行还是会出现一些问题。

  还有一种方法是使一方放弃外键维护权,为什么会产生多余的 SQL,就是因为现在两边都可以维护这个外键。

  因为我们之需要更改一次外键就行了,所以必须让一方放弃外键维护权。那么,让哪一方放弃呢?

    一的一方放弃,为什么一的一方放弃呢。因为关系的维护应当由多的一方发起(让一个国家主席记住所有人的名字,和所有人记住国家主席的名字)。

  那怎么让一的一方放弃呢,就需要在一的一方的配置那,在 set 集合那加上一个配置 inverse,值设置为 true。

  那么放弃外键的维护权,在哪个地方还会用到呢?一对多的查询的修改的时候,现在还没有做带页面的,做带页面的时候,既有商品,又有分类。能查出分类,当你查出分类的时候,点击修改分类时候,会跳到一个界面,有分类的名称。。然后就提交了,当你提交过去以后,这个集合是空的,因为页面没有去提交集合;当你一修改,它会把这个分类下原来所有的商品的外键全都改空。这个时候,就必须放弃一的一方的外键维护权。

  减轻了服务器的压力,少发一些 SQL 语句。

区分cascade和inverse

    @Test/*** 区分cascade和inverse的区别*/public void demo9(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();Customer customer = new Customer();customer.setCust_name("李兵");LinkMan linkMan = new LinkMan();linkMan.setLkm_name("凤姐");customer.getLinkMans().add(linkMan);// 条件在Customer.hbm.xml上的set中配置了cascade="save-update" inverse="true"session.save(customer); // 客户会插入到数据库,联系人也会插入到数据库,但是外键为null
        tx.commit();}

  执行程序,客户会插入到数据库,联系人也会插入到数据库 ,但是外键为空。注意,cascade是操作关联对象的,也就是客户进去了,关联对象(联系热人)也会进去,这是由cascade控制的;但是具体有没有外键,是靠inverse去控制的。

Hibernate 的多对多关联映射

(一)、Hibernate 多对多关系的配置

多对多和一对多还有一点点的渊源,为什么这么说呢?其实就是因为如果你不会多对多,其实你也可以建两个一对多,也是可以的。

1、创建表

角色表

CREATE TABLE `sys_user` (`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',`user_code` varchar(32) NOT NULL COMMENT '用户账号',`user_name` varchar(64) NOT NULL COMMENT '用户名称',`user_password` varchar(32) NOT NULL COMMENT '用户密码',`user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

用户表

CREATE TABLE `sys_role` (`role_id` bigint(32) NOT NULL AUTO_INCREMENT,`role_name` varchar(32) NOT NULL COMMENT '角色名称',`role_memo` varchar(128) DEFAULT NULL COMMENT '备注',PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

现在有了用户表和角色表,一个客户可以选择多个角色(多对多关系),所以还需要有一张中间表。中间表就两个字段,一个是角色 id,一个是用户 id;分别创建两个外键,分别去指向角色表的主键和用户表的主键。

中间表

CREATE TABLE `sys_user_role` (`role_id` bigint(32) NOT NULL COMMENT '角色id',`user_id` bigint(32) NOT NULL COMMENT '用户id',PRIMARY KEY (`role_id`,`user_id`),KEY `FK_user_role_user_id` (`user_id`),CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、创建实体

用户的实体

package com.itheima.hibernate.domain;import java.util.HashSet;
import java.util.Set;/*** 用户的实体* @author jt*CREATE TABLE `sys_user` (`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',`user_code` varchar(32) NOT NULL COMMENT '用户账号',`user_name` varchar(64) NOT NULL COMMENT '用户名称',`user_password` varchar(32) NOT NULL COMMENT '用户密码',`user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;*/
public class User {private Long user_id;private String user_code;private String user_name;private String user_password;private String user_state;// 设置多对多关系:表示一个用户选择多个角色?// 放置的是角色的集合private Set<Role> roles = new HashSet<Role>();// 生成相应的get、setpublic Long getUser_id() {return user_id;}public void setUser_id(Long user_id) {this.user_id = user_id;}public String getUser_code() {return user_code;}public void setUser_code(String user_code) {this.user_code = user_code;}public String getUser_name() {return user_name;}public void setUser_name(String user_name) {this.user_name = user_name;}public String getUser_password() {return user_password;}public void setUser_password(String user_password) {this.user_password = user_password;}public String getUser_state() {return user_state;}public void setUser_state(String user_state) {this.user_state = user_state;}public Set<Role> getRoles() {return roles;}public void setRoles(Set<Role> roles) {this.roles = roles;}}

角色的实体

package com.itheima.hibernate.domain;import java.util.HashSet;
import java.util.Set;/*** 角色的实体* @author jt*CREATE TABLE `sys_role` (`role_id` bigint(32) NOT NULL AUTO_INCREMENT,`role_name` varchar(32) NOT NULL COMMENT '角色名称',`role_memo` varchar(128) DEFAULT NULL COMMENT '备注',PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;*/
public class Role {private Long role_id;private String role_name;private String role_memo;// 一个角色被多个用户选择:// 放置的是用户的集合private Set<User> users = new HashSet<User>();public Long getRole_id() {return role_id;}public void setRole_id(Long role_id) {this.role_id = role_id;}public String getRole_name() {return role_name;}public void setRole_name(String role_name) {this.role_name = role_name;}public String getRole_memo() {return role_memo;}public void setRole_memo(String role_memo) {this.role_memo = role_memo;}public Set<User> getUsers() {return users;}public void setUsers(Set<User> users) {this.users = users;}}

  现在实体的一些基本的属性已经建好了,但是现在我们要创建的是一个多对多关系的实体。也就是一个用户可以选择多个角色,那么如何去设置多对多的关系呢?多对多关系设置的时候,要想表示之间的关系,放置的都是对方的集合。

3、创建映射

用户的映射

<?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.itheima.hibernate.domain.User" table="sys_user"><!-- 建立OID与主键的映射 --><id name="user_id" column="user_id"><generator class="native"/></id><!-- 建立普通属性与字段映射 --><property name="user_code" column="user_code"/><property name="user_name" column="user_name"/><property name="user_password" column="user_password"/><property name="user_state" column="user_state"/><!-- 建立与角色的多对多的映射关系 --><!-- set标签* name        :对方的集合的属性名称。* table        :多对多的关系需要使用中间表,放的是中间表的名称。--><set name="roles" table="sys_user_role" cascade="save-update,delete"  ><!-- key标签:* column    :当前的对象对应中间表的外键的名称。--><key column="user_id"/><!-- many-to-many标签:* class        :对方的类的全路径* column    :对方的对象在中间表中的外键的名称。--><many-to-many class="com.itheima.hibernate.domain.Role" column="role_id"/></set></class>
</hibernate-mapping>

角色的映射

<?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.itheima.hibernate.domain.Role" table="sys_role"><!-- 建立OID与主键的映射 --><id name="role_id" column="role_id"><generator class="native"/></id><!-- 建立普通属性与字段的映射 --><property name="role_name" column="role_name"/><property name="role_memo" column="role_memo"/><!-- 与用户的多对多的映射关系 --><!-- set标签* name        :对方的集合的属性名称。* table        :多对多的关系需要使用中间表,放的是中间表的名称。--><set name="users" table="sys_user_role" cascade="save-update,delete" inverse="true"><!-- key标签:* column    :当前的对象对应中间表的外键的名称。--><key column="role_id"/><!-- many-to-many标签:* class        :对方的类的全路径* column    :对方的对象在中间表中的外键的名称。--><many-to-many class="com.itheima.hibernate.domain.User" column="user_id"/></set></class>
</hibernate-mapping>

  映射配置好以后,别忘了,需要把映射文件添加到核心配置里面去 。

        <mapping resource="com/itheima/hibernate/domain/User.hbm.xml"/><mapping resource="com/itheima/hibernate/domain/Role.hbm.xml"/>    

4、编写测package com.itheima.hibernate.demo2;import org.hibernate.Session;import org.hibernate.Transaction;import org.junit.Test;

import com.itheima.hibernate.domain.Role;
import com.itheima.hibernate.domain.User;
import com.itheima.hibernate.utils.HibernateUtils;/*** Hibernate的多对多的映射* @author jt**/
public class HibernateDemo2 {@Test/*** 保存多条记录:保存多个用户和角色*/public void demo1(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 创建2个用户User user1 = new User();user1.setUser_name("赵洪");User user2 = new User();user2.setUser_name("李兵");// 创建3个角色Role role1 = new Role();role1.setRole_name("研发部");Role role2 = new Role();role2.setRole_name("市场部");Role role3 = new Role();role3.setRole_name("公关部");// 设置双向的关联关系:
        user1.getRoles().add(role1);user1.getRoles().add(role2);user2.getRoles().add(role2);user2.getRoles().add(role3);role1.getUsers().add(user1);// 获得用户的集合,双向关联关系就建好了role2.getUsers().add(user1);role2.getUsers().add(user2);role3.getUsers().add(user2);// 保存操作:多对多建立了双向的关系必须有一方放弃外键维护。// 一般是被动方放弃外键维护权。
        session.save(user1);session.save(user2);// 角色也要保存,因为没有配置级联;session.save(role1);session.save(role2);session.save(role3);// 这样一执行,就会出错(一对多也是这样设置的,不会出错)// 设置了双向的关联关系,在一对多的时候,是做了两次修改,关系维护是靠多的一方的外键,修改外键就可以了,修改几次都没关系;// 但是,多对多的关系想要去维护,靠的是中间表,中间表关系的维护是往里面插入// 两个字段共同决定一条记录,共同作为主键,两个字段不能同时重复。必须有一方放弃外键维护权-// 一般是被动方放弃外键维护权,像我们用户选角色,角色一般是被动的;所以我们需要在角色这边的配置里,在set这需要添加inverse=true

// 如果单向关联,这种是可以的;但有的时候需要双向关联,双向关联的时候,让一方放弃就可以了。tx.commit();}
}

多对多的操作,只保存一边是否可以?不可以,瞬时对象异常  如果想要执行,需要设置级联
    @Test/*** 多对多的操作:* * 只保存一边是否可以?不可以,瞬时对象异常*/public void demo2(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 创建2个用户User user1 = new User();user1.setUser_name("赵洪");// 创建3个角色Role role1 = new Role();role1.setRole_name("研发部");// 设置双向的关联关系:
        user1.getRoles().add(role1);role1.getUsers().add(user1);// 只保存用户:// session.save(user1);
        session.save(role1);tx.commit();}

多对多的级联保存或更新  保存那个对象,外键也需要交给它来维护
@Test/*** 多对多的级联保存:* * 保存用户级联保存角色。在用户的映射文件中配置。* * 在User.hbm.xml中的set上配置 cascade="save-update"*/public void demo3(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 创建2个用户User user1 = new User();user1.setUser_name("赵洪");// 创建3个角色Role role1 = new Role();role1.setRole_name("研发部");// 设置双向的关联关系:
        user1.getRoles().add(role1);role1.getUsers().add(user1);// 只保存用户:
        session.save(user1);tx.commit();}/*** 多对多的级联保存:* * 保存角色级联保存用户。在角色的映射文件中配置。* * 在Role.hbm.xml中的set上配置 cascade="save-update"*/@Testpublic void demo4(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 创建2个用户User user1 = new User();user1.setUser_name("李兵");// 创建3个角色Role role1 = new Role();role1.setRole_name("公关部");// 设置双向的关联关系:
        user1.getRoles().add(role1);role1.getUsers().add(user1);// 只保存用户:
        session.save(role1);tx.commit();}

多对多的级联删除(基本用不上)

    /*** 多对多的级联删除:* * 删除用户级联删除角色* * 在User.hbm.xml中的set上配置 cascade="delete"*/@Testpublic void demo5(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 查询1号用户:User user  = session.get(User.class, 1l);session.delete(user);tx.commit();}/*** 多对多的级联删除:* * 删除角色级联删除用户* * 在Role.hbm.xml中的set上配置 cascade="delete"*/@Testpublic void demo6(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 查询2号角色:Role role  = session.get(Role.class, 2l);session.delete(role);tx.commit();}

给用户选择角色,或给用户改选角色,或是给用户删除角色。

    @Test/*** 给用户选择角色*/public void demo7(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 给1号用户多选2号角色// 查询1号用户User user  = session.get(User.class, 1l);// 查询2号角色Role role = session.get(Role.class, 2l);user.getRoles().add(role);tx.commit();}@Test/*** 给用户改选角色*/public void demo8(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 给2号用户将原有的2号角色改为3号角色// 查询2号用户User user  = session.get(User.class, 2l);// 查询2号角色Role role2 = session.get(Role.class, 2l);Role role3 = session.get(Role.class, 3l);user.getRoles().remove(role2);user.getRoles().add(role3);tx.commit();}@Test/*** 给用户改选角色*/public void demo9(){Session session = HibernateUtils.getCurrentSession();Transaction tx = session.beginTransaction();// 给2号用户删除1号角色// 查询2号用户User user  = session.get(User.class, 2l);// 查询2号角色Role role = session.get(Role.class, 1l);user.getRoles().remove(role);tx.commit();}

两者之间关系的维护,主要靠的是集合。操作集合,就可以操作他们之间的关系,所以说多对多一般进行操作的时候,都操作的是集合。

转载于:https://www.cnblogs.com/xifengbuqi/p/9546276.html

Hibernate(三) - hibernate 表操作-多对多配置相关推荐

  1. 多对多关系需要建立中间表_【数据库基础】为什么需要三张表之多对多表结构设计...

    了解完一对一和一对多表结构设计,接下来一起了解一下多对多的表结构设计. 同样,咱们先来想一般什么场景需要用到多对多.假如说咱们有一个叫订单和一个叫商品的这两张表,这两张表的关系,它其实就是一个多对多的 ...

  2. mysql三:表操作

    阅读目录 一 存储引擎介绍 二 表介绍 三 创建表 四 查看表结构 五 数据类型 六 表完整性约束 七 修改表ALTER TABLE 八 复制表 九 删除表 一 存储引擎介绍 存储引擎即表类型,mys ...

  3. 三 .数据库(表操作)

    一 .数据表(table) 1.数据表操作 创建表: 语法: create table 表名( 字段名1 类型[(宽度) 约束条件], 字段名2 类型[(宽度) 约束条件], 字段名3 类型[(宽度) ...

  4. mysql创建三个表_mysql三:表操作

    示例: mysql> usedb2 Readingtable information for completion of table and columnnames You can turnof ...

  5. Django学习手册 - ORM 数据创建/表操作 汇总

    ORM 查询的数据类型: QuerySet与惰性机制(可以看作是一个列表) 所谓惰性机制:表名.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它 ...

  6. Hibernate框架--学习笔记(中):一对多配置、多对多配置

    一.一对多: 一个客户可以有多个联系人,一个联系人只能属于一个客户. 1.一对多映射配置: (1)创建实体类 //客户类:一个客户可以有多个联系人,一个联系人只能属于一个客户 public class ...

  7. Hibernate②一(多)对多的映射配置与级联操作

    文章目录 1 一对多(部门对员工) 1.0 确定一对多中的关系 1.1 编写实体类 1.2 编写映射配置文件与核心配置文件 2 多对多(学生对教师) 2.0 确定多对多中的关系 2.1 编写实体类 2 ...

  8. (转)Hibernate中的多表操作

    http://blog.csdn.net/yerenyuan_pku/article/details/70556208 Hibernate中的多表操作 在实际开发中,我们不可能只是简简单单地去操作单表 ...

  9. hibernate课程 初探单表映射3-1 hibernate单表操作简介

    本章简介: 1 单一主键 2 基本类型 3 对象类型 4 组件属性 5 单表操作CRUD实例 转载于:https://www.cnblogs.com/1446358788-qq/p/8232078.h ...

最新文章

  1. 我问占小狼到底什么是面向对象编程?他转头就走。
  2. hsrp热备路由协议实验
  3. 移动端 元素外面使用伪类after加边框 导致其内部元素无法选中
  4. Uva 11732 strcmp()函数
  5. 在微型计算机机箱的面板上,【简评】全侧透快拆设计,迎广101机箱体验
  6. 解决no such file or directory的问题
  7. 训练作用_感觉统合是什么意思,感觉统合训练有什么作用
  8. eclipse-中如何显示工程树形结构
  9. HCIA-IoT V2.5物联网初级考试总结(附题库,历年真题,刷题软件)
  10. # python # # 分形 # Sierpinski Carpet 谢尔宾斯基地毯
  11. 【课堂笔记】模型制作流程
  12. 储存数据与操作Excel [爬虫专题(9)]
  13. matlab lud矩阵分解,MIT线性代数总结笔记——LU分解
  14. 欧姆定理验证,电阻的电压/电流/功率,电阻串联,电阻并联,电阻分压
  15. 密码学Kerberos协议理解
  16. list 根据相同字段合并数据
  17. 嵌入式GUI及其开发工具——miniGUI和mStudio
  18. Ubuntu MATE 19.10-远程办公启示录
  19. 深入学习RTC时钟库 DS1302
  20. EDIUS设置字幕时要注意哪些

热门文章

  1. C#与数据库访问技术总结(十七)
  2. HDU - 1723 - Distribute Message
  3. 转 ABAP_ALV_Function方式与OO方式(较为简单、普通的ALV)
  4. 加个属性让div成纵向横向无缝滚动(支持IE和FF)
  5. 我的vscode配置 利用Settings Sync一键安装
  6. PHP递归实现无限极分类
  7. 干不掉的钉钉:从哪来,往哪去?
  8. C# 委托链、多路广播委托
  9. 《SQL初学者指南(第2版)》——2.4 指定列
  10. 20155222卢梓杰 《Java程序设计》第1周学习总结