Hibernate part 8:一对多关联关系映射

数据库建表原则,在多的一方添加一个外键列,引用一的一方的主键,例如客户和订单,在订单表中增加客户编号作为外键

一对多,类对象之间的关系,在多的一方添加一个集合

class A {
	B b; // 一个A对应一个B
}
class B {
	A[] 、List<A>、Set<A> // 一个B 对应很多A 
}

以客户与订单关系为例建立映射关系

建立客户类

public class Customer implements Serializable{
	private Integer id;
	private String name;
	private String city;
	pprivate Set<Order> orders = new HashSet<Order>();//一个客户对应多个订单
}
 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="rock.lee.bean.Customer" table="customer" catalog="test">
		<id name="id" column="id" type="int">
			<generator class="native"></generator>
		</id>
		<set name="orders">
			<!-- customer表在order表中所生成的外键列 -->
			<key column="customer_id"></key>
			<one-to-many class="rock.lee.bean.Order" />
		</set>
		<property name="name" column="name" type="java.lang.String"></property>
		<property name="city" column="city" type="java.lang.String"></property>
	</class>
</hibernate-mapping>

建立订单类

public class Order implements Serializable{
	private Integer id;
	private String address;
	private Double money;
	private 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="rock.lee.bean.Order" table="orders" catalog="test">
		<id name="id" column="id" type="int">
			<generator class="native"></generator>
		</id>
		<property name="address" column="address" type="java.lang.String"></property>
		<property name="money" column="money" type="double"></property>
		<!-- 在orders表中添加customer外键列,column为外键列名 -->
		<many-to-one name="customer" class="rock.lee.bean.Customer" column="customer_id"></many-to-one>
	</class>
</hibernate-mapping>
 在hibernate.cfg.xml中配置加载Custoemr和Order映射文件
	<mapping resource="rock/lee/bean/Customer.hbm.xml" />
	<mapping resource="rock/lee/bean/Order.hbm.xml" />
 案例一:保存Customer、Order
	@Test
	public void testOneToMany01() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer c= new Customer("小明", "BJ");
		Order order1 = new Order("二环", 11D);
		Order order2 = new Order("三环", 22D);
		
		order1.setCustomer(c);//订单关联客户
		order2.setCustomer(c);
		c.getOrders().add(order1);//客户关联订单
		c.getOrders().add(order2);
		
		session.save(c);
		session.save(order1);
		session.save(order2);
		
		transaction.commit();
		session.close();
	}
  根据控制台打印的SQL,在insert Customer和Order后会update orders表中的外键列
Hibernate: 
    update
        test.orders 
    set
        customer_id=? 
    where
        id=?
 程序分别保存了customer和order,如果只保存customer不保存order会抛出异常
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: rock.lee.bean.Order
 在Session关闭后,Hibernate不允许一个peristient状态对象关联一个tranient状态对象   案例二:级联保存 保存customer的同时自动保存order,修改customer类的映射文件增加casecade属性
<?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="rock.lee.bean.Customer" table="customer" catalog="test">
		<id name="id" column="id" type="int">
			<generator class="native"></generator>
		</id>
		<set name="orders" cascade="save-update">
			<!-- customer表order是中所生成的外键列 -->
			<key column="customer_id"></key>
			<one-to-many class="rock.lee.bean.Order" />
		</set>
		<property name="name" column="name" type="java.lang.String"></property>
		<property name="city" column="city" type="java.lang.String"></property>
	</class>
</hibernate-mapping>
 只保存customer,不保存order
	@Test
	public void testOneToMany02() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer c= new Customer("小明", "BJ");
		Order order1 = new Order("二环", 11D);
		Order order2 = new Order("三环", 22D);
		
		order1.setCustomer(c);//订单关联客户
		order2.setCustomer(c);
		c.getOrders().add(order1);//客户关联订单
		c.getOrders().add(order2);
		
		session.save(c);
		
		transaction.commit();
		session.close();
	}
配置了casecade后当一个persistent对象关联了一个traniesent对象是会自动保存关联的那个transient对象 案例三:对象导航

  案例四:级联删除
	@Test
	public void testOneToMany03() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer c = (Customer) session.get(Customer.class, 1);
		session.delete(c);
		
		transaction.commit();
		session.close();
	}
 删除customer时会先把order表中的外键列customer_id设置为null,在删除customer
Hibernate: 
    update
        test.orders 
    set
        customer_id=null 
    where
        customer_id=1
Hibernate: 
    delete 
    from
        test.customer 
    where
        id=1
 删除orders表中的数据时直接删除,因为没有外键依赖关系 修改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="rock.lee.bean.Customer" table="customer" catalog="test">
		<id name="id" column="id" type="int">
			<generator class="native"></generator>
		</id>
		<set name="orders" cascade="save-update,delete">
			<!-- customer表order是中所生成的外键列 -->
			<key column="customer_id"></key>
			<one-to-many class="rock.lee.bean.Order" />
		</set>
		<property name="name" column="name" type="java.lang.String"></property>
		<property name="city" column="city" type="java.lang.String"></property>
	</class>
</hibernate-mapping>
 此时删除customer对象时,也将删除order对象,级联删除,仍然是先接触外键关系,在删除数据,如果删除一个detached状态的customer对象无法级联删除的 修改Order.hbm.xml文件,将外键设置为非null
<?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="rock.lee.bean.Order" table="orders" catalog="test">
		<id name="id" column="id" type="int">
			<generator class="native"></generator>
		</id>
		<property name="address" column="address" type="java.lang.String"></property>
		<property name="money" column="money" type="double"></property>
		<!-- 在orders表中添加customer外键列,column为外键列名 -->
		<many-to-one name="customer" class="rock.lee.bean.Customer" column="customer_id" not-null="true"></many-to-one>
	</class>
</hibernate-mapping>
 此时,当删除Customer对象是无法级联删除Order,因为删除前需要先接触外键,设置orders表中customer_id为null,但由于配置了not-null="true"属性会抛出异常
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'customer_id' cannot be null
 这种情况只能先删除orders表中的订单数据,在删除customer表中的数据,业务上不会有此种情况,删个订单还把客户干掉了 案例五:孤儿删除 在一对多关系中,表的建立存在父子表,customer为父表,orders为子表,当一个customer和一个order解除关系后,作为order信息实际上就不完整了,不会有一个订单不关联任何用户的情况 修改Customer.hbm.xml,在casecade属性中增加delete-orphan
<?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="rock.lee.bean.Customer" table="customer" catalog="test">
		<id name="id" column="id" type="int">
			<generator class="native"></generator>
		</id>
		<set name="orders" cascade="save-update,delete,delete-orphan" >
			<!-- customer表order是中所生成的外键列 -->
			<key column="customer_id"></key>
			<one-to-many class="rock.lee.bean.Order" />
		</set>
		<property name="name" column="name" type="java.lang.String"></property>
		<property name="city" column="city" type="java.lang.String"></property>
	</class>
</hibernate-mapping>
 不调用deleet(),只接触cusomter与order之间的关系
	@Test
	public void testOneToMany04() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer c1 = (Customer) session.get(Customer.class, 1);
		Order o1  =  (Order) session.get(Order.class, 1);
		c1.getOrders().remove(o1);//解除客户与订单关系
		
		transaction.commit();
		session.close();
	}
 Hibernate会先接触外键关系,在删除
Hibernate: 
    update
        test.orders 
    set
        customer_id=null 
    where
        customer_id=? 
        and id=?
Hibernate: 
    delete 
    from
        test.orders 
    where
        id=?
  casecade取值: casecade取值来源JPA规范,但Hibernate对JPA进行了扩展

 有红点的是Hibernate扩展的,其它是JPA inverse单相关系维护: 数据库数据
mysql> select * from customer;
+----+--------+------+
| id | name   | city |
+----+--------+------+
|  1 | 孙艺珍       | BJ   |
|  2 | 林允儿      | SH   |
+----+--------+------+
2 rows in set (0.00 sec)

mysql> select * from orders;
+----+---------+-------+-------------+
| id | address | money | customer_id |
+----+---------+-------+-------------+
|  1 | 二环        |    11 |           1 |
+----+---------+-------+-------------+
1 row in set (0.00 sec)
   将order表中1号订单与林允儿关联
	@Test
	public void testOneToMany06() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Order o = (Order) session.get(Order.class, 1);
		Customer lyr = (Customer) session.get(Customer.class, 2);
		o.setCustomer(lyr);
		lyr.getOrders().add(o);
		
		transaction.commit();
		session.close();
	}
 控制台会有两条update语句,更新orders表中的外键列
Hibernate: 
    update
        test.orders 
    set
        address=?,
        money=?,
        customer_id=? 
    where
        id=?
Hibernate: 
    update
        test.orders 
    set
        customer_id=? 
    where
        id=?
 原因是Session中的缓存与快照比对后发现不一样所有cusotmer和order都去更新orders表中的外键列 修改Customer.hbm.xml增加inverse="true"
<?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="rock.lee.bean.Customer" table="customer" catalog="test">
		<id name="id" column="id" type="int">
			<generator class="native"></generator>
		</id>
		<set name="orders" cascade="save-update,delete,delete-orphan" inverse="true" >
			<!-- customer表order是中所生成的外键列 -->
			<key column="customer_id"></key>
			<one-to-many class="rock.lee.bean.Order" />
		</set>
		<property name="name" column="name" type="java.lang.String"></property>
		<property name="city" column="city" type="java.lang.String"></property>
	</class>
</hibernate-mapping>
同样的操作,只有一条update,有order发出,在Cusomter.hbm.xml配置invers="true" 表示关联关系的控制权被反转了,交由order控制
Customer customer = new Customer();// 定义一个客户
customer.setName("张三");
Order order = new Order();// 定义一个订单
order.setMoney(2000d);
order.setAddr("二环");
// 建立对象之间关系
customer.getOrders().add(order); // 客户对象关联订单
session.save(customer);
 custoemr和order都会被保存,但orders表中cusotmer_id列为null,因为维护外键的权利由order控制,但保存了cusomter时级联保存order,却没有维护外键的权利,所以orders表中的外键列为null    
 

猜你喜欢

转载自mvplee.iteye.com/blog/2186414