数据表中种存在一对一、一对多、多对一的关系,比如部门表跟员工表就存在一对多的关系,一个部门对应多个员工,Hibernate中使用one-to-many、many-to-one、one-to-one来处理这些关系
一、SQL语句
订单表
CREATE TABLE `order` (
`ORDER_ID` int(2) NOT NULL AUTO_INCREMENT,
`ORDER_NAME` varchar(30) DEFAULT NULL,
`CUSTOMER_ID` int(2) DEFAULT NULL,
PRIMARY KEY (`ORDER_ID`),
KEY `customer_id_fk` (`CUSTOMER_ID`),
CONSTRAINT `customer_id_fk` FOREIGN KEY (`CUSTOMER_ID`) REFERENCES `customer` (`CUSTOMER_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
顾客表
CREATE TABLE `customer` (
`CUSTOMER_ID` int(2) NOT NULL,
`CUSTOMER_NAME` varchar(30) DEFAULT NULL,
PRIMARY KEY (`CUSTOMER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
顾客和订单是一对多关系,一个顾客对应多个订单
二、实体和表映射文件
顾客实体(省略getter和setter)
public class Customer implements java.io.Serializable {
// Fields
private Integer customerId;
private String customerName;
/*
* 1.声明集合类型时, 需使用接口类型, 因为 hibernate 在获取
* 集合类型时, 返回的是 Hibernate 内置的集合类型, 而不是 JavaSE 一个标准的
* 2.需要把集合初始化化,否则会出现空指针异常
*/
private Set<Order> orders = new HashSet<>();
}
注:一那一方的实体需要一个set来装载多的一方
顾客表映射文件
<?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">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.zhuojing.hibernate.entities.onetomany.Customer" table="customer" catalog="hibernate">
<id name="customerId" type="java.lang.Integer">
<column name="CUSTOMER_ID" />
<generator class="assigned" />
</id>
<property name="customerName" type="java.lang.String">
<column name="CUSTOMER_NAME" length="30" />
</property>
<!-- 映射1 对 多 -->
<!-- set:映射 set 类型属性,table:set 中元素对应的记录放在哪一个数据表中,该值需要和多对一的多的那个表的名字一致 -->
<!-- inverse:指定由哪一方来维护列的名字 ,通常设置为true,以指定由对方来维护关联关系,这边在set设置true就表示交给1的一方维护,set代表多的一方-->
<!-- cascade:设定级联操作,开发时候不建议设定这个属性,建议用手工的操作 -->
<!-- order-by:在查询时对集合的元素进行排序,order-by 中使用是表的字段名,不是持久化的属性名 -->
<set name="orders" inverse="true" table="order" cascade="delete" order-by="ORDER_NAME ASC">
<!-- 指定多的表中的外键的列的名字 -->
<key>
<column name="CUSTOMER_ID" />
</key>
<!-- 指定映射关系 -->
<one-to-many class="com.zhuojing.hibernate.entities.onetomany.Order"/>
</set>
</class>
</hibernate-mapping>
订单实体
public class Order implements java.io.Serializable {
// Fields
private Integer orderId;
private Customer customer;
private String orderName;
}
订单表映射文件
<?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">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.zhuojing.hibernate.entities.onetomany.Order" table="order" catalog="hibernate">
<id name="orderId" type="java.lang.Integer">
<column name="ORDER_ID" />
<generator class="identity" />
</id>
<property name="orderName" type="java.lang.String">
<column name="ORDER_NAME" length="30" />
</property>
<!--
映射多对一的关联关系:使用 many-to-one 来映射多对一关联关系
name:多对一关联的属性的名字
class:一的那一端的属性对应的类名
column:一那端在多的一端对应的数据表的外键的名字
-->
<many-to-one name="customer" class="com.zhuojing.hibernate.entities.onetomany.Customer" fetch="select">
<column name="CUSTOMER_ID" />
</many-to-one>
</class>
</hibernate-mapping>
四、测试类
package com.zhuojing.hibernate.entities.onetomany;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import javax.imageio.stream.FileImageInputStream;
import org.hibernate.Hibernate;
import org.hibernate.LazyInitializationException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jdbc.Work;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateOneToManyTest {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public 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();
}
@After
public void destroy(){
transaction.commit();
session.close();
sessionFactory.close();
}
@Test
public void testOneToManySave(){
Customer customer = new Customer(10, "DD");
Order order1 = new Order(customer, "ORDER-7");
Order order2 = new Order(customer, "ORDER-8");
customer.getOrders().add(order1);
customer.getOrders().add(order2);
//先插入一的一端,再插入多的一端:只有三条INSERT,两天 UPDATE 语句
//可以在1 的一端set 节点指定inverse=true,来使1 的一端放弃,维护关系,来减少 2 条UPDATE
session.save(customer);
session.save(order1);
session.save(order2);
//先查诶多的一端,再插入一的一端:3条INSERT,4条UPDATE
//因为在插入多的一端是,无法确定1的一端的外键值,所有智能等1的一端插入后,再额外发送UPDATE 语句
//推荐先插入一端,在插入多一端
/*session.save(order1);
session.save(order2);
session.save(customer);*/
}
@Test
public void testOneToManyQuery(){
//1.对n 的一端的集合使用延迟加载
Customer customer = (Customer)session.get(Customer.class, 6);
System.out.println(customer.getCustomerName());
//2.返回的n 的一端的集合是 Hibernate 内置的集合类型,该类型具有延迟加载和存放代理类的功能
System.out.println(customer.getOrders().getClass());
System.out.println(customer.getOrders().toString());
//可能会抛出 LazyInitializationException异常
//4.在需要使用集合元素的时候进行初始化
}
@Test
public void testOneToManyUpdate(){
Customer customer = (Customer)session.get(Customer.class, 5);
customer.getOrders().iterator().next().setOrderName("ABCD");
}
@Test
public void testManyToOneDelete(){
//在不设定定级联关系的情况下,且1这个一端的对象有 n 的对象在引用,不能直接删除 1 这一端的对象
Customer customer = (Customer)session.get(Customer.class, 5);
session.delete(customer);
}
}