上一篇博文总结了 Hibernate 的一对一的关联关系, 包括基于主键的单向一对一, 基于外键的单向一对一, 基于外键的双向一对一.
下面咱们说一下 Hibernate 的一对多关联关系.
其实一对多和多对一是一样的, 一对多反过来想就是多对一了.
Hibernate的一对多可分为:
1. 单向一对多.
2. 双向一对多.
OneByOne
一: 单向一对多
准备工作:
咱们以 客户(Customer) 和 订单(Order) 的例子来说, 一个客户可以有多个订单, 但是一个订单只能属于一个客户, 这就构成了一对多的关系.
1. 建立持久化类:
Customer类:
package com.single.many2one; public class Customer { private Integer id; private String name; public Customer() { super(); } public Customer(String name) { super(); this.name = name; } 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; } }
Order类:
package com.single.many2one; public class Order { private Integer id; private String name; private Customer customer; public Order() { super(); } public Order(String name, Customer customer) { super(); this.name = name; this.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 getName() { return name; } public void setName(String name) { this.name = name; } }
2. 建立对应的映射文件
Customer类映射文件:
<?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.single.many2one.Customer" table="CUSTOMERS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> </class> </hibernate-mapping>
Order类映射文件:
<?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.single.many2one"> <class name="Order" table="ORDERS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <many-to-one name="customer" class="Customer"> <column name="CUSTOMER_ID" /> </many-to-one> </class> </hibernate-mapping>
3. 把映射文件加入到 Hibernate 的主配置文件( hibernate.cfg.xml )里
4. 建立单元测试类
package com.single.many2one; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestHibernate { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void init(){ sessionFactory = new Configuration().configure().buildSessionFactory(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); } @After public void distory(){ transaction.commit(); session.close(); sessionFactory.close(); } }
在上面的持久化类中, 订单(Order)中有对客户(Customer)的引用, 而客户(Customer)不知道订单(Order)的存在.
1. 测试保存数据:
@Test public void testInsert(){ Customer customer = new Customer("Mike"); Order order1 = new Order("O-Mike-01", customer); Order order2 = new Order("O-Mike-02", customer); Order order3 = new Order("O-Mike-03", customer); // 保存的时候要先保存 1 的一端, 然后保存 n 的一端 // 这样可以避免多出 n 条 update 语句 session.save(customer); session.save(order1); session.save(order2); session.save(order3); }
为什么反过来保存会多出 n 条update语句, 很简单, 维护关联关系. 如果先保存 order 这个时候, 外键还没有值, 最后插入Customer的时候, 外键有值了, 就会发送update语句来更新Order表的外键值.
2. 测试查询数据
@Test public void testQuery(){ // 默认使用懒加载模式, 注意懒加载异常 Order order = session.get(Order.class, 1); System.out.println(order.getName()); System.out.println(order.getCustomer().getClass().getName()); }
默认使用懒加载, 如果想不适用懒加载可以在 <many-to-one> 标签中使用 lazy="false", 来禁用懒加载.
3. 测试删除数据:
@Test public void testDelete(){ // 想想都知道, 有外键约束, 肯定不会删除成功. Customer customer = session.get(Customer.class, 1); session.delete(customer); }
4. 测试更新数据:
@Test public void testUpdte(){ // 一般不会出现什么问题 // 作为程序员这样说, 实在是太不负责任了 // 是我掌握的不够. 以后会继续深入研究的. Customer customer = session.get(Customer.class, 1); customer.setName("Jerry"); session.update(customer); }
二: 双向一对多
准备工作:
咱们还是以客户(Customer)和订单(Order)的例子来说, 与上面不同的是, 现在Customer类中有一个集合(Set)来存放Order对象.
1. 建立持久化类:
Customer类:
package com.doubles.one2many; import java.util.HashSet; import java.util.Set; public class Customer { private Integer id; private String name; private Set<Order> orders = new HashSet<>(); public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } public Customer() { super(); } public Customer(String name) { super(); this.name = name; } 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; } }
Order类:
package com.doubles.one2many; public class Order { private Integer id; private String name; private Customer customer; public Order() { super(); } public Order(String name, Customer customer) { super(); this.name = name; this.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 getName() { return name; } public void setName(String name) { this.name = name; } }
2. 建立映射文件
Customer的映射文件:
<?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.doubles.one2many"> <class name="Customer" table="CUSTOMERS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <!-- 在hibernate中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系. inverse = false 的为主动方 inverse = true 的为被动方 由主动方负责维护关联关系 在没有设置 inverse=true 的情况下, 父子两边都维护父子关系 在 1-n 关系中, 将 n 方设为主控方将有助于性能改善 (如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多) 在 1-N 关系中,若将 1 方设为主控方会额外多出 update 语句. 插入数据时无法同时插入外键列,因而无法为外键列添加非空约束. --> <set name="orders" table="ORDER" inverse="true"> <!-- 设定与所关联的持久化类对应的表的外键 column: 指定关联表的外键名, 就是 Order.hbm.xml 中的外键(CUSTOMER_ID) --> <key column="CUSTOMER_ID" /> <!-- 设定集合属性中所关联的持久化类 class: 指定关联的持久化类的类名 --> <one-to-many class="Order" /> </set> </class> </hibernate-mapping>
Order的映射文件:
<?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.doubles.one2many"> <class name="Order" table="ORDERS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <many-to-one name="customer" class="Customer"> <column name="CUSTOMER_ID" /> </many-to-one> </class> </hibernate-mapping>
3. 把映射文件加入到 Hibernate 的主配置文件( hibernate.cfg.xml )里
4. 建立单元测试类(略)
1. 测试保存数据:
@Test public void testInsert() { Customer customer = new Customer("Jerry"); Order order1 = new Order("O-Jerry-01", customer); Order order2 = new Order("O-Jerry-02", customer); Order order3 = new Order("O-Jerry-03", customer); customer.getOrders().add(order1); customer.getOrders().add(order2); customer.getOrders().add(order3); /* * 1. 在没有设置 inverse="true" 的情况下 * a). 如果先保存 n 的一端, 再保存 1 的一端, 会多出 6 条update语句. * b). 如果先保存 1 的一端, 在保存 n 的一端, 会多出 3 条update语句. * 2. 设置了 inverse="true" 的情况下 * a). 如果先保存 n 的一端, 再保存 1 的一端, 会多出 3 条update语句. * b). 如果先保存 1 的一端, 再保存 n 的一端, 没有多余的update语句. */ session.save(customer); session.save(order1); session.save(order2); session.save(order3); }
出现 update 语句是因为要维护两个数据表之间的关联关系.
2. 测试删除数据:
@Test public void testQuery(){ // 默认使用懒加载 Customer customer = session.get(Customer.class, 1); System.out.println(customer.getName()); System.out.println(customer.getOrders().getClass().getName()); //上一语句打印: org.hibernate.collection.internal.PersistentSet // 这是 Hibernate内置的类型, 该类型具有延迟加载和存放代理对象的功能. //可能出现懒加载问题 // 默认使用懒加载 // Order order = session.get(Order.class, 1); // System.out.println(order.getName()); // System.out.println(order.getCustomer().getClass().getName()); }
3. 测试删除数据(略)
4. 测试更新数据(略)
Customer.hbm.xml中的<set>标签还有很多属性可以选, 比如: order-by="": 值为数据列的某列列名.