hibernate 一对多、多对多操作

表之间的关系

  1. 一对多

    一个部门有多个员工,一个员工只能属于某一个部门
    一个班级有多个学生,一个学生只能属于一个班级

  2. 多对多

    一个老师教多个学生,一个学生可以被多个老师教
    一个学生可以先择多门课程,一门课程可以被多个学生选择
    一个用户可以选择多个角色,一个角色也可以被多个用户选择

  3. 一对一

    一个公司只能对应一个注册地址

表之间关系建表原则

  1. 一对多

    在多的一方创建一个外键,指向一的一方的主键

  2. 多对多

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

  3. 一对一

    唯一外键对应或主键对应

多表操作

一对多
  1. 建立表

    CREATE TABLE `linkman` (
      `link_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
      `link_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
      `link_cust_id` bigint(32) NOT NULL COMMENT '客户id',
      `link_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
      `link_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
      `link_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
      `link_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
      `link_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
      `link_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
      `link_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
      PRIMARY KEY (`link_id`)
    ) comment '联系人表' ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    
    CREATE TABLE `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`)
    ) comment '客户表' ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
  2. 创建实体类并设置表之间的关系

    /**
     * 客户实体类
     * @author zhou
     * @create 2019/11/19/21:33
     * @class com.zhou.domian.Linkman
     */
    @Getter
    @Setter
    public class Customer {
    
        private long cust_id;
        private String cust_name;
        private String cust_source;
        private String cust_industry;
        private String cust_level;
        private String cust_phone;
        private String cust_mobile;
    
        // 一个客户有多个联系人
        private Set<Linkman> linkmens = new HashSet<>();
    
    }
    
    /**
     * 联系人实体类
     * @author zhou
     * @create 2019/11/19/21:11
     * @class com.zhou.domian.Linkman
     */
    @Getter
    @Setter
    public class Linkman {
        private Long link_id;
        private String link_name;
        private String link_gender;
        private String link_phone;
        private String link_mobile;
        private String link_email;
        private String link_qq;
        private String link_position;
        private String link_memo;
    
        // 联系人表关联一个客户
        private Customer customer;
    }
    
  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.zhou.domian.Customer" table="customer" schema="hibernate">
        <!-- 建立那个字段是主键 -->
        <id name="cust_id" column="cust_id" >
            <!-- 主键的生成策略 -->
            <generator class="native"/>
        </id>
        <!-- 建立POJO类字段和数据库字段关联 -->
        <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属性: 多的一方的集合的属性名称  -->
        <set name="linkmens">
            <!-- key的 column表示多的一方外键名 -->
            <key column="link_cust_id"></key>
            <!-- one-to-many的class属性表示多的一方类的全限定名 -->
            <one-to-many class="com.zhou.domian.Linkman"></one-to-many>
        </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.zhou.domian.Linkman" table="linkman" schema="hibernate">
            <id name="link_id" column="link_id">
                <generator class="native"></generator>
            </id>
            <property name="link_name" column="link_name"/>
            <property name="link_gender" column="link_gender"/>
            <property name="link_phone" column="link_phone"/>
            <property name="link_mobile" column="link_mobile"/>
            <property name="link_email" column="link_email"/>
            <property name="link_qq" column="link_qq"/>
            <property name="link_position" column="link_position"/>
            <property name="link_memo" column="link_memo"/>
    
            <!--多对一
                name: 一的一方对象的名字
                class: 一的一方类的全限定名
                column: 多的一方外键ID的名称
            -->
            <many-to-one name="customer" column="link_cust_id" class="com.zhou.domian.Customer" />
        </class>
    
    </hibernate-mapping>
    
  4. 创建核心配置文件

    <!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>
            <!-- 设置连接数据库的url和用户名和密码和数据库驱动-->
            <property name="url">jdbc:mysql:///hibernate</property>
            <property name="username">root</property>
            <property name="password">123456</property>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <!-- 打印SQL语句 -->
            <property name="hibernate.show_sql">true</property>
            <!-- 格式化SQL -->
            <property name="hibernate.format_sql">true</property>
            <!-- 设置hibernate的方言 -->
            <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
            <!-- 设置自动创建表 -->
            <property name="hibernate.hbm2ddl.auto">create</property>
    
            <!--阿里巴巴 Druid 连接池 注意:如果使用Druid连接池的话需要把数据基本连接属性改为Druid的属性-->
            <property name="hibernate.connection.provider_class">
                com.alibaba.druid.support.hibernate.DruidConnectionProvider
            </property>
    
            <!-- 配置初始化大小、最小、最大 -->
            <property name="initialSize">1</property>
            <property name="minIdle">5</property>
            <property name="maxActive">20</property>
            <!-- 配置获取连接等待超时的时间 -->
            <property name="maxWait">60000</property>
            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis">60000</property>
            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
            <property name="minEvictableIdleTimeMillis">300000</property>
    
            <!-- 设置事务隔离级别 -->
            <property name="hibernate.connection.isolation">3</property>
    
            <!-- 创建一个session绑定到当前线程 -->
            <property name="current_session_context_class">thread</property>
    
            <!-- 加载映射(mapper)文件 -->
            <mapping resource="mapper/customer.hbm.xml"></mapping>
            <mapping resource="mapper/linkman.hbm.xml"/>
        </session-factory>
    </hibernate-configuration>
    
  5. 引入工具类

    package com.zhou.utils;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    /**
     * @author zhou
     * @create 2019/11/12/21:21
     * @class com.zhou.utils.HibernateUtils
     */
    public class HibernateUtils {
    
        public static final SessionFactory sessionFactory;
    
        static {
            // 读取配置,并创建工厂类SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        }
    
        /**
         * 获取Session对象
         * @return Session
         */
        public static Session getSession () {
            return sessionFactory.openSession();
        }
    
        /**
         * 使用时必须要在hibernate核心配置文件中配置
         * <property name="current_session_context_class">thread</property>
         * 获取Session对象,不过是从内部已经绑定好了ThreadLocal,获取的Session对象
         * @return Session
         */
        public static Session getCurrentSession(){
            return sessionFactory.getCurrentSession();
        }
    
    }
    
  6. 编写测试类

    级联操作与懒加载

    注意: 谁需要级联操作,就在mapper(映射文件)中加cascade属性


    级联的配置属性

    6.1 级联查询与懒加载

    <many-to-one name="customer" column="link_cust_id" class="com.zhou.domian.Customer" lazy="proxy" />
    
       /**
        * @author zhou
        * @create 2019/11/19/23:15
        * @class com.zhou.test.HibernateORMTest
        */
       public class HibernateORMTest {
       
           /**
            * 级联查询与懒加载
            */
           @Test
           public void get(){
               Session session = HibernateUtils.getCurrentSession();
               Transaction transaction = session.beginTransaction();
               Linkman linkman = session.get(Linkman.class, 1L);
               transaction.commit();
       
               System.out.println(linkman.getLink_name());
               /**
                * org.hibernate.LazyInitializationException: could not initialize proxy [com.zhou.domian.Customer#1] - no Session
                * 报这个错误是因为使用hibernate默认使用了懒加载的方式,要使用到另外一个对象时才会发送SQL去查询,
                * 而getCurrentSession()方法取出来的Session在commit后,会自动把session.close()掉,所以报no session的错误
                * 如果不想要对象懒加载可以在mapper配置文件中配置,lazy: 设置是否懒加载,默认为proxy(懒加载),false为不启用懒加载
                * <many-to-one name="customer" column="link_cust_id" class="com.zhou.domian.Customer" lazy="proxy" />
                */
               System.out.println(linkman.getCustomer().getCust_name());
           }
       }
    

    6.2. 级联保存

       <set name="linkmens" cascade="save-update" lazy="false">
               <!-- key的 column表示多的一方外键名 -->
               <key column="link_cust_id"></key>
               <!-- one-to-many的class属性表示多的一方类的全限定名 -->
               <one-to-many class="com.zhou.domian.Linkman"></one-to-many>
       </set> 
    
      /**
         * @author zhou
         * @create 2019/11/19/23:15
         * @class com.zhou.test.HibernateORMTest
         */
        public class HibernateORMTest {
        
            /**
             * 一对多的级联保存save, 双向绑定,不需要设置cascade属性
             */
            @Test
            public void save () {
        
                // 获取Session对象并开启事务
                Session session = HibernateUtils.getCurrentSession();
                Transaction transaction = session.beginTransaction();
        
                // 创建实体类并设置关系
                Customer customer1 = new Customer();
                Customer customer2 = new Customer();
                Customer customer3 = new Customer();
        
                customer1.setCust_name("customer1");
                customer2.setCust_name("customer2");
                customer3.setCust_name("customer3");
        
                Linkman linkman1 = new Linkman();
                Linkman linkman2 = new Linkman();
                Linkman linkman3 = new Linkman();
        
                linkman1.setLink_name("linkman1");
                linkman2.setLink_name("linkman2");
                linkman3.setLink_name("linkman3");
        
        
                linkman1.setCustomer(customer1);
                linkman2.setCustomer(customer1);
                linkman3.setCustomer(customer3);
        
                customer1.getLinkmens().add(linkman1);
                customer1.getLinkmens().add(linkman2);
                customer2.getLinkmens().add(linkman3);
        
                // 保存数据
                session.save(customer1);
                session.save(customer2);
                session.save(customer3);
        
                session.save(linkman1);
                session.save(linkman2);
                session.save(linkman3);
        
                // 提交事务
                transaction.commit();
        
            }
        
            /**
             * 一对多的级联保存save, 单项绑定
             */
            @Test
            public void saveOne () {
        
                // 获取Session对象并开启事务
                Session session = HibernateUtils.getCurrentSession();
                Transaction transaction = session.beginTransaction();
        
                // 创建实体类并设置关系,单项绑定
                Customer customer1 = new Customer();
                Customer customer2 = new Customer();
                Customer customer3 = new Customer();
        
                customer1.setCust_name("customer1");
                customer2.setCust_name("customer2");
                customer3.setCust_name("customer3");
        
                Linkman linkman1 = new Linkman();
                Linkman linkman2 = new Linkman();
                Linkman linkman3 = new Linkman();
        
                linkman1.setLink_name("linkman1");
                linkman2.setLink_name("linkman2");
                linkman3.setLink_name("linkman3");
        
                /**
                 * java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance -
                 * save the transient instance before flushing: com.zhou.domian.Linkman
                 * 持久态对象关联了一个瞬时态对象,就会报瞬时对象异常
                 * 如果想要支持单项绑定,在mapper配置文件中添加cascade="save-update"
                 * set标签的name属性: 多的一方的集合的属性名称
                 * <set name="linkmens" cascade="save-update">
                 *      key的 column表示多的一方外键名
                 *      <key column="link_cust_id"></key>
                 *       one-to-many的class属性表示多的一方类的全限定名
                 *      <one-to-many class="com.zhou.domian.Linkman"></one-to-many>
                 * </set>
                 *
                 */
                customer1.getLinkmens().add(linkman1);
                customer1.getLinkmens().add(linkman2);
                customer2.getLinkmens().add(linkman3);
        
                // 保存数据
                session.save(customer1);
                session.save(customer2);
                session.save(customer3);
        
                // 提交事务
                transaction.commit();
        
            }
        }  
    
    

    6.3. 级联删除

           <set name="linkmens" cascade="delete" lazy="false">
               <!-- key的 column表示多的一方外键名 -->
               <key column="link_cust_id"></key>
               <!-- one-to-many的class属性表示多的一方类的全限定名 -->
               <one-to-many class="com.zhou.domian.Linkman"></one-to-many>
           </set>
    
       public class HibernateORMTest {
    
           /**
            * 级联删除
            */
           @Test
           public void delete() {
               // 获取Session对象并开启事务
               Session session = HibernateUtils.getCurrentSession();
               Transaction transaction = session.beginTransaction();
               // 进行查询并删除,如果没有设置级联属性则不会级联删除,只会把附属表的属性先更新为null,在删除主表中的数据
               Customer customer = session.get(Customer.class, 1L);
               session.delete(customer);
               // 提交事务
               transaction.commit();
           }
       }
    

    6.4. 外键维护权inverse

       <!-- inverse="true" 使对象放弃外键维护-->
       <!-- set标签的name属性: 多的一方的集合的属性名称  -->
       <set name="linkmens" cascade="save-update, delete" inverse="true" lazy="false">
           <!-- key的 column表示多的一方外键名 -->
           <key column="link_cust_id"></key>
           <!-- one-to-many的class属性表示多的一方类的全限定名 -->
           <one-to-many class="com.zhou.domian.Linkman"></one-to-many>
       </set>
    
     public class HibernateORMTest{
          /**
           * 级联更新外键
           * inverse外加维护权,双向绑定使用
           */
          @Test
          public void updateInverse() {
              // 获取Session对象并开启事务
              Session session = HibernateUtils.getCurrentSession();
              Transaction transaction = session.beginTransaction();
              Customer customer2 = session.get(Customer.class, 2L);
              Linkman linkman2 = session.get(Linkman.class, 2L);
              // 双向绑定,如果双向绑定,hibernate会发送两个sql来更改同一个数据,这就造成数据库的压力,需要进行优化
              // 如果想要优化,那就让一方放弃外键维护,需要设置inverse属性让一方放弃,默认是false,false是不放弃, true是放弃
              customer2.getLinkmens().add(linkman2);
              linkman2.setCustomer(customer2);
              // 提交事务
              transaction.commit();
      
          }
      
          /**
           * 级联更新外键
           * 单项绑定
           */
          @Test
          public void update() {
              // 获取Session对象并开启事务
              Session session = HibernateUtils.getCurrentSession();
              Transaction transaction = session.beginTransaction();
              Customer customer2 = session.get(Customer.class, 2L);
              Linkman linkman2 = session.get(Linkman.class, 2L);
              // 单项绑定, 只需要一个对象进行绑定.
              customer2.getLinkmens().add(linkman2);
              // linkman2.setCustomer(customer2);
              // 提交事务
              transaction.commit();
          }
     }
    
多对多
  1. 建立表

       # 用户表
           #一个用户可以有多个角色
            CREATE TABLE `user` (
                  `user_id` bigint(20) NOT NULL AUTO_INCREMENT,
                  `user_code` varchar(255) DEFAULT NULL,
                  `user_name` varchar(255) DEFAULT NULL,
                  `user_password` varchar(255) DEFAULT NULL,
                  `user_state` varchar(255) DEFAULT NULL,
                  PRIMARY KEY (`user_id`)
                ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
        #角色表
           #一个角色可以被多个用户选择
            CREATE TABLE `role` (
                  `role_id` bigint(20) NOT NULL AUTO_INCREMENT,
                  `role_name` varchar(255) DEFAULT NULL,
                  `role_memo` varchar(255) DEFAULT NULL,
                  PRIMARY KEY (`role_id`)
                ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
       # 中间表
               CREATE TABLE `user_role` (
                 `role_id` bigint(20) NOT NULL,
                 `user_id` bigint(20) NOT NULL,
                 PRIMARY KEY (`user_id`,`role_id`),
                 CONSTRAINT `user_id_fk` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`),
                 CONSTRAINT `role_id_fk` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`)
               ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 
    

    关系图


    关系图.png
  2. 创建实体类并建立关系

       /**
        * @author zhou
        * @create 2019/11/24/19:33
        * @class com.zhou.domian.User
        */
       @Getter
       @Setter
       public class User {
           private long user_id;
           private String user_code;
           private String user_name;
           private String user_password;
           private String user_state;
       
           /**
            * 建立用户对应多个角色的映射
            */
           Set<Role> roles = new HashSet<>();
       }
    
       /**
        * @author zhou
        * @create 2019/11/24/19:33
        * @class com.zhou.domian.Role
        */
       @Getter
       @Setter
       public class Role {
           private long role_id;
           private String role_name;
           private String role_memo;
       
           /**
            * 建立多角色对应多用户
            */
           private Set<User> users = new HashSet<>();
       }
    
  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.zhou.domian.User" table="user" schema="hibernate">
              <id name="user_id" column="user_id">
                  <generator class="native"></generator>
              </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"/>
      
      
              <!--
                  name: 是当前集合属性名称
                  table: 是多对多中间表
                  <key column="role_id"></key>
                  当前表的外键
                  <many-to-many column="role_id" class="com.zhou.domian.Role"/>
                  column: 集合中对象的外键
                  class: 集合中对象的全路径
              -->
              <set name="roles" table="user_role" cascade ="save-update">
                  <key column="user_id"></key>
                  <many-to-many class="com.zhou.domian.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.zhou.domian.Role" table="role" schema="hibernate">
                <!-- 建立那个字段是主键 -->
                <id name="role_id" column="role_id">
                    <!-- 主键的生成策略 -->
                    <generator class="native"/>
                </id>
                <!-- 建立POJO类字段和数据库字段关联 -->
                <property name="role_name" column="role_name"/>
                <property name="role_memo" column="role_memo"/>
                <!--
                    name: 是当前集合属性名称
                    table: 是多对多中间表
                    <key column=""></key>
                    当前表的外键
                    <many-to-many column="" class=""/>
                    column: 集合中对象的外键
                    class: 集合中对象的全路径
                -->
                <set name="users" table="user_role" inverse="false">
                    <key column="role_id"></key>
                    <many-to-many column="user_id" class="com.zhou.domian.User"/>
                </set>
            </class>
    </hibernate-mapping>
    
  4. 在核心配置文件当中添加两个新配置

    <!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>
            <!-- 设置连接数据库的url和用户名和密码和数据库驱动-->
            <property name="url">jdbc:mysql:///hibernate</property>
            <property name="username">root</property>
            <property name="password">123456</property>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <!-- 打印SQL语句 -->
            <property name="hibernate.show_sql">true</property>
            <!-- 格式化SQL -->
            <property name="hibernate.format_sql">true</property>
            <!-- 设置hibernate的方言 -->
            <property name="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</property>
            <!-- 设置自动创建表 -->
            <property name="hibernate.hbm2ddl.auto">update</property>
    
            <!--阿里巴巴 Druid 连接池 注意:如果使用Druid连接池的话需要把数据基本连接属性改为Druid的属性-->
            <property name="hibernate.connection.provider_class">
                com.alibaba.druid.support.hibernate.DruidConnectionProvider
            </property>
    
            <!-- 配置初始化大小、最小、最大 -->
            <property name="initialSize">1</property>
            <property name="minIdle">5</property>
            <property name="maxActive">20</property>
            <!-- 配置获取连接等待超时的时间 -->
            <property name="maxWait">60000</property>
            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis">60000</property>
            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
            <property name="minEvictableIdleTimeMillis">300000</property>
    
            <!-- 设置事务隔离级别 -->
            <property name="hibernate.connection.isolation">3</property>
    
            <!-- 创建一个session绑定到当前线程 -->
            <property name="current_session_context_class">thread</property>
    
            <!-- 加载映射(mapper)文件 -->
            <!--一对多的映射文件-->
            <mapping resource="mapper/customer.hbm.xml"/>
            <mapping resource="mapper/linkman.hbm.xml"/>
            <!--添加多对多的映射文件-->
            <mapping resource="mapper/role.hbm.xml"/>
            <mapping resource="mapper/user.hbm.xml"/>
        </session-factory>
    </hibernate-configuration>
    
    
  5. 编写测试类

    public class HibernateManyAndMany {
    
       /**
        * 双向维护
        * 级联保存,双向维护,从表开启放弃外键维护, 如在mapper映射文件中的多对多属性中加上inverse="false",放弃外键维
        * 双向维护时,必须要有一方放弃外键维护
        * 如果两边都有维护的话, 就会有重复的的记录,由于关系表是两个字段作为共同主键,不能有相同的记录
        * 解决办法
        * 通常都是让被动方放弃,用户选角色,角色为被动方
        */
        @Test
        public void save() {
            // 获取Session对象并开启事务
            Session currentSession = HibernateUtils.getCurrentSession();
            Transaction transaction = currentSession.beginTransaction();
    
            User user1 = new User();
            user1.setUser_name("user1");
            User user2 = new User();
            user2.setUser_name("user2");
            User user3 = new User();
            user3.setUser_name("user3");
    
            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(role3);
            user2.getRoles().add(role1);
            user3.getRoles().add(role2);
    
            currentSession.save(user1);
            currentSession.save(user2);
            currentSession.save(user3);
            // 放弃外键维护
            currentSession.save(role1);
            currentSession.save(role2);
            currentSession.save(role3);
    
            // 提交事务
            transaction.commit();
        }
    }
    
    /**
    * @author zhou
    * @create 2019/11/24/20:01
    * @class com.zhou.test.HibarnateManyAndMany
    */
    public class HibernateManyAndMany {
       /**
        * 级联保存,单项绑定, 单项绑定需要配置cascade="save-update"
        */
       @Test
       public void saveOne() {
           // 获取Session对象并开启事务
           Session currentSession = HibernateUtils.getCurrentSession();
           Transaction transaction = currentSession.beginTransaction();
    
           User user1 = new User();
           user1.setUser_name("user1");
           User user2 = new User();
           user2.setUser_name("user2");
           User user3 = new User();
           user3.setUser_name("user3");
    
           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(role3);
           user2.getRoles().add(role1);
           user3.getRoles().add(role2);
    
           currentSession.save(user1);
           currentSession.save(user2);
           currentSession.save(user3);
    
           // 提交事务
           transaction.commit();
       }
    }
    

    多对多的级联操作和一对多的级联操作是一样的

    /**
     * @author zhou
     * @create 2019/11/24/20:01
     * @class com.zhou.test.HibarnateManyAndMany
     */
    public class HibernateManyAndMany {
    
        /**
         * 添加角色,双向绑定,让一方放弃外键维护,需要设置inverse属性让一方放弃,默认是false,false是不放弃, true是放弃
         * 如果不让一方放弃外键维护的话,会报错
         */
        @Test
        public void addRole() {
            // 获取Session并开启事务
            Session session = HibernateUtils.getCurrentSession();
            Transaction transaction = session.beginTransaction();
    
            // 用户3添加角色1
            User user3 = session.get(User.class, 3L);
            Role role1 = session.get(Role.class, 1L);
            user3.getRoles().add(role1);
            role1.getUsers().add(user3);
            // 提交事务
            transaction.commit();
        }
    
    
        /**
         * 单项维护,添加角色
         */
        @Test
        public void addRoleOne() {
            // 获取Session并开启事务
            Session session = HibernateUtils.getCurrentSession();
            Transaction transaction = session.beginTransaction();
    
            // 用户3添加角色1
            User user3 = session.get(User.class, 3L);
            Role role1 = session.get(Role.class, 1L);
            user3.getRoles().add(role1);
            // 提交事务
            transaction.commit();
        }
    
    
        /**
         * 关系操作,就是操作内部的集合
         * 把用户1的角色3修改为角色2
         */
        @Test
        public void UpdateUserAndRole() {
            // 获取Session并开启事务
            Session session = HibernateUtils.getCurrentSession();
            Transaction transaction = session.beginTransaction();
    
            // 把用户1的角色3修改为角色2
            User user1 = session.get(User.class, 1L);
            Role role2 = session.get(Role.class, 2L);
            Role role3 = session.get(Role.class, 3L);
            // 删除角色2
            user1.getRoles().remove(role3);
            // 添加角色3
            user1.getRoles().add(role2);
    
            // 提交事务
            transaction.commit();
        }
    }
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容

  • 本文包括:1、一对多结构的准备2、双向关联与单向关联3、级联保存4、级联删除5、cascade 属性——级联6、i...
    廖少少阅读 1,222评论 1 6
  • 本文包括:1、Hibernate 的查询方式2、HQL (Hibernate Query Language) 查询...
    廖少少阅读 2,646评论 0 15
  • layout: posttitle: hibernatesubtitle: 用法date: ...
    虫儿飞ZLEI阅读 323评论 0 1
  • 本文包括: 1、CRM 项目的整体介绍 2、Hibernate 框架概述 3、Hibernate 快速入门 4、H...
    廖少少阅读 3,462评论 9 66
  • 本文包括:1、Hibernate的持久化类2、Hibernate 持久化对象的三个状态(难点)3、Hibernat...
    廖少少阅读 1,433评论 0 13