Hibernate 映射关联关系_第1页
Hibernate 映射关联关系_第2页
Hibernate 映射关联关系_第3页
Hibernate 映射关联关系_第4页
Hibernate 映射关联关系_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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使用 节点来维护多对一关联关系。name 属性:多这一端关联的一那一端的属性的名称。class 属性:关联的一端的属性的类型。column 属性:一那一端在多的一端对应的数据表中的外键。可以任意命名,但需要和数据表中的字段对应。(4)单向多对一的 CRUD 以及需要注意的问题。 新增先保存一的一端 Customer,后保存多的一端 Order。 Save.java打印 SQL: Output结论:发送了3条 INSERT 语句。先保存多的一端 Order,再保存一的一端 Customer。 Save2.java打印 SQL: Output2结论:发送了3条 INSERT 语句,2条 UPDATE 语句。总结:在单向多对一的关联关系下,先插入 1 的一端会减少 SQL 语句的执行,性能更高。删除先删除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 的一端。更新 Update.java Output查询查询 n 的一端,但是不使用查询出来关联的 1 的一端的对象。Testpublic void testMany2OneGet() Order order = (Order) session.get(Order.class, 1); System.out.println(order.getCustomer().getClass().getName(); 复制代码Hibernate: select order0_.order_id as order_id1_1_0_, order0_.order_name as order_na2_1_0_, order0_.customer_id as customer3_1_0_ from hibernate.order order0_ where order0_.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: select order0_.order_id as order_id1_1_0_, order0_.order_name as order_na2_1_0_, order0_.customer_id as customer3_1_0_ from hibernate.order order0_ where order0_.order_id=?com.nucsoft.hibernate.Customer_$_jvst30c_1Hibernate: select customer0_.customer_id as customer1_0_0_, customer0_.customer_name as customer2_0_0_ from hibernate.customer customer0_ where customer0_.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();复制代码org.hibernate.LazyInitializationException: 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 文件。手动的去建立关联关系。生成简单的持久化类 和 Entity.hbm.xml 文件 Customer.java Order.java Customer.hbm.xml Order.hbm.xml手动建立关联关系在 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 类型的属性。1 的一端的 Set 类型属性数据还是存放在 n 的一端。 set 元素name 属性:待映射的 Set 类型的属性的属性名称。table 属性:待映射的 Set 属性的泛型类型所对应的表。key 子元素:column 属性,多的一端的外键名称。one-to-many 子元素:class 属性,n 的一端的持久化类名称。对应关系如图。最终的实体类和 Entity.hbm.xml 文件。 Customer.java Order.java Customer.hbm.xml Order.hbm.xml(4)通过 Intellij Idea 直接生成双向的多对一的关联关系。为生成的每个 Entity.hbm.xml 文件添加主键生成方式。为 Customer 类中的 orders 属性进行初始化。最终的持久化类和 Entity.hbm.xml。 Customer.java Order.java Customer.hbm.xml Order.hbm.xml对比发现,通过 Intellij Idea 自动生成的 Customer.hbm.xml 文件中 set 元素多了一个 inverse 属性。稍后进行说明。(5)双向多对一的 CRUD 和需要注意的问题新增双方都维护关联关系,即没有设置 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 的一端来维护。即设置 Customer.hbm.xml 的 set 元素 inverse 属性值为 true。先保存 1 的一端,后保存 n 的一端。 Output结果:只会发送3条 INSERT 语句。总结:介绍了双向的多对一的下的保存操作,若都维护关联关系,则会多出 UPDATE 语句。且若外键存在非空约束时,不能先保存 n 的一端。所以在进行 Hibernate 双向多对一保存的时候,最好的做法就是:1 的一端放弃维护关联关系,即 设置 set 节点的 inverse 属性为 true。同时在保存的时候先保存 1 的一端,后保存 n 的一端。删除 Delete同删除单向的多对一相同,会抛出异常:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails存在外键约束。更新 Update Output查询Testpublic void testMany2OneBothGet() Customer customer = (Customer) session.get(Customer.class, 5); System.out.println(customer.getOrders().getClass();打印结果:复制代码Hibernate: select customer0_.customer_id as customer1_0_0_, customer0_.customer_name as customer2_0_0_ from hibernate.customer customer0_ where customer0_.customer_id=?class ernal.PersistentSet复制代码并没有查询关联的 Order 集合,实际类型为 Hibernate 内置的一个 Set 实现类。证明了:当 Session 从数据库中加载 Java 集合时,创建的是 Hibernate 内置的集合类的实例。因此在持久化类中定义集合属性时需要定义成接口类型,不能是具体的某个实现类。也证明了:Hibernate 内置的集合具有集合代理功能,因为有代理功能,所以支持延迟检索策略。说明这里没有介绍 cascade 属性,是因为在实际的项目中,为了保护数据,很少设置 cascade 属性,而是手动去处理。二、映射一对一关联关系。1.这里只介绍双向的一对一关联关系如何映射。单向的一对一只需要把没有外键的一端去掉就好了。2.基于外键映射的双向一对一(1)以 Dempartment 和 Manager 为例。一个部门只能有一个经理,一个经理职能管理一个部门。(2)创建 Department 和 Manager 表。在 Department 表建立 Manager 表的外键。 department.sql Manager.xml(3)通过 Intellij Idea 自动生成的双向 1 对 1 无法映射列。这里通过 Intellij Idea 生成简单的持久化类和 Entity.hbm.xml 文件,然后手动的添加映射。 Department.java Manager.java Department.hbm.xml Manager.hbm.xml(4)说明:在 department 表中来维护外键。在映射的时候选择 元素,通过 unique 属性来达到 一对一的效果。在 manager 表中没有维护外键。在映射时候选择 元素,至于属性 property-ref 稍后在查询的时候说明。(5)CRUD 以及需要注意的地方。save Save1.java Output1 Save2.java Output2对比发现,先保存没有外键列的对象,会减少 UPDATE 语句的发送,提高性能。delete Delete1.java Output1 Delete2.javaCannot delete or update a parent row: a foreign key constraint fails (hibernate.department, CONSTRAINT FK_kpcmf8csabfn9epikikcfqbk0 FOREIGN KEY (manager_id_fk) REFERENCES manager (manager_id)外键关联对象存在的情况下,不能先删除拥有外键的对象。update Update.java Outputget Get1.java Output1可以看到, 查询拥有外键对象关联的对象时,采用的还是懒加载机制。此种情况下,若 session 关闭,再去调用关联对象的某个属性,会发生懒加载异常。查询双向一对一中没有外键的一端: Get2.java复制代码Hibernate: select manager0_.manager_id as manager1_1_1_, manager0_.manager_name as manager2_1_1_, department1_.dept_id as dept1_0_0_, department1_.dept_name as dept2_0_0_, department1_.manager_id_fk as manager3_0_0_ from hibernate.manager manager0_ left join hibernate.department department1_ on manager0_.manager_id=department1_.dept_id where manager0_.manager_id=?com.nucsoft.hibernate.Manager8ba复制代码复制代码Hibernate: select manager0_.manager_id as manager1_1_1_, manager0_.manager_name as manager2_1_1_, department1_.dept_id as dept1_0_0_, department1_.dept_name as dept2_0_0_, department1_.manager_id_fk as manager3_0_0_ from hibernate.manager manager0_ left outer join hibernate.department department1_ on manager0_.manager_id=department1_.manager_id_fk where manager0_.manager_id=?com.nucsoft.hibernate.Manager8ba复制代码可以发现,在第一次查询时,没有设置 property-ref 属性。左外链接查询时,虽然结果正确,但是连接条件不正确。至于说,为什么查询 Manager 对象的时候,使用了左外链接而不是懒加载,因为 Manager 端没有 Deparment 的外键。它不知道谁与它有关系。只能通过左外链接查询一次查询。3.基于主键的双向的一对一(1)本质上和基于外键的双向一对一关联一样,只不过是以主键作为了外键来使用的。(2)还是以 Department 和 Manager 为例。(3)deparment 表做了改动,因为是基于主键的映射,这里将 department 表中 manager_id_fk 去掉了。(4)Department.hbm.xml 和 Manager.hbm.xml 文件复制代码 manager 复制代码对于 Department.hbm.xml 文件,主键生成方式改为 foreign,并且指定了主键生成所依赖的属性所对应的持久化类的主键生成方式。需要注意的是,需要在 节点添加 constrained 属性为true。复制代码 复制代码将 Manager.hbm.xml 文件的 节点的 property-ref 属性去掉了。(5)CRUD及需要注意的问题save Save1.java Save2.java Output结论:发现不论是先保存 department ,还是先保存 manager,都是先插入的 manager。因为 department 的主键生成方式是依赖于 manager 的。update Update.java Output Update2.java Output2get Get.java Output通过 department 获取到的 manager 采用的是懒加载机制。而从 manager 获取 dept ,是通过左外链接查询的。至于原因,第一小点已经提到,本质上和基于外键的双向一对一关联一样,只不过是以主键作为了外键来使用的。delete Delete.javaCannot delete or update a parent row: a foreign key constraint fails (hibernate.department, CONSTRAINT FK_8hf3vewo7w3v9doungcc51wwy FOREIGN KEY (dept_id) REFERENCES manager (manager_id)若先删除 manager ,且存在和此 manager 关联的 department ,需要先删除关联的 department 记录。三、映射 n 对 n 关联关系1.单向的 n 对 n 关联(1)必须使用中间表(2)以 Category 和 Item 为例。一个分类下可以有多个商品,一个商品可以属于多个分类。以 Category 下存在 Set 集合为例来测试单向的 n 对 n 映射。(3)创建 category、item、categories_items 表 Sql(4)通过 Intellij Idea 生成持久化类和 Entity.hbm.xml 文件生成的持久化类和 Category.hbm.xml 和 Item.hbm.xml 文件。 Category.java Item.java Item.hbm.xml复制代码 复制代码注释已经讲的很明白了。(5)CRUD 以及需要注意的地方。save Save1.java Output1 Save2.java Output2结论:对比发现,不论是先保存 Item 还是先保存 Category 对象,都不会多发送 UPDATE 语句,因为最终的关联关系是通过中间表进行维护的。get Get.java Output还是懒加载,但是查询关联的 Items 的时候,内连接关联了中间表。delete Delete1.java复制代码Hibernate: select

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论