Hibernate3入门之第三章Hibernate关联关系的映射

Hibernate3入门之第三章Hibernate关联关系的映射

实体之间的关系:

实体之间有三种关系:

  • 一对多

    ​ 一个用户,生成多个订单,每一个订单只能属于一个用户.

    ​ 建表原则:

    ​ 在多的一方创建一个字段,作为外键,指向一的一方的主键.

  • 多对多

    ​ 一个学生可以选择多门课程,一个课程可以被多个学生选择.

    ​ 建表原则:

    ​ 建第三张表,中间表至少有两个字段,分别作为外键指向多对多双方主键.

  • 一对一(特殊.最少.)

    ​ 一个公司只能有一个注册地址,一个注册地址,只能被一个公司使用.(否则将两个表建到一个表.)

    ​ 建表原则

    • 唯一外键:

      ​ 一对一的双方,假设一方是多的关系.需要在多的一方创建一个字段,作为外键.指向一的一方的主键.但是在外键添加一个unique.

    • 主键对应:

      ​ 一对一的双方,通过主键进行关联.

Hibernate3入门之第三章Hibernate关联关系的映射

一对多的配置

  • 创建实体(分别为客户和订单的实体)

    1. 客户实体(Customer)

      	private Integer cid;
      	private String cname;
      	// 一个客户有多个订单.
      	private Set<Order> orders = new HashSet<Order>();
      	//分别提供get/set方法
      
    2. 订单实体(Order)

      	private Integer oid;
      	private String addr;
      	// 订单属于某一个客户.放置一个客户的对象.
      	private Customer customer;
      	//分别提供get/set方法	
      
  • 建立映射

    1. Customer.hbm.xml

      <hibernate-mapping>
          <class name="com.syj.vo.Customer"  table="customer" >
          <!-- 配置唯一标识	主键 -->
          	<id name="cid"  column="cid"  >
          		<!--  主键生成策略-->
          		<generator class="native"/>
          	</id>
          	
          <!-- 配置普通属性 -->
          	<property name="cname"  column="cname" length="20" />
          	
          <!-- 配置映射 -->
          <!-- 配置一个set集合的name是本类对象中关联对象属性的名称 || 那个集合-->	
           <set name="orders"  >
           		<!--<key>标签中column属性:用来描述一对多多的一方的外键的名称  ||  那个外键 -->
           		<key column="cno" />
           		<!-- 配置一个<one-to-many>标签中class:为多的一方的全路径  ||  对应那个类 -->
           		<one-to-many class="com.syj.vo.Order" />
           </set>
          </class>
      </hibernate-mapping>
      
    2. Order.hbm.xml

      <hibernate-mapping>
         <class name="com.syj.vo.Order"  table="orders" >
          <!-- 配置唯一标识(主键)-->
          	<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="cno" class="com.syj.vo.Customer"/>
          </class>
      </hibernate-mapping>
      
  • 测试一对多的配置

    1. 原始的保存效果(分别添加客户和订单)

      	// 向客户表中插入一个客户,在订单表中插入两个订单
      	public void demo1() {
      		Session session = Hibernate3Utils.openSession();
      		Transaction ts = 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);
      
      		ts.commit();
      	}
      
    2. 级联保存的效果(只添加一方就能将另外一方一起级联保存)

      • 保存客户级联订单.

        <set>集合是客户的关联订单对象的集合.所以在<set>上配置一个属性:cascade=“save-update”

      • 保存订单级联客户.

        在Order.hbm.xml中<many-to-one>配置cascade="save-update"属性:级联保存

    3. 级联删除的效果

      • 当我们不添加任何配置的时候

        默认的情况下,将外键置为null,删除数据记录.

      • 添加配置

        在Customer.hbm.xml的<set>标签上配置cascade=“delete”,将级联删除所关联表中的数据。

      在删除中有一种特殊的情况(孤儿删除

      当订单所属的客户不存在时,此时存在的订单是没有意义的(孤儿),属于删除效果的一种

    4. 双向维护产生多余的SQL:

      ​ 关于重复发送更新sql语句
      ​ 双向维护:自动更新数据库,产生多余的SQL.
      ​ 双方都有外键的维护能力.必须让其中一方放弃外键的维护权.(一般情况下都是一的放弃.)
      Hibernate3入门之第三章Hibernate关联关系的映射
      举例说明:

      ​ 一号客户,一号订单; 二号客户,二号订单 ;修改二号订单属于一号客户

      		Session session = Hibernate3Utils.openSession();
      		Transaction ts = session.beginTransaction();
      		// 持久化对象有自动更新数据库的能力
      		// 得到一号客户
      		Customer customer = (Customer) session.get(Customer.class, 1);
      		// 得到二号订单
      		Order order = (Order) session.get(Order.class, 2);
      		// 修改一号客户和二号订单之间的关系(产生多余的更新sql)
      		customer.getOrders().add(order);//对外键进行维护
      		order.setCustomer(customer);//对外键进行维护
      
      		ts.commit();
      

      配置inverse=”true”:在那一端配置.那么哪一端放弃了外键的维护权.

      (国家主席和我们每一个人)由我们自己去进行维护最好

      ​ 一般情况下,一的一方去放弃.

      ​ inverse:控制外键的维护.

    5. 一对多中的配置属性cascade和inverse

      • Cascade属性的取值
      取值 描述
      none 忽略其他关联的对象,默认值。
      save-update 当session通过save(),update(),saveOrUpdate()方法来保存或更新对象时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离对象。
      persist 当session通过persist()方法来保存当前对象时,会级联保存所有关联的新建的临时对象。
      merge 通过Session的merge()方法来保存当前对象时,会级联融合所有关联的游离对象。
      delete 通过delete()删除当前对象时,会级联删除所有关联的对象。
      lock 通过lock()把当前游离对象加入session缓存时,会把所有的游离对象也加入Session缓存中。
      replicate 通过replicate()复制当前对象时,会级联复制所有关联的对象。
      evict 通过evict()清除session缓存中对象时,会级联清除所有关联的对象。
      refresh 通过refresh()刷新当前对象时,会级联刷新所有关联的对象。(刷新是指同步更新session缓存中数据)
      delete-orphan 删除所有和当前对象时,解除关联行为的对象。
      all-delete-orphan 通过delete()删除当前对象时,会级联删除所有关联的对象。
      all save-update(),persist(),merge(),delete(),lock(),replicate(),evict()及refresh()的行为。

      比较常见的:

      none			:不使用级联
      dave-update		:保存或更新的时候级联
      delete			:删除的时候级联
      all				:除了孤儿删除以外的所有级联.
      delete-orphan	:孤儿删除(孤子删除).
        仅限于一对多.只有一对多时候,才有父子存在.认为一的一方是父亲,多的一方是子方.
        当一个客户与某个订单解除了关系.将外键置为null.订单没有了所属客户,相当于一个孩子没有了父亲.将这种记录就删除了.
      all-delete-orphan	:包含了孤儿删除的所有的级联.
      
      • inverse属性的取值

        inverse=“false”和inverse=“true”

      • Cascade属性和inverse属性的区别

      ​ 举例说明:

      	// 区分cascade和inverse
      	// 在Customer.hbm.xml中的<set>上配置 cascade="save-update" inverse="true"
      	public void demo11(){
      		Session session = Hibernate3Utils.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.
      		session.save(customer);
      		
      		tx.commit();
      	}
      

      结论: 客户是否存到数据库:存
      订单是否存到数据库:存 cascade=“save-update”.外键是null.

多对多的配置

  • 创建实体类(分别为学生和课程的实体)

    学生实体(Student)

    	private Integer sid;
    	private String sname;
    	// 一个学生选择多门课程:
    	private Set<Course> courses = new HashSet<Course>();
    	//提供get/set方法
    

    课程实体(Course)

    	private Integer cid;
    	private String cname;
    	// 一个课程被多个学生选择:
    	private Set<Student> students = new HashSet<Student>();
    	//提供get/set方法
    
  • 建立映射

    Student.hbm.xml

    <hibernate-mapping>
    	<class name="cn.itcast.hibernate3.demo3.Student" table="student">
    		<!-- 配置唯一标识 -->
    		<id name="sid" column="sid">
    			<generator class="native"/>
    		</id>
    		<!-- 配置普通属性 -->
    		<property name="sname" column="sname" length="20"/>
    		
    		<!-- 配置关联映射 -->
    		<!-- <set>标签 name:对应学生中的课程集合的名称   table:中间表名称. -->
    		<set name="courses" table="stu_cour">
    			<!-- <key>中column写 当前类在中间表的外键.-->
    			<key column="sno"></key>
    			<!-- <many-to-many>中class:另一方类的全路径. column:另一方在中间表中外键名称-->
    			<many-to-many class="cn.itcast.hibernate3.demo3.Course" column="cno"/>
    		</set>
    	</class>
    </hibernate-mapping>
    

    Course.hbm.xml

    <hibernate-mapping>
    	<class name="cn.itcast.hibernate3.demo3.Course" table="course">
    		<!-- 配置唯一标识 -->
    		<id name="cid" column="cid">
    			<generator class="native"/>
    		</id>
    		<!-- 配置普通属性 -->
    		<property name="cname" column="cname" length="20"/>
    		<!-- 配置与学生关联映射 -->
    		<!-- <set>中name:对应当前类中的学生的集合的名称  table:中间表的名称-->
    		<set name="students" table="stu_cour">
    			<!-- <key>中column:当前类在中间表中外键 -->
    			<key column="cno"></key>
    			<!-- <many-to-many>中class:另一方的类全路径. column:另一方在中间表中外键名称 -->
    			<many-to-many class="cn.itcast.hibernate3.demo3.Student" column="sno"/>
    		</set>
    	</class>
    </hibernate-mapping>
    
  • 测试多对多的配置

    1. 原始的保存效果(分别添加保存学生和课程)

      // 张三选择java和python的课程 李四只选择java的课程
      	public void demo1() {
      		Session session = Hibernate3Utils.openSession();
      		Transaction ts = session.beginTransaction();
      
      		// 创建学生张三和李四
      		Student student1 = new Student();
      		student1.setSname("张三");
      		Student student2 = new Student();
      		student2.setSname("李四");
      
      		// 创建java和Python课程
      		Course course1 = new Course();
      		course1.setCname("Java");
      		Course course2 = new Course();
      		course2.setCname("Python");
      
      		// 张三选择java和python的课程
      		student1.getCourses().add(course1);
      		student1.getCourses().add(course2);
      		// 李四选择python的课程
      		student2.getCourses().add(course1);
      
      		// 添加
      		// 在双方都进行添加的时候会对中间表进行重复更新从而产生报错(一方放弃主键)
      		// 一般会选择被动方放弃主键的维护
      		session.save(student1);
      		session.save(student2);
      		session.save(course1);
      		session.save(course2);
      
      		ts.commit();
      	}
      
    2. 级联保存

      配置Cascade属性Cascade=“save-uodate”

    3. 级联删除

      配置Cascade属性Cascade=“save-uodate,delete”

      注意事项:

      ​ 删除学生的同时级联课程一起删除(不合理),只能是删除学生方和中间表关于该课程的数据,而保留课程表本身的数据

      	// 级联删除
      	// 删除学生的同时只删除学生不删除课程
      	public void demo4() {
      		Session session = Hibernate3Utils.openSession();
      		Transaction ts = session.beginTransaction();
      		// 查询二号学生
      		Student student = (Student) session.get(Student.class, 2);
      		Course course = (Course) session.get(Course.class, 1);
      		// 移除学生所选的课程
      		student.getCourses().remove(course);
      
      		ts.commit();
      	}