`

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
 
 

 
  • 大小: 65.6 KB
  • 大小: 319.3 KB
分享到:
评论

相关推荐

    Hiberante part 9:一对一关系映射

    在本篇博文中,我们将深入探讨Hibernate框架中的一个重要特性——一对一(One-to-One)关系映射。Hibernate作为Java领域中最流行的ORM(对象关系映射)工具,它允许开发者以面向对象的方式处理数据库操作,简化了...

    孙卫琴精通hibernate part2

    - Hibernate支持多种关系映射,本章将深入讲解一对一、一对多和多对一的关系配置,包括集合类型的选择(List, Set, Map等)和级联操作的设置。 4. **Chapter 9:多对多关系映射** - 多对多关系在数据库设计中常见...

    孙卫琴精通hibernate part3

    - Chapter16可能深入讲解了一对一和一对多关联的映射与操作。 - Chapter17至19可能涉及了多对多关系的处理,以及在实际应用中的注意事项。 - Chapter21和22可能探讨了更复杂的查询技术,如子查询、连接查询和分页...

    孙卫琴精通hibernate part1

    在后续的章节中,`appendixA`、`appendixB`和`appendixC`可能是对特定主题的补充,如 Hibenate的缓存机制、多对一、一对多、多对多的关系映射,或者更高级的主题,如懒加载、级联操作、自定义类型等。这些内容对于...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

    第7章 映射一对多关联关系  7.1 建立多对一的单向关联关系  7.1.1 元素的not-null属性  7.1.2 级联保存和更新  7.2 映射一对多双向关联关系  7.2.1 元素的inverse属性  7.2.2 级联删除  7.2.3 父子关系  7.3...

    精通hibernate:对象持久化技术孙卫琴第二版part2

    本章介绍一对多关联关系的映射方法,重点介绍inverse属性和cascade属性的用法。本章还将介绍通过Hibernate API来保存、修改和删除具有关联关系的对象的方法。 7.1 建立多对一的单向关联关系 148 7.1.1 [many-to-...

    Hibernate程序高手秘笈.part10-11.rar

    5. **多对多关系映射**:详述如何配置和处理多对多关联,包括中间表的管理,以及集合的懒加载和级联操作。 6. **一对一关系映射**:深入讨论一对一关系的配置,包括外键约束、主键共享以及一对一关系的级联操作。 ...

    精通Hibernate:对象持久化技术第二版part3

    本章介绍一对多关联关系的映射方法,重点介绍inverse属性和cascade属性的用法。本章还将介绍通过Hibernate API来保存、修改和删除具有关联关系的对象的方法。 7.1 建立多对一的单向关联关系 148 7.1.1 [many-to-...

    精通hibernate(part 2)共分4个part (孙卫琴)

    - **关联映射**:深入探讨了一对一、一对多、多对多等各种关联关系的映射方法。 - **继承映射**:讨论了单表继承、类表继承和子类表继承等多种继承映射策略。 - **查询语言**:介绍了Hibernate Query Language (HQL)...

    精通hibernate(part 4)共分4个part (孙卫琴)

    4. **Associations**:关联映射处理一对多、多对多等关系类型,支持不同类型的关联映射策略。 5. **Composite Components**:复合组件允许将复杂的Java对象映射到数据库中,例如将地址对象映射为一个表中的多个字段...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part4

    第7章 映射一对多关联关系  7.1 建立多对一的单向关联关系  7.1.1 元素的not-null属性  7.1.2 级联保存和更新  7.2 映射一对多双向关联关系  7.2.1 元素的inverse属性  7.2.2 级联删除  7.2.3 父子关系  7.3...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part3

    第7章 映射一对多关联关系  7.1 建立多对一的单向关联关系  7.1.1 元素的not-null属性  7.1.2 级联保存和更新  7.2 映射一对多双向关联关系  7.2.1 元素的inverse属性  7.2.2 级联删除  7.2.3 父子关系  7.3...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part1.rar

    第7章 映射一对多关联关系  7.1 建立多对一的单向关联关系  7.1.1 元素的not-null属性  7.1.2 级联保存和更新  7.2 映射一对多双向关联关系  7.2.1 元素的inverse属性  7.2.2 级联删除  7.2.3 父子关系  7.3...

    Hibernate程序高手秘笈.part01-03.rar

    3. 实体类与对象关系映射:讲解如何定义Java实体类,并使用注解或XML文件进行对象关系映射,包括基本类型映射、关联映射(一对一、一对多、多对一、多对多)和继承映射。 4. Session与Transaction:介绍Hibernate的...

    hibernate-distribution-3.3.1.part4

    6. **关联映射**:Hibernate支持一对一、一对多、多对一、多对多等各种关联关系的映射,方便处理复杂的数据库关系。 然而,压缩包内的文件名称列表似乎包含了与Hibernate无关的文件,如“我和一个偷吃禁果的女孩的...

    hibernate3.3.2jar包part1

    - **关联映射**:支持多种关联类型(一对一、一对多、多对一、多对多),使得数据库表之间的复杂关系可以方便地在Java对象中表达。 总结来说,Hibernate 3.3.2作为一款成熟的ORM框架,极大地简化了Java应用中的...

    精通hibernate(part 1)共分4个part (孙卫琴)

    - **多对多关联**:介绍如何在Hibernate中实现多对多关联,并给出具体的配置示例。 - **继承映射**:探讨如何使用单表、多表和联合表继承模式来映射具有层次结构的实体。 6. **事务管理** - **本地事务**:讨论...

    Hibernate程序高手秘笈.part04-06.rar

    3. **映射文件详解**:详细讲解XML或注解方式的实体映射,包括属性映射、关系映射(一对一、一对多、多对多)、继承映射等。 4. **HQL(Hibernate Query Language)**: Hibernate自己的查询语言,类似于SQL,但更...

    Hibernate程序高手秘笈.part07-09.rar

    4. **实体关系映射**:详述了如何配置和管理实体类之间的多对一、一对一、多对多关系,以及集合类型的映射。 5. **缓存机制**:讨论了Hibernate的缓存层次结构,包括一级缓存(Session缓存)和二级缓存...

Global site tag (gtag.js) - Google Analytics