关联映射、级联操作、关系维护 ---- Hibernate之一对多|多对一关系
叙:hibernate中的关联映射存在“一对多|多对一”关系和“多对多”关系;本章节轻风就关联映射的一对多多对一关系进行学习和总结记录;
Hibernate的关联映射之“一对多|多对一”关系
准备工作
需要有两个实体类以及实体类所对应的映射文件,由于之前创建的有Customer类以及其对应的映射文件,因此再创建一个实体类和其映射文件就好了(下面的代码涉及到马桑要学习的知识,即使看不懂可以先记着,下面会有详细的讲解记录);
创建LinkMan类
这个是联系人类,这个类是客户的联系人,也就是说一个客户下有多个联系人,这个联系人类就是“一对多|多对一”关系中的哪个“多”的一方;
package com.java.domain; public class LinkMan { private Long lkm_id; private Character lkm_gender; private String lkm_name; private String lkm_phone; private String lkm_mobile; private String lkm_email; private String lkm_qq; private String lkm_position; private String lkm_memo; //关联映射的关联类 private Customer customer; : : : // 省略的是类属性的get/set方法的构建 }
映射文件LinkMan.hbm.xml
映射文件之前有详细的介绍
<?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"> <hibernate-mapping package="com.java.domain"> <!-- 建立类与表的映射 --> <class name="LinkMan" table="cst_linkman"> <!-- 主键的配置 --> <id name="lkm_id" column="lkm_id"> <generator class="native" /> </id> <!-- 非主键属性的配置 --> <property name="lkm_gender" column="lkm_gender" ></property> <property name="lkm_name" column="lkm_name"></property> <property name="lkm_phone" column="lkm_phone"></property> <property name="lkm_mobile" column="lkm_mobile"></property> <property name="lkm_email" column="lkm_email"></property> <property name="lkm_qq" column="lkm_qq"></property> <property name="lkm_position" column="lkm_position"></property> <property name="lkm_memo" column="lkm_memo"></property> <!-- 多对一配置 --> <many-to-one name="customer" column="lkm_cust_id" class="Customer"> </many-to-one> </class> </hibernate-mapping>
新知识点讲解
根据代码说一下新增的知识点:
在创建的实体类中
- LinkMan实体类中
在这个类中有一个private Customer customer;
这个是创建一个关联映射中关联的类的对象,在这里创建一个Customer类对象是为了在操作数据进行保存联系人时能将关联的客户信息保存到联系人的数据表中;
- Customer实体类中
这个类并没有写出来,与原来的数据相比需要添加一个private Set<LinkMan> linkMans = new HashSet<LinkMan>();
这个是在Customer类中创建一个联系人LinkMan类的对象集合,目的和LinkMan中添加Customer类的对象一样;
分析:
其二者的书写是由其所处的位置决定,LinkMan类是“一对多|多对一”关系中的“多”的一项,多个LinkMan才对应一个Customer,因此在LinkMan类中只要创建一个Customer类就好了,反过来,在Customer类中则需要创建的是一个集合,方便一个Customer对应多个LinkMan;
映射文件配置分析
- LinkMan的映射文件LinkMan.hbm.xml
<!-- 多对一配置 -->
<many-to-one name="customer" column="lkm_cust_id" class="Customer"></many-to-one>
只有一个标签<many-to-one>翻译成中文就是“多对一”,关于映射文件中的关联映射的配置都在这类配置中了(有<one-to-one>、<many-to-one>、<many-to-many>、<one-to-many>),本章节我只学习了“多对一|一对多”关系,因此用到的只有<many-to-one>、<one-to-many>这两种,下面就先对多对一的配置进行学习:
在标签中有三个标签,分别是:
属性名 | 属性值 | 解释 |
---|---|---|
name | customer | LinkMan类中关联的类在LinkMan类中创建的对象名 |
column | lkm_cust_id | 存放关联对象相信息的属性名(在数据表中就是列名) |
class | Customer | LinkMan类所关联的类的全类名(我只写了类名,因为我在hibernate-mapping标签中配置了package的值) |
- Customer实体类的映射文件配置
在Customer.hbm.xml中配置的代码如下所示:
<!-- 一对多配置 -->
<set name="linkMans">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
这一段就是配置关联映射的配置;
总共有三个标签,其中各有各自的属性、属性值,详细介绍如下:
标签名 | 属性名 | 属性值 | 解释 |
---|---|---|---|
set | name | linkMans | Customer类中关联的类在LinkMan类中创建的对象名(因为Customer类中创建的LinkMan类对象是用set集合创建的,因此要用set标签进行配对) |
key | column | lkm_cust_id | Customer的主键作为外键被LinkMan的哪个属性(列)所引用; |
one-to-many | class | LinkMan | Customer类所关联的类的全类名(我只写了类名,因为我在hibernate-mapping标签中配置了package的值) |
在核心配置文件上进行配置
这个也就是在核心配置文件中引入两个类的ORM源路径,如下图所示:
测试
测试代码如下所示:
package com.java.demo; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.java.domain.Customer; import com.java.domain.LinkMan; import com.java.hibernate.Utils.HibernateUtils; public class Demo1 { @Test public void demo1(){ // 1.使用工具类创建出session Session session = HibernateUtils.openSession(); // 2.开启事务 Transaction bt = session.beginTransaction(); // 3.分别创建Customer类、LinkMan类的对象并写入数据 Customer c = new Customer(); c.setCust_name("喜羊羊与灰太狼"); LinkMan L1 = new LinkMan(); L1.setLkm_name("喜羊羊"); LinkMan L2 = new LinkMan(); L2.setLkm_name("美羊羊"); // 在Customer类对象中保存其对应的联系人信息 c.getLinkMans().add(L1); c.getLinkMans().add(L2); //在LinkMan类对象中保存其对应的客户信息 L1.setCustomer(c); L2.setCustomer(c); session.save(c); session.save(L1); session.save(L2); // 4.提交事务 bt.commit(); session.close(); } }
注意:在此关联映射中,需要关系声明,这个声明是需要两方都进行的,代码就是下面的:
// 在Customer类对象中保存其对应的联系人信息 -- 一对多的插入关系(关系声明) c.getLinkMans().add(L1); c.getLinkMans().add(L2); //在LinkMan类对象中保存其对应的客户信息 -- 多对一的插入关系(关系声明) L1.setCustomer(c); L2.setCustomer(c);
运行@Test后数据库中的对应的表中会出现对应的数据,如下图所示:
客户表中的数据:
联系人表中的数据:
重点看联系人表中的“lkm_cust_id”这列中的数值与客户表中的新创建的数据的id是否一致;
进阶操作
级联操作
在进行操作时会发现在数据的save()方法保存时存在缺陷,比如,新建一个客户并且客户下有N个联系人需要添加进去,那么在进行session.save(“linkman1”);的操作时需要写N次才行,因此,为了减少此代码的编写量,有了级联操作;
具体用法:
在客户和联系人的ORM文件中配置且配置一样,具体配置如下:
<set name="linkMans" inverse="true" cascade="save-update">
既是:在set标签中添加一个cascade属性;
cascade:级联属性
属性名 | 属性值 | 解释 |
---|---|---|
cascade | save-update | 配置此值是代表的是级联保存更新 |
cascade | delete | 配置此值是代表的是级联删除 |
cascade | all | 配置此值是代表的是以上两种都包含 |
在LinkMan的映射问价中没有set标签,只有一个<many-to-one>标签,因此直接写在这个标签中就好;
当配置完后无论从哪一方进行数据保存时,另一方的亦可以被连带保存;
注意:
1) 在联系人进行级联删除时,必须要在客户的映射文件中配置级联删除的功能,不然会存在被级联删除的客户下的联系人仍存在,这样会照成数据垃圾;
2) 一般情况下使用的是save-update这个设置,目的是为了保护数据的安全性,防止被误删;
关系维护
在hibernate中关系的维护默认是一对多和多对一都要进行维护,这是没有必要的,只要有一方进行维护就好,两次的话虽然在这个关系中不会报错,但是浪费资源,并且在多对多关系中会报错(下一章节学习的),因此,我们可以使用inverse属性进行设定是谁来维护;
配置维护方的属性是inverse;其属性值只有false、true这两个,inverse译为反转,如果设置为true,意味着这个实体不负责进行维护关系,如果为false则是负责关系的为维护;
配置的位置和cascade配置位置一样,如果在Customer类的ORM文件中配置了此设置并将其属性值配置为true的话,这就意味着Customer类并不负责关系维护,因此,默认的就是LinkMan类负责(在LinkMan类的映射文件中不要再进行设置);一般情况下只需要一方负责,约定俗成的是由“多”的一方来负责维护;
另外,以上面的代码为例,Customer类代表的是客户,客户不负责对关系的维护后在代码的实际编程中也会有所体现,可以省略掉Customer的关系设置,只由LinkMan类来设置关系就好,具体如下所示:
pass:本章节的“一对多|多对一”关系学习就到这里了,下章节会对“多对多”关系进行学习记录;