Hibernate 一对多关系映射


一、问题:我们为什么要学习多表映射?  

答案:在实际开发中,我们数据库的表难免会有相互的关联关系,在操作表的时候就有可能会涉及到多张表的操作。例如:一个商城项目中商品和分类,用户和订单,订单和商品的关系等等。


通过之前文章对Hibernate框架学习,我们知道hibernate实现了ORM的思想,可以让我们通过操作实体类就实现对数据库表的操作。所以接下来学习重点是:掌握配置实体之间的关联关系

要想实现多表映射,我们可以使用以下步骤来实现:

  • 第一步:首先确定两张表之间的关系。如果关系确定错了,后面做的所有操作就都不可能正确。
  • 第二步:在数据库中实现两张表的关系
  • 第三步:在实体类中描述出两个实体的关系
  • 第四步:配置出实体类和数据库表的关系映射 配置的方式支持注解和XML,我们以注解为重点。

那么请思考:表之间的关系到底有几种呢?

数据库中多表之间存在着三种关系,如图所示。

从上图可以看出,系统设计的三种实体关系分别为:多对多、一对多和一对一关系

注意:
    一对多关系可以看为两种:  即一对多,多对一。实际开发中常用的关联关系,一对多和多对多。而一对一的情况,在实际开发中比较少见

二、Hibernate一对多关系映射

2.1、需求分析

我们采用的示例为客户和订单。

  •  客户:指的是一个网站的会员,我们记为A。
  •  订单:指的是客户会员在网站下的订单。
  •  客户和订单的关系即为一对多。

2.2、一对多(多对一)表关系建立

在一对多关系中,我们习惯把一的一方称之为主表,把多的一方称之为从表。在数据库中建立一对多的关系,需要使用数据库的外键约束

什么是外键?

指的是从表中有一列,取值参照主表的主键,这一列就是外键。
一对多数据库关系的建立,如下图所示:

2.3、建立实体类关系

Customer实体类:

/**
 * 客户(一方)
 * @author http://www.yiidian.com
 *
 */
public class Customer {
	private Integer id;
	private String name;
	private String gender;
	
	//关联订单
	private Set<Order> orders = new HashSet<Order>();
	
	
	public Set<Order> getOrders() {
		return orders;
	}
	public void setOrders(Set<Order> orders) {
		this.orders = orders;
	}
	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 String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	
	
}

由于订单是多的一方,在实体类中要体现出,每个订单只能对应一个客户,代码如下:

/**
 * 订单(多方)
 * @author http://www.yiidian.com
 *
 */
public class Order {

	private Integer id;
	private String orderno;
	private String productName;
	
	//关联客户
	private Customer customer;
	
	
	public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getOrderno() {
		return orderno;
	}
	public void setOrderno(String orderno) {
		this.orderno = orderno;
	}
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}

}

接下来的问题是如何何通过配置的方式把客户实体的订单Set集合和订单实体的中Customer对象与数据库建立起来关系。

2.4、映射配置文件

客户配置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 package="com.yiidian.domain">
 
 	<class name="Customer" table="t_customer">
 		<id name="id" column="id">
 			<generator class="native"></generator>
 		</id>
 		<property name="name" column="name"></property>
 		<property name="gender" column="gender"></property>
 		
 		<!-- 一对多配置 -->
 		<set name="orders">
 			<!-- 外键字段名称 -->
 			<key column="cust_id"></key>
 			<one-to-many class="Order"/>
 		</set>
 	</class>
 
</hibernate-mapping>   

订单配置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 package="com.yiidian.domain">
 
 	<class name="Order" table="t_order">
 		<id name="id" column="id">
 			<generator class="native"></generator>
 		</id>
 		<property name="orderno" column="orderno"></property>
 		<property name="productName" column="product_name"></property>
 		
 		<!-- 多对一配置 -->
 		<many-to-one name="customer" class="Customer" column="cust_id"/>
 	</class>
 
</hibernate-mapping>   

2.5、hibernte.cfg.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
	
<hibernate-configuration>	
	<!-- 连接数据库的参数 -->
<session-factory>
	<!-- 1.连接数据库参数 -->
	<property name="hibernate.connection.driver_class">
		com.mysql.jdbc.Driver
	</property>
	<property name="hibernate.connection.url">
		jdbc:mysql://localhost:3306/hibernate
	</property>
	<property name="hibernate.connection.username">root</property>
	<property name="hibernate.connection.password">root</property>

	<!-- hibernate方言 -->
	<property name="hibernate.dialect">
		org.hibernate.dialect.MySQLDialect
	</property>

	<!-- 2.hibernate扩展参数 -->
	<property name="hibernate.show_sql">true</property>
	<property name="hibernate.format_sql">true</property>
	<property name="hibernate.hbm2ddl.auto">update</property>

	<!-- *.hbm.xml文件 -->
	<mapping resource="com/yiidian/domain/Customer.hbm.xml" />
	<mapping resource="com/yiidian/domain/Order.hbm.xml" />
</session-factory>
</hibernate-configuration>	

2.6、执行一对多保存保存

/**
 * 演示Hibernate的一对多关系映射操作
 * 
 * @author http://www.yiidian.com
 * 
 */
public class Demo {

	/**
	 * 保存操作
	 */
	@Test
	public void test1() {
		// 准备数据
		// 需求:1个客户 2张订单
		Customer cust = new Customer();
		cust.setName("小苍");
		cust.setGender("女");

		Order o1 = new Order();
		o1.setOrderno("201709070001");
		o1.setProductName("JavaWeb开发详解");

		Order o2 = new Order();
		o2.setOrderno("201709070002");
		o2.setProductName("Spring开发详解");

		Session session = HibernateUtil.getSession();
		Transaction tx = session.beginTransaction();

		// 建立一对多双向关系
		cust.getOrders().add(o1);
		cust.getOrders().add(o2);
		o1.setCustomer(cust);
		o2.setCustomer(cust);

		session.save(cust);
		session.save(o1);
		session.save(o2);

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

}

2.7、运行结果

控制台显示:

Hibernate: 
    insert 
    into
        t_customer
        (name, gender) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_order
        (orderno, product_name, cust_id) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        t_order
        (orderno, product_name, cust_id) 
    values
        (?, ?, ?)
Hibernate: 
    update
        t_order 
    set
        cust_id=? 
    where
        id=?
Hibernate: 
    update
        t_order 
    set
        cust_id=? 
    where
        id=?

t_customer表数据:

t_order表的数据:

 

源码下载:https://pan.baidu.com/s/1dFplm9N