2017-02-22Hibernate(4)

一、映射一对多关联关系

1.单向 n-1

•单向 n-1 关联只需从 n 的一端可以访问 1 的一端

•域模型: 从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在  Customer 类中无需定义存放 Order 对象的集合属性

•无法直接用 property 映射 customer 属性

•Hibernate 使用 <many-to-one> 元素来映射多对一关联关系

     many-to-one
        <many-to-one> 元素来映射组成关系

–name: 设定待映射的持久化类的属性的名字

–column: 设定和持久化类的属性对应的表的外键

–class:设定待映射的持久化类的属性的类型

2.双向 1-n

•双向 1-n 与 双向 n-1 是完全相同的两种情形

•双向 1-n 需要在 1 的一端可以访问 n 的一端, 反之依然.

域模型:从 Order 到 Customer 的多对一双向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer  类中需定义存放 Order 对象的集合属性


        •关系数据模型:ORDERS 表中的 CUSTOMER_ID参照 CUSTOMER 表的主键


•当 Session 从数据库中加载 Java 集合时, 创建的是 Hibernate内置集合类的实例, 因此 在持久化类中定义集合属性时必须把属性声明为 Java 接口类型

–Hibernate 的内置集合类具有集合代理功能, 支持延迟检索策略

–事实上,Hibernate 的内置集合类封装了 JDK 中的集合类, 这使得 Hibernate 能够对缓存中的集合对象进行脏检查, 按照集合对象的状态来同步更新数据库。

•在定义集合属性时, 通常把它初始化为集合实现类的一个实例. 这样可以提高程序的健壮性, 避免应用程序访问取值为 null 的集合的方法抛出 NullPointerException

3.元素

< set > 元素来映射 持久化类的 set 类型的属性

name: 设定待映射的持久化类的属性的

< key > 元素 设定与所关联的持久化类对应的表的外键

column: 指定关联表的外键名

< one-to-many > 元素 设定集合属性中所关联的持久化类

class: 指定关联的持久化类的类名


二、映射一对一关联关系


1.基于外键映射的 1-1

•对于基于外键的1-1关联,其外键可以存放在任意一边, 在需要存放外键一端,增加 many-to-one 元素。为many-to-one元素增加unique=“true” 属性来表示为1-1关联

•另一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段

–不使用 property-ref 属性的 sql

       

–使用 property-ref 属性的 sql

      

2.映射多对多关联关系


1).单向 n-n


n-n 的关联必须使用连接表

•与 1-n 映射类似, 必须为 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中参照 CATEGORIES 表的外键为 CATEGORIY_ID. 与 1-n 关联映射不同的是,建立n-n 关联时, 集合中的元素使用 many-to-many.many-to-many 子元素的 class 属性指定 items 集合中存放的是 Item 对象, column 属性指定  CATEGORIES_ITEMS 表中参照 ITEMS 表的外键为 ITEM_ID

2).双向 n-n


•双向 n-n 关联需要 两端都使用集合属性

•双向n-n关联 必须使用连接表

•集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类

在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名 . 两个集合元素 set table 元素的值必须指定,而且必须相同 set 元素的两个子元素: key many-to-many 都必须指定 column 属性 ,其中, key many-to-many 分别指定本持久化类和关联类在连接表中的外键列名,因此两边的 key many-to-many column 属性交叉相同 也就是说,一边的set元素的key的 cloumn值为a,many-to-many 的 column 为b;则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a. 

对于双向 n-n 关联 , 必须把其中一端的 inverse 设置为 true , 否则两端都维护关联关系可能会造成主键冲突.


实例n-1:

HibernateTest.java

@Test
	public void testDelete(){
		//在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
		Customer customer = (Customer) session.get(Customer.class, 1);
		session.delete(customer); 
	}
	
	@Test
	public void testUpdate(){
		Order order = (Order) session.get(Order.class, 1);
		order.getCustomer().setCustomerName("AAA");
	}
	
	@Test
	public void testMany2OneGet(){
		//1. 若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的
		//1 的那一端的对象!
		Order order = (Order) session.get(Order.class, 1);
		System.out.println(order.getOrderName()); 
		
		System.out.println(order.getCustomer().getClass().getName());
		
		session.close();
		
		//2. 在需要使用到关联的对象时, 才发送对应的 SQL 语句. 
		Customer customer = order.getCustomer();
		System.out.println(customer.getCustomerName()); 
		
		//3. 在查询 Customer 对象时, 由多的一端导航到 1 的一端时, 
		//若此时 session 已被关闭, 则默认情况下
		//会发生 LazyInitializationException 异常
		
		//4. 获取 Order 对象时, 默认情况下, 其关联的 Customer 对象是一个代理对象!
		
	}
	
	@Test
	public void testMany2OneSave(){
		Customer customer = new Customer();
		customer.setCustomerName("BB");
		
		Order order1 = new Order();
		order1.setOrderName("ORDER-3");
		
		Order order2 = new Order();
		order2.setOrderName("ORDER-4");
		
		//设定关联关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);
		
		//执行  save 操作: 先插入 Customer, 再插入 Order, 3 条 INSERT
		//先插入 1 的一端, 再插入 n 的一端, 只有 INSERT 语句.
//		session.save(customer);
//		
//		session.save(order1);
//		session.save(order2);
		
		//先插入 Order, 再插入 Customer. 3 条 INSERT, 2 条 UPDATE
		//先插入 n 的一端, 再插入 1 的一端, 会多出 UPDATE 语句!
		//因为在插入多的一端时, 无法确定 1 的一端的外键值. 所以只能等 1 的一端插入后, 再额外发送 UPDATE 语句.
		//推荐先插入 1 的一端, 后插入 n 的一端
		session.save(order1);
		session.save(order2);
		
		session.save(customer);
	}

}

Customer.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">


<hibernate-mapping>
    
    <class name="com.atguigu.hibernate.entities.n21.Customer" table="CUSTOMERS">
    
        <id name="customerId" type="java.lang.Integer">
            <column name="CUSTOMER_ID" />
            <generator class="native" />
        </id>
    
        <property name="customerName" type="java.lang.String">
            <column name="CUSTOMER_NAME" />
        </property>
        
    </class>
    
</hibernate-mapping>
Order.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.atguigu.hibernate.entities.n21">

    <class name="Order" table="ORDERS">

        <id name="orderId" type="java.lang.Integer">
            <column name="ORDER_ID" />
            <generator class="native" />
        </id>
        
        <property name="orderName" type="java.lang.String">
            <column name="ORDER_NAME" />
        </property>
        
		<!-- 
			映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 
			name: 多这一端关联的一那一端的属性的名字
			class: 一那一端的属性对应的类名
			column: 一那一端在多的一端对应的数据表中的外键的名字
		-->
		<many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>

    </class>
</hibernate-mapping>
实例 n - n:

Customer.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    
    <class name="com.atguigu.hibernate.entities.n21.Customer" table="CUSTOMERS">
    
        <id name="customerId" type="java.lang.Integer">
            <column name="CUSTOMER_ID" />
            <generator class="native" />
        </id>
    
        <property name="customerName" type="java.lang.String">
            <column name="CUSTOMER_NAME" />
        </property>
        
    </class>
    
</hibernate-mapping>
Order.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.atguigu.hibernate.entities.n21">

    <class name="Order" table="ORDERS">

        <id name="orderId" type="java.lang.Integer">
            <column name="ORDER_ID" />
            <generator class="native" />
        </id>
        
        <property name="orderName" type="java.lang.String">
            <column name="ORDER_NAME" />
        </property>
        
		<!-- 
			映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 
			name: 多这一端关联的一那一端的属性的名字
			class: 一那一端的属性对应的类名
			column: 一那一端在多的一端对应的数据表中的外键的名字
		-->
		<many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>

    </class>
</hibernate-mapping>

HibernateTest.java

@Test
	public void testDelete(){
		//在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
		Customer customer = (Customer) session.get(Customer.class, 1);
		session.delete(customer); 
	}
	
	@Test
	public void testUpdate(){
		Order order = (Order) session.get(Order.class, 1);
		order.getCustomer().setCustomerName("AAA");
	}
	
	@Test
	public void testMany2OneGet(){
		//1. 若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的
		//1 的那一端的对象!
		Order order = (Order) session.get(Order.class, 1);
		System.out.println(order.getOrderName()); 
		
		System.out.println(order.getCustomer().getClass().getName());
		
		session.close();
		
		//2. 在需要使用到关联的对象时, 才发送对应的 SQL 语句. 
		Customer customer = order.getCustomer();
		System.out.println(customer.getCustomerName()); 
		
		//3. 在查询 Customer 对象时, 由多的一端导航到 1 的一端时, 
		//若此时 session 已被关闭, 则默认情况下
		//会发生 LazyInitializationException 异常
		
		//4. 获取 Order 对象时, 默认情况下, 其关联的 Customer 对象是一个代理对象!
		
	}
	
	@Test
	public void testMany2OneSave(){
		Customer customer = new Customer();
		customer.setCustomerName("BB");
		
		Order order1 = new Order();
		order1.setOrderName("ORDER-3");
		
		Order order2 = new Order();
		order2.setOrderName("ORDER-4");
		
		//设定关联关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);
		
		//执行  save 操作: 先插入 Customer, 再插入 Order, 3 条 INSERT
		//先插入 1 的一端, 再插入 n 的一端, 只有 INSERT 语句.
//		session.save(customer);
//		
//		session.save(order1);
//		session.save(order2);
		
		//先插入 Order, 再插入 Customer. 3 条 INSERT, 2 条 UPDATE
		//先插入 n 的一端, 再插入 1 的一端, 会多出 UPDATE 语句!
		//因为在插入多的一端时, 无法确定 1 的一端的外键值. 所以只能等 1 的一端插入后, 再额外发送 UPDATE 语句.
		//推荐先插入 1 的一端, 后插入 n 的一端
		session.save(order1);
		session.save(order2);
		
		session.save(customer);
	}

hibernate.cfg.xml:和之前一样,只是这些不太一样。

<!-- 需要关联的 hibernate 映射文件 .hbm.xml -->
		<!--  
    	<mapping resource="com/atguigu/hibernate/entities/News.hbm.xml"/>
    	<mapping resource="com/atguigu/hibernate/entities/Worker.hbm.xml"/>
    	
    	<mapping resource="com/atguigu/hibernate/entities/n21/Customer.hbm.xml"/>
    	<mapping resource="com/atguigu/hibernate/entities/n21/Order.hbm.xml"/>
		-->
		
    	<mapping resource="com/atguigu/hibernate/entities/n21/both/Customer.hbm.xml"/>
    	<mapping resource="com/atguigu/hibernate/entities/n21/both/Order.hbm.xml"/>



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值