在表中的一对多,是使用外键关联,通过一张表的一个键另一个表的外键来建立一多关系;而在类中表示为一个类中有一个集合属性包含对方类的很多对象,而在另一个类中,只包含前述类的一个对象,从而实现一对多关系的建立!
而在Hibernate中采用的是Set类型集合,使用<one-to-many>和<many-to-one>来实现。
对于一对多的映射关系可以分为两种情况:单向映射和双向映射。
单向映射:只能从一方访问到另一方,无法反向访问。
双向映射:双方都可以通过映射访问到对方。
这里以双向映射为例,单向映射只需在一端的配置文件和代码中删除相应的代码块即可。
二、实例演示(双向映射)
1、模型抽象
这里以 Customer 和 Order 为例: 一个客户能发出多个订单, 而一个订单只能属于一个客户. 从 Order 到 Customer 的关联是多对一关联; 而从 Customer 到 Order 是一对多关联。
Customer.java
package com.chen.one2many; import java.util.HashSet; import java.util.Set; public class Customer { private Integer customerID; private String customerName; //持有一个order的集合对象,表示一个客户customer可以有多个订单order Set<Order> orders=new HashSet<>(); public Integer getCustomerID() { return customerID; } public void setCustomerID(Integer customerID) { this.customerID = customerID; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } }
Order.java
package com.chen.one2many; public class Order { private Integer orderID; private String orderName; //持有一个Customer的引用,一个订单order只能属于一个用户customer private Customer customer; public Integer getOrderID() { return orderID; } public void setOrderID(Integer orderID) { this.orderID = orderID; } public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
2、配置相关xml文件
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"> <!-- Generated 2017-3-10 10:28:28 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.chen.one2many.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> <!--利用set标签完成映射 name:该集合对应的属性名 orders table:关联的另一端对应的表名 ORDERS cascade 级联关系,当保存或更新时会级联保存与这个Customers对象相关联的所有Orders对象 inverse=true是将控制权抛出(给Order) --> <set name="orders" table="ORDERS" cascade="save-update" inverse="true"> <key column="CUSTOMER_ID"></key><!-- 表字段 --> <one-to-many class="com.chen.one2many.Order"/><!-- 关联的类 --> </set> </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"> <!-- Generated 2017-3-10 10:28:28 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.chen.one2many.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: Order(也就是一对多中多的这一端)中持有的另一端的属性名,也就是customer class: 上一个属性所对应的类 column: 映射的外键名,也就是另一个表的主键 --> <many-to-one name="customer" class="com.chen.one2many.Customer" column="CUSTOMER_ID"></many-to-one> </class> </hibernate-mapping>
Hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Hibernate 连接数据库的基本信息 --> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://192.168.1.148:3306/hibernate5</property> <!-- Hibernate 的基本配置 --> <!-- Hibernate 使用的数据库方言,为了使mysql自动生成数据表 对于mysql5.x使用如下设置 --> <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!-- 运行时是否打印 SQL --> <property name="show_sql">true</property> <!-- 运行时是否格式化 SQL --> <property name="format_sql">true</property> <!-- 生成数据表的策略 --> <property name="hbm2ddl.auto">update</property> <!-- 设置 Hibernate 的事务隔离级别 --> <property name="connection.isolation">2</property> <!-- 删除对象后, 使其 OID 置为 null --> <property name="use_identifier_rollback">true</property> <!-- 配置 C3P0 数据源 --> <property name="hibernate.c3p0.max_size">10</property> <property name="hibernate.c3p0.min_size">5</property> <property name="c3p0.acquire_increment">2</property> <property name="c3p0.idle_test_period">2000</property> <property name="c3p0.timeout">2000</property> <property name="c3p0.max_statements">10</property> <!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 --> <property name="hibernate.jdbc.fetch_size">100</property> <!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 --> <property name="jdbc.batch_size">30</property> <!-- 需要关联的 hibernate 映射文件 .hbm.xml --> <mapping resource="com/chen/one2many/Customer.hbm.xml"/> <mapping resource="com/chen/one2many/Order.hbm.xml"/> </session-factory> </hibernate-configuration>
3、编写测试方法
HibernateTest.java
package com.chen.one2many; import java.util.Iterator; import java.util.Set; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; public class HibernateTest { private static SessionFactory sessionFactory; private static Session session; private static Transaction transaction; //定义事务初始化函数 public static void init(){ Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); session = sessionFactory.openSession(); transaction = session.beginTransaction(); } //定义事务结束时的处理函数 public static void doit(){ transaction.commit(); session.close(); sessionFactory.close(); } //编写自己的处理过程 public static void test(){ //初始化基本信息:一个客户chen有两个订单book和food Customer customer=new Customer(); customer.setCustomerName("chen"); Order order1=new Order(); order1.setOrderName("book"); Order order2=new Order(); order2.setOrderName("food"); //设置对应关系(双向映射) //关联customer order1.setCustomer(customer); order2.setCustomer(customer); //关联order customer.getOrders().add(order1); customer.getOrders().add(order2); //因为配置文件中cascade="save-update",所以保存customer时会级联保存与之相关的order session.save(customer); //从order表中获取customer Order orderResult=(Order) session.get(Order.class, 1); String ordername=orderResult.getOrderName(); String customername=orderResult.getCustomer().getCustomerName(); System.out.println(ordername+" is owned by "+customername); //从customer表中获取order Customer customerResult=(Customer) session.get(Customer.class, 1); Set<Order> set=customerResult.getOrders(); System.out.println(set.size()); for (Iterator iterator2 = set.iterator(); iterator2.hasNext();) { Order order = (Order) iterator2.next(); System.out.println(order.getOrderName()); } } public static void main(String[] args) { HibernateTest.init(); HibernateTest.test(); HibernateTest.doit(); System.out.println("完成---------"); } }
4、运行结果
数据表
customers
orders
运行窗口显示
三、单向一对多映射与双向映射原理相同,只是把其中一端的配置文件和代码进行删减即可。具体而言:
1、单向order--->customer。即order中持有customer的一个引用。
order端的代码和配置文件不用改。把customer代码中的order集合代码删除,配置文件中的set标签内容删除。
2、单向customer---->order。不推荐。操作复杂
一般推荐使用order(多的一端)持有customer(一的一端)的引用,这样可以减小维护代价。
四、inverse 属性详解
在hibernate中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系。inverse = false 的为主动方,inverse = true 的为被动方即交出控制权, 由主动方负责维护关联关系。在没有设置 inverse=true 的情况下,父子两边都维护关联关系 。
在 1-N关系中,将 N 方设为主控方将有助于性能改善。在 1-N 关系中,若将 1 方设为主控方会额外多出 update 语句,且插入数据时无法同时插入外键列,因而无法为外键列添加非空约束。