1、 新增先保存一的一端 Customer,后保存多的一端 Order。 Save.java打印 SQL: Output结论:发送了3条 INSERT 语句。先保存多的一端 Order,再保存一的一端 Customer。 Save2.java Output2发送了3条 INSERT 语句,2条 UPDATE 语句。总结:在单向多对一的关联关系下,先插入 1 的一端会减少 SQL 语句的执行,性能更高。2删除先删除1的一端。 Delete.java控制台打印:Cannot delete or update a parent row: a foreign key constraint fails (h
2、ibernate.order, CONSTRAINT FK_m6q2ofkj1g5aobtb2p00ajpqg FOREIGN KEY (customer_id) REFERENCES customer (customer_id)在不设置级联关系的前提下,不能删除 1 的一端。3更新 Update.java4查询查询 n 的一端,但是不使用查询出来关联的 1 的一端的对象。Testpublic void testMany2OneGet() Order order = (Order) session.get(Order.class, 1); System.out.println(order.ge
3、tCustomer().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 的一端的对象。 or
4、der.getCustomer().getCustomerName(); customer0_.customer_id as customer1_0_0_, customer0_.customer_name as customer2_0_0_ hibernate.customer customer0_ customer0_.customer_id=?可以发现,采用的是懒加载机制,即获取到的 1 的一端的对象是一个代理对象。只有在使用这个对象的属性的情况下,才会发送 SQL 语句。 由懒加载机制引发的 懒加载异常。 session.close();org.hibernate.LazyInitia
5、lizationException: could not initialize proxy - no Session在需要使用对象之前,关闭了 Session 连接,由此会引发 LazyInitializationException 异常。2.双向的多对一(1)还是以 Order 和 Customer 为例:双向的多对一不仅仅要在 Order 类中定义一个 Customer 属性,而在 Customer 类中也需定义存放 Order 对象的集合属性。(2)创建 Order 和 Customer 表和创建单向多对一相同。(3)通过 Intellij Idea 生成简单的持久化类和 Entity.
6、hbm.xml 文件。手动的去建立关联关系。生成简单的持久化类 和 Entity.hbm.xml 文件 Customer.java Order.java Customer.hbm.xml手动建立关联关系在 Order 一端建立多对一的关联关系。在 Order 持久化类中添加 Customer 类型的一个属性 customer。在 Order.hbm.xml 文件中添加多对一的关联关系。同时修改主键生成方式为 native。在 Customer 一端建立一对多的关联关系。在 Customer 持久化类中添加 Order 的一个集合 orders。在 Customer.hbm.xml 添加一对多的
7、关联关系。详细说明:在 Customer.hbm.xml 文件中添加一对多的关联关系。当 Session 从数据库中加载 Java 集合时,创建的是 Hibernate 内置的集合类的实例。因此在持久化类中定义集合属性时需要定义成接口类型,不能是具体的某个实现类。Hibernate 内置的集合具有集合代理功能,因为有代理功能,所以支持延迟检索策略。在定义集合的时候,通常将其初始化为集合实现类的一个实例,防止 NullPointerException。Hibernate 使用 元素来映射 Set 类型的属性。1 的一端的 Set 类型属性数据还是存放在 n 的一端。 set 元素待映射的 Set
8、 类型的属性的属性名称。table 属性:待映射的 Set 属性的泛型类型所对应的表。key 子元素:column 属性,多的一端的外键名称。one-to-many 子元素:class 属性,n 的一端的持久化类名称。对应关系如图。最终的实体类和 Entity.hbm.xml 文件。(4)通过 Intellij Idea 直接生成双向的多对一的关联关系。为生成的每个 Entity.hbm.xml 文件添加主键生成方式。为 Customer 类中的 orders 属性进行初始化。最终的持久化类和 Entity.hbm.xml。对比发现,通过 Intellij Idea 自动生成的 Custome
9、r.hbm.xml 文件中 set 元素多了一个 inverse 属性。稍后进行说明。(5)双向多对一的 CRUD 和需要注意的问题新增双方都维护关联关系,即没有设置 inverse 属性,且没有添加非空约束。先保存 1 的一端,再保存 n 的一端。打印SQL:结果:打印了 3 条 INSERT 语句,2 条 UPDATE 语句先保存 n 的一端,再保存 1 的一端。打印 SQL :打印了 3 条 INSERT 语句,4 条 UPDATE 语句。原因,双方都维护这关联关系。双方都维护关联关系,即没有设置 inverse 属性,对 order 表中的 customer_id 列添加非空约束(需要
10、更改两个地方)。先保存 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.h
11、bm.xml 的 set 元素 inverse 属性值为 true。先保存 1 的一端,后保存 n 的一端。只会发送3条 INSERT 语句。总结:介绍了双向的多对一的下的保存操作,若都维护关联关系,则会多出 UPDATE 语句。且若外键存在非空约束时,不能先保存 n 的一端。所以在进行 Hibernate 双向多对一保存的时候,最好的做法就是:1 的一端放弃维护关联关系,即 设置 set 节点的 inverse 属性为 true。同时在保存的时候先保存 1 的一端,后保存 n 的一端。 Delete同删除单向的多对一相同,会抛出异常:com.mysql.jdbc.exceptions.jdb
12、c4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails存在外键约束。 Updatepublic void testMany2OneBothGet() Customer customer = (Customer) session.get(Customer.class, 5); System.out.println(customer.getOrders().getClass();打印结果:class org.hibernat
13、e.collection.internal.PersistentSet并没有查询关联的 Order 集合,实际类型为 Hibernate 内置的一个 Set 实现类。证明了:也证明了:5说明这里没有介绍 cascade 属性,是因为在实际的项目中,为了保护数据,很少设置 cascade 属性,而是手动去处理。二、映射一对一关联关系。1.这里只介绍双向的一对一关联关系如何映射。单向的一对一只需要把没有外键的一端去掉就好了。2.基于外键映射的双向一对一(1)以 Dempartment 和 Manager 为例。一个部门只能有一个经理,一个经理职能管理一个部门。(2)创建 Department 和
14、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
15、 表中没有维护外键。在映射时候选择 元素,至于属性 property-ref 稍后在查询的时候说明。(5)CRUD 以及需要注意的地方。save Save1.java Output1对比发现,先保存没有外键列的对象,会减少 UPDATE 语句的发送,提高性能。delete Delete1.java Delete2.java a foreign key constraint fails (hibernate.department, CONSTRAINT FK_kpcmf8csabfn9epikikcfqbk0 FOREIGN KEY (manager_id_fk) REFERENCES mana
16、ger (manager_id)外键关联对象存在的情况下,不能先删除拥有外键的对象。updateget Get1.java可以看到, 查询拥有外键对象关联的对象时,采用的还是懒加载机制。此种情况下,若 session 关闭,再去调用关联对象的某个属性,会发生懒加载异常。查询双向一对一中没有外键的一端:one-to-one name=dept class=com.nucsoft.hibernate.Department/ Get2.java manager0_.manager_id as manager1_1_1_, manager0_.manager_name as manager2_1_1_
17、, department1_.dept_id as dept1_0_0_, department1_.dept_name as dept2_0_0_, department1_.manager_id_fk as manager3_0_0_ hibernate.manager manager0_ left join hibernate.department department1_ on manager0_.manager_id=department1_.dept_id manager0_.manager_id=?com.nucsoft.hibernate.Manager8ba property
18、-ref=manager left outer join on manager0_.manager_id=department1_.manager_id_fk 可以发现,在第一次查询时,没有设置 property-ref 属性。左外链接查询时,虽然结果正确,但是连接条件不正确。至于说,为什么查询 Manager 对象的时候,使用了左外链接而不是懒加载,因为 Manager 端没有 Deparment 的外键。它不知道谁与它有关系。只能通过左外链接查询一次查询。3.基于主键的双向的一对一(1)本质上和基于外键的双向一对一关联一样,只不过是以主键作为了外键来使用的。(2)还是以 Departmen
19、t 和 Manager 为例。(3)deparment 表做了改动,因为是基于主键的映射,这里将 department 表中 manager_id_fk 去掉了。(4)Department.hbm.xml 和 Manager.hbm.xml 文件hibernate-mapping id name=deptId column=dept_idgenerator class=foreignparam name=propertymanager/generator/idproperty name=deptNamedept_namecom.nucsoft.hibernate.Manager constra
20、ined=true/class/hibernate-mapping对于 Department.hbm.xml 文件,主键生成方式改为 foreign,并且指定了主键生成所依赖的属性所对应的持久化类的主键生成方式。需要注意的是,需要在 节点添加 constrained 属性为true。managerIdmanager_idnativemanagerNamemanager_name将 Manager.hbm.xml 文件的 节点的 property-ref 属性去掉了。(5)CRUD及需要注意的问题发现不论是先保存 department ,还是先保存 manager,都是先插入的 manager。因为 department 的主键生成方式是依赖于 manager 的。 Update2.java Get.java通过 department 获取到的 manager 采用的是懒加载机制。而从 manager 获取 dept ,是通过左外链接查询的。至于原因,第一小点已经提到,本质上和基于外键的双向一对一关联一样,只不过是以主键作