Hibernate基础知识(4)

一、hibernate 关联关系映射 (多表映射配置 和 数据 增加、删除 )

1、 系统模型中 实体设计三种关系 
      关系型数据库 设计 通常用E-R 绘制 
      概念E-R图也称实体-联系图(EntityRelationshipDiagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。

      不同实体关系 建表原则 
      一对多: 在多个一方添加 一的一方 主键作为外键 
      多对多: 产生中间关系表,引入两个实体主键,作为外键 ,两个主键成为联合主键
      一对一: 在任意一方 引入对方主键 作为外键  (开发中使用非常少 )


二、一对多关联关系映射

1、 一对多关联映射的配置和 Java对象编写

客户类:

package lsq.hibernate.onetomany;

import java.util.HashSet;
import java.util.Set;

/**
 * 客户类
 * 
 * @author Daniel Li
 * 
 */
public class Customer {
	private Integer id;
	private String name;
	// 一个客户对应多个订单
	private Set<Order> orders = new HashSet<Order>();

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Set<Order> getOrders() {
		return orders;
	}

	public void setOrders(Set<Order> orders) {
		this.orders = orders;
	}

}

订单类:

package lsq.hibernate.onetomany;

/**
 * 订单类
 * 
 * @author Daniel Li
 * 
 */
public class Order {
	private Integer id;
	private Double money;
	private String receiverinfo;
	// 一个订单关联一个客户
	private Customer customer;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public Double getMoney() {
		return money;
	}

	public void setMoney(Double money) {
		this.money = money;
	}

	public String getReceiverinfo() {
		return receiverinfo;
	}

	public void setReceiverinfo(String receiverinfo) {
		this.receiverinfo = receiverinfo;
	}

	public Customer getCustomer() {
		return customer;
	}

	public void setCustomer(Customer customer) {
		this.customer = customer;
	}

}

Order.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="lsq.hibernate.onetomany.Order" table="orders" catalog="hibernate3day2">
		 <id name="id">
		 	<generator class="native"></generator>
		 </id>
		 <property name="money"></property>
		 <property name="receiverinfo"></property>
		 
		 <!-- 一个订单关联一个客户 -->
		 <!-- 
		 	name:订单类中客户属性名
		 	class:关联属性的类型
		 	column:列名,关联属性添加外键约束
		  -->
		  <many-to-one name="customer" class="lsq.hibernate.onetomany.Customer" column="customer_id" cascade="save-update"  not-null="true"></many-to-one>
	</class>
</hibernate-mapping>

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="lsq.hibernate.onetomany.Customer" table="customers" catalog="hibernate3day2">
		<id name="id">
			<generator class="native"></generator>
		</id>
		<property name="name"></property>
		<!-- 一个客户关联多个订单 -->
		<!-- name:关联订单集合属性名 -->
		<set name="orders" cascade="save-update" inverse="true">
			<!-- 客户关联订单后,在对方表中添加外键列 -->
			<!-- column:对方表外键列列名 -->
			<key column="customer_id"></key>
			<!-- 一对多配置 -->
			<!-- class是orders集合元素类型 -->
			<one-to-many class="lsq.hibernate.onetomany.Order"/>
		</set>
	</class>
</hibernate-mapping>

案例一: 一对多保存操作

package lsq.hibernate.onetomany;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import lsq.hibernate.utils.HibernateUtils;

/**
 * 编写一对多测试用例
 * 
 * @author Daniel Li
 * 
 */
public class HibernateTest {

	// 插入数据
	@Test
	public void demo1() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();

		Customer customer = new Customer();
		customer.setName("Curry");

		Order order1 = new Order();
		order1.setMoney(100d);
		order1.setReceiverinfo("美国 奥克兰");

		Order order2 = new Order();
		order2.setMoney(1000d);
		order2.setReceiverinfo("中国 上海");

		session.save(customer);
		session.save(order1);
		session.save(order2);

		// 建立客户和订单的联系
		customer.getOrders().add(order1);
		customer.getOrders().add(order2);

		order1.setCustomer(customer);
		order2.setCustomer(customer);

		transaction.commit();
		session.close();
	}
}

案例二 : 保存操作,只保存客户或者只保存订单是否可以 
      默认情况下 异常
      org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: cn.itcast.onetomany.Order
      原因: 持久化对象关联瞬时态对象。

	// 保存订单同时保存客户
	// 订单级联客户,订单--->客户,在Order.hbm.xml中添加
	// <many-to-one name="customer" class="lsq.hibernate.onetomany.Customer"
	// column="customer_id" cascade="save-update" ></many-to-one>
	@Test
	public void demo3() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();

		Customer customer = new Customer();
		customer.setName("Durant");

		Order order1 = new Order();
		order1.setMoney(100d);
		order1.setReceiverinfo("美国 华盛顿");

		session.save(order1);

		// 建立关系
		order1.setCustomer(customer);

		transaction.commit();
		session.close();
	}

      在添加用户时,同时将用户关联订单也保存到数据表 (将订单自动保存 )

      级联保存 : 当一个对象是持久化对象,该对象关联瞬时/脱管 对象,持久态对象 自动对关联对象 进行保存或者更新操作 
      * 保存客户 同时 保存关联订单  
            在Customer.hbm.xml 配置 <set name="orders" cascade="save-update">
      * 保存订单 同时 保存关联客户 
            在Order.hbm.xml 配置 <many-to-one name="customer" cascade="save-update"></many-to-one>


练习: 对象导航练习

	//练习
	@Test
	public void demo4() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();

		Customer customer = new Customer();
		customer.setName("Green");

		Order order1 = new Order();
		order1.setMoney(100d);
		order1.setReceiverinfo("美国 奥克兰");

		Order order2 = new Order();
		order2.setMoney(1000d);
		order2.setReceiverinfo("中国 上海");

		Order order3 = new Order();
		order3.setMoney(10000d);
		order3.setReceiverinfo("中国 北京");

		// 建立客户和订单的联系
		order1.setCustomer(customer);
		customer.getOrders().add(order2);
		customer.getOrders().add(order3);

		// 两边都配置cascade="save-update"
		session.save(order1);// 插入几条数据?4
		// session.save(customer);//插入几条数据?3
		// session.save(order2);//插入几条数据?1

		transaction.commit();
		session.close();
	}

案例三 : 级联删除 
      删除订单时,关联客户是否 也被删除 ??? 删除客户时,订单会不会删除 ???
      * 默认情况下 ,删除客户时,将相关订单外键设置为null , 完成删除 

      * 如果设置 Order.hbm.xml  中 <manytoone name="customer" not-null="true" /> 必须 设置inverse=true 

      不允许存在 没有客户的订单 !!!!  在开发时,需要删除客户时, 级联删除该客户的订单 
      * 配置 Customer.hbm.xml  cascade="delete"
      * 删除脱管对象无法产生级联删除效果, 必须删除持久对象

	@Test
	// 删除客户, 记录删除订单
	// 客户 ---> 订单 , 配置Customer.hbm.xml
	// <set name="orders" cascade="delete">
	public void demo5() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();

		// 删除脱管 (无法产生级联效果)
		// Customer customer = new Customer();
		// customer.setId(1);

		// 删除持久对象
		Customer customer = (Customer) session.get(Customer.class, 1);

		session.delete(customer);

		transaction.commit();
		session.close();
	}

案例四、孤儿删除 (孤子删除 )

      在一对多模型中,存在父子表关系。例如:客户和订单,一方通常是父方,多方是子方,不存在没有客户的订单。

      问题:解除客户和订单关系时,订单是否有效?---无效

                  对于无效订单,应该删除,客户解除和订单关系时,自动删除订单。

      孤儿删除步骤:

            1) 在Customer.hbm.xml 配置 <set name="orders" cascade="delete-orphan">
            2) 在程序 customer.getOrders().remove(order);  删除解除关系的订单对象

	@Test
	// 孤儿删除
	// 客户 ---> 订单 , 配置Customer.hbm.xml
	// <set name="orders" cascade="delete-orphan">
	public void demo6() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();

		//解除1号订单和1号客户的关系
		Customer customer = (Customer) session.get(Customer.class, 1);
		Order order = (Order) session.get(Order.class,1);
		
		customer.getOrders().remove(order);

		transaction.commit();
		session.close();
	}


☆:cascade属性取值, 有JPA规范定义的, Hibernate框架实现JPA规范,对其进行扩展 
      * save-Update 级联保存更新,持久对象 关联瞬时对象 执行save, 关联脱管对象 执行update
      * delete 级联删除 
      * delete-orphan 主要用于一对多模型,进行孤儿删除 
      * all 除掉delete-orphan外 所有级联关系 
      * all-delete-orphan 包含 all和 delete-orphan


案例五、双向维护 --- 多余的SQL

      问题:将1号订单改为属于2号客户  ---------- 产生两条SQL语句,怎么解决?


      不要在一对多模型中,使双方都具有 外键维护能力 
      * 设置inverse属性 --- 单向维护 
      通过inverse属性来设置由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系
      * 哪一方设置 inverse=true,代表放弃外键维护能力 ,在实际开发中 通常由多方来维护外键 , 在一方添加 inverse=true

	//多余SQL语句
	//在Customer.hbm.xml中配置inverse=“true"
	@Test
	public void demo7() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();

		Order order = (Order) session.get(Order.class, 1);
		Customer customer = (Customer)session.get(Customer.class, 2);
		
		order.setCustomer(customer);
		
		customer.getOrders().add(order);

		transaction.commit();
		session.close();
	}


案例六、inverse 和 cascade的区别 ?

                cascade 进行级联操作,如果配置cascade 对数据进行级联操作, inverse 属性只是针对外键列维护能力 ,如果设置inverse=true,将外键维护交给另一方

	//inverse和cascade的区别
	//<set name="orders" cascade="save-update" inverse="true">
	@Test
	public void demo8(){
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();

		Customer customer = new Customer();
		customer.setName("Wade");
		
		Order order = new Order();
		order.setMoney(1000d);
		order.setReceiverinfo("芝加哥");
		
		//建立客户和订单关系
		customer.getOrders().add(order);
		
		//保存客户
		session.save(customer);
		
		//问题:订单数据会不会被保存?

		transaction.commit();
		session.close();
	}
执行结果:(会级联保存订单,但是不会维护订单中客户ID外键列)



      小结: 
        1) 一对多存在 父子关系,一方是父方,多方是子方, 子方数据 依赖 父方数据
        2) 通常将cascade 配置在 父方 (一的一方 客户表)
            例如: 添加客户时  添加订单, 删除客户时 删除订单 , 孤儿删除 
        3) 外键方维护权,通常交给多方负责,需要在一方配置 inverse="true"
            <set name="orders" cascade="all-delete-orphan" inverse="true" >
      


      

                  







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值