Hibernate 映射关联关系一、映射多对一关联关系。
1.单向的多对一(1)以Customer 和Order 为例:一个用户可以发出多个订单,而一个订单只能属于一个客户。
从Order 到Customer 是多对一关联关系。
(2)创建Customer 和Order 表。
Create(3)用Intellij Idea 自动生成关联关系,以及对应的Entitiy.hbm.xml 和持久化类。
说明:其中Type 是用来修饰对应的Attribute Name 的。
在Order 端,定义Customer 类,一个订单属于一个客户。
而在Customer 端,一个客户可以有多个订单,因为是单向的,所以这里放弃属性的添加。
在Join Columns 定义了Order 和Customer 之间的关联关系,order 表中的customer_id 外键和customer 表中的customer_id 主键关联。
来看生成的Schema:没有勾选customer_id,是因为Intellij Idea 没法直接映射为Customer 类型的customer。
Order.hbm.xml使用<many-to-one> 节点来维护多对一关联关系。
name 属性:多这一端关联的一那一端的属性的名称。
class 属性:关联的一端的属性的类型。
column 属性:一那一端在多的一端对应的数据表中的外键。
可以任意命名,但需要和数据表中的字段对应。
(4)单向多对一的CRUD 以及需要注意的问题。
<1> 新增①先保存一的一端Customer,后保存多的一端Order。
Save.java打印SQL:Output结论:发送了3条INSERT 语句。
②先保存多的一端Order,再保存一的一端Customer。
Save2.java打印SQL:Output2结论:发送了3条INSERT 语句,2条UPDATE 语句。
总结:在单向多对一的关联关系下,先插入 1 的一端会减少SQL 语句的执行,性能更高。
<2>删除先删除1的一端。
Delete.java控制台打印:Cannot delete or update a parent row: a foreign key constraint fails (`hibernate`.`order`, CONSTRAINT `FK_m6q2ofkj1g5aobtb2p00ajpqg` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`))结论:在不设置级联关系的前提下,不能删除 1 的一端。
<3>更新Update.javaOutput<4>查询①查询n 的一端,但是不使用查询出来关联的 1 的一端的对象。
@Testpublic void testMany2OneGet() {Order order = (Order) session.get(Order.class, 1);System.out.println(order.getCustomer().getClass().getName());}复制代码Hibernate:selectorder0_.order_id as order_id1_1_0_,order0_.order_name as order_na2_1_0_,order0_.customer_id as customer3_1_0_fromhibernate.order order0_whereorder0_.order_id=?order1com.nucsoft.hibernate.Customer_$$_jvst30c_1复制代码②查询n 的一端,使用查询出来关联的 1 的一端的对象。
@Testpublic void testMany2OneGet() {Order order = (Order) session.get(Order.class, 1);System.out.println(order.getCustomer().getClass().getName());order.getCustomer().getCustomerName();}复制代码Hibernate:selectorder0_.order_id as order_id1_1_0_,order0_.order_name as order_na2_1_0_,order0_.customer_id as customer3_1_0_fromhibernate.order order0_whereorder0_.order_id=?com.nucsoft.hibernate.Customer_$$_jvst30c_1Hibernate:selectcustomer0_.customer_id as customer1_0_0_,customer0_.customer_name as customer2_0_0_fromhibernate.customer customer0_wherecustomer0_.customer_id=?复制代码总结:可以发现,采用的是懒加载机制,即获取到的 1 的一端的对象是一个代理对象。
只有在使用这个对象的属性的情况下,才会发送SQL 语句。
③由懒加载机制引发的懒加载异常。
复制代码@Testpublic void testMany2OneGet() {Order order = (Order) session.get(Order.class, 1);System.out.println(order.getCustomer().getClass().getName());session.close();order.getCustomer().getCustomerName();}复制代码zyInitializationException: could not initialize proxy - no Session在需要使用对象之前,关闭了Session 连接,由此会引发LazyInitializationException 异常。
2.双向的多对一(1)还是以Order 和Customer 为例:双向的多对一不仅仅要在Order 类中定义一个Customer 属性,而在Customer 类中也需定义存放Order 对象的集合属性。
(2)创建Order 和Customer 表和创建单向多对一相同。
(3)通过Intellij Idea 生成简单的持久化类和Entity.hbm.xml 文件。
手动的去建立关联关系。
<1>生成简单的持久化类和Entity.hbm.xml 文件Customer.javaOrder.javaCustomer.hbm.xmlOrder.hbm.xml<2>手动建立关联关系①在Order 一端建立多对一的关联关系。
在Order 持久化类中添加Customer 类型的一个属性customer。
在Order.hbm.xml 文件中添加多对一的关联关系。
同时修改主键生成方式为native。
②在Customer 一端建立一对多的关联关系。
在Customer 持久化类中添加Order 的一个集合orders。
在Customer.hbm.xml 添加一对多的关联关系。
同时修改主键生成方式为native。
③详细说明:在Customer.hbm.xml 文件中添加一对多的关联关系。
当Session 从数据库中加载Java 集合时,创建的是Hibernate 内置的集合类的实例。
因此在持久化类中定义集合属性时需要定义成接口类型,不能是具体的某个实现类。
Hibernate 内置的集合具有集合代理功能,因为有代理功能,所以支持延迟检索策略。
在定义集合的时候,通常将其初始化为集合实现类的一个实例,防止NullPointerException。
Hibernate 使用<set> 元素来映射Set 类型的属性。
1 的一端的Set 类型属性数据还是存放在n 的一端。
④set 元素name 属性:待映射的Set 类型的属性的属性名称。
table 属性:待映射的Set 属性的泛型类型所对应的表。
key 子元素:column 属性,多的一端的外键名称。
one-to-many 子元素:class 属性,n 的一端的持久化类名称。
对应关系如图。
⑤最终的实体类和Entity.hbm.xml 文件。
Customer.javaOrder.javaCustomer.hbm.xmlOrder.hbm.xml(4)通过Intellij Idea 直接生成双向的多对一的关联关系。
<1>为生成的每个Entity.hbm.xml 文件添加主键生成方式。
<2>为Customer 类中的orders 属性进行初始化。
<3>最终的持久化类和Entity.hbm.xml。
Customer.javaOrder.javaCustomer.hbm.xmlOrder.hbm.xml<4>对比发现,通过Intellij Idea 自动生成的Customer.hbm.xml 文件中set 元素多了一个inverse 属性。
稍后进行说明。
(5)双向多对一的CRUD 和需要注意的问题<1>新增①双方都维护关联关系,即没有设置inverse 属性,且没有添加非空约束。
先保存 1 的一端,再保存n 的一端。
Save.java打印SQL:Output结果:打印了 3 条INSERT 语句,2 条UPDATE 语句先保存n 的一端,再保存 1 的一端。
Save2.java打印SQL :Output2结果:打印了 3 条INSERT 语句,4 条UPDATE 语句。
原因,双方都维护这关联关系。
②双方都维护关联关系,即没有设置inverse 属性,对order 表中的customer_id 列添加非空约束(需要更改两个地方)。
先保存n 的一端,再保存 1 的一端,会抛出异常。
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : com.nucsoft.hibernate.Order.customer -> com.nucsoft.hibernate.Customer③ 1 的一端放弃维护关联关系,只由n 的一端来维护。