查看“Hibernate笔记 5:实体关联关系”的源代码
←
Hibernate笔记 5:实体关联关系
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Hibernate]] == 关于 == 数据库中多表之间存在着三种关联关系(一对一、一对多、多对多),用于描述实体数据之间的关系,而这种关系也可以通过对象进行描述。 实体关系 # 一对一: #: <syntaxhighlight lang="Java" highlight=""> class A{ B b; } class B{ A a; } </syntaxhighlight> #* 开发中并不常用; # '''一对多''': #: <syntaxhighlight lang="Java" highlight=""> class A{ Set<B> bs; } class B{ A a; } </syntaxhighlight> # '''多对多''': #: <syntaxhighlight lang="Java" highlight=""> class A{ Set<B> bs; } class B{ Set<A> as; } </syntaxhighlight> == 一对多 == 有三种方式维护“一对多”的关联关系: 1、单向关联:一对多 ——仅由“一端”维护关联关系。 2、单向关联:多对一 ——仅由“多端”维护关联关系。 3、双向关联:一对多 ——在“一端”与“多端”均维护关联关系。 '''根据不同的情况和业务需求来选择不同的关联方式。''' 以“客户”(Customer)与“联系人”(LinkMan)为例: # 一个客户可以有多个联系人; # 一个联系人只能被指定给一个客户; === 单向关联:一对多 === 由“'''一端'''”维护关联关系,“多端”仅维护自身属性。 示例: * 在“Customer”中使用“linkMans”来维护其关联的“LinkMan”集合; # Customer: #: <syntaxhighlight lang="Java" highlight="8-9"> public class Customer { private Long cust_id; private String cust_name; private String cust_industry; private String cust_level; private String cust_phone; // 客户类中维护联系人对象集合 private Set<LinkMan> linkMans = new HashSet<LinkMan>(); // getter、setter ... } </syntaxhighlight> #: <syntaxhighlight lang="xml" highlight="13-17"> ... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> <id name= "cust_id"> <generator class="native"/> </id> <property name="cust_name" length="32"/> <property name="cust_industry" column= "cust_industry"/> <property name="cust_level" column= "cust_level"/> <property name="cust_phone" column= "cust_phone"/> <!-- 配置关联对象集合 --> <set name="linkMans"> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> </class> </hibernate-mapping> </syntaxhighlight> #* 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。 # LinkMan: #: <syntaxhighlight lang="Java" highlight=""> public class LinkMan { private Long lkm_id; private String 1km_name; private String lkm_phone; // getter、setter ... } </syntaxhighlight> #: <syntaxhighlight lang="xml" highlight=""> ... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> <id name= "lkm_id"> <generator class="native"/> </id> <property name="1km_name" length="32"/> <property name="1km_name" column= "1km_name"/> <property name="lkm_phone" column= "lkm_phone"/> </class> </hibernate-mapping> </syntaxhighlight> === 单向关联:多对一 === “一端”仅维护自身属性,由“'''多端'''”维护关联关系。 示例: * 在“LinkMan”中使用“customer”属性来维护其关联的“Customer”; # Customer: #: <syntaxhighlight lang="Java" highlight=""> public class Customer { private Long cust_id; private String cust_name; private String cust_industry; private String cust_level; private String cust_phone; // getter、setter ... } </syntaxhighlight> #: <syntaxhighlight lang="xml" highlight=""> ... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> <id name= "cust_id"> <generator class="native"/> </id> <property name="cust_name" length="32"/> <property name="cust_industry" column= "cust_industry"/> <property name="cust_level" column= "cust_level"/> <property name="cust_phone" column= "cust_phone"/> </class> </hibernate-mapping> </syntaxhighlight> # LinkMan: #: <syntaxhighlight lang="Java" highlight="6-7"> public class LinkMan { private Long lkm_id; private String 1km_name; private String lkm_phone; // 联系人类中维护客户对象 private Customer customer; // getter、setter ... } </syntaxhighlight> #: <syntaxhighlight lang="xml" highlight="12-13"> ... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> <id name= "lkm_id"> <generator class="native"/> </id> <property name="1km_name" length="32"/> <property name="1km_name" column= "1km_name"/> <property name="lkm_phone" column= "lkm_phone"/> <!-- 配置关联对象 --> <many-to-one name="customer" class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> </class> </hibernate-mapping> </syntaxhighlight> #* 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。 === 双向关联:一对多/多对一 === 在“'''一端'''”与“'''多端'''”均维护关联关系。 示例: * 在“Customer”中使用“linkMans”来维护其关联的“LinkMan”集合; * 在“LinkMan”中使用“customer”属性来维护其关联的“Customer”; # Customer: #: <syntaxhighlight lang="Java" highlight="8-9"> public class Customer { private Long cust_id; private String cust_name; private String cust_industry; private String cust_level; private String cust_phone; // 客户类中维护联系人对象集合 private Set<LinkMan> linkMans = new HashSet<LinkMan>(); // getter、setter ... } </syntaxhighlight> #: <syntaxhighlight lang="xml" highlight="13-17"> ... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> <id name= "cust_id"> <generator class="native"/> </id> <property name="cust_name" length="32"/> <property name="cust_industry" column= "cust_industry"/> <property name="cust_level" column= "cust_level"/> <property name="cust_phone" column= "cust_phone"/> <!-- 配置关联对象集合 --> <set name="linkMans"> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> </class> </hibernate-mapping> </syntaxhighlight> # LinkMan: #: <syntaxhighlight lang="Java" highlight="6-7"> public class LinkMan { private Long lkm_id; private String 1km_name; private String lkm_phone; // 联系人类中维护客户对象 private Customer customer; // getter、setter ... } </syntaxhighlight> #: <syntaxhighlight lang="xml" highlight="12-13"> ... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> <id name= "lkm_id"> <generator class="native"/> </id> <property name="1km_name" length="32"/> <property name="1km_name" column= "1km_name"/> <property name="lkm_phone" column= "lkm_phone"/> <!-- 配置关联对象 --> <many-to-one name="customer" class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> </class> </hibernate-mapping> </syntaxhighlight> * 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。 == 多对多 == == cascade(级联操作) == “级联操作”:是指当“'''主控方'''”执行“保存”、“更新”或者“删除”操作时,其关联对象(“被控方”)也执行相同的操作。 1、在映射文件中通过对 cascade 属性的设置来控制是否对关联对象采用级联操作。 2、级联操作对各种关联关系都是有效的。 以下,以“一对多”关系为例。 '''级联是有方向性的''':通过“一端级联操作多端”或“多端级联操作一端”来进行级联操作,二者的含义是完全不同的。 === 级联更新 === ==== 主控端:“一端” ==== 主控端为“一端”,即“'''单向关联:一对多'''”。 Customer: <syntaxhighlight lang="xml" highlight="3"> ... <!-- 配置关联对象集合 --> <set name="linkMans" cascade=""save-update> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> ... </syntaxhighlight> 测试代码: : <syntaxhighlight lang="Java" highlight="18-23"> @Test // 保存客户,并级联保存其联系人 public void demo1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkManl = new LinkMan(); linkManl.setLkm_name("老张"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("老李"); // 建立关联关系 customer.getLinkMans().add(linkManl); customer.getLinkMans().add(linkMan2); // 保存 session.save(customer); tx.commit(); } </syntaxhighlight> ==== 主控端:“多端” ==== 主控端为“多端”,即“'''单向关联:多对一'''”。 LinkMan: <syntaxhighlight lang="xml" highlight="3"> ... <!-- 配置关联对象 --> <many-to-one name="customer" cascade=""save-update class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> ... </syntaxhighlight> 测试代码: : <syntaxhighlight lang="Java" highlight="18-24"> @Test // 保存联系人,并级联保存其客户 public void demo2() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkManl = new LinkMan(); linkManl.setLkm_name("老张"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("老李"); // 建立关联关系 linkManl.setCustomer(customer); linkMan2.setCustomer(customer); // 保存 session.save(linkManl); session.save(linkManl); tx.commit(); } </syntaxhighlight> === 级联删除 === ==== 主控端:“一端” ==== ==== 主控端:“多端” ==== === 级联操作与 SQL === == inverse(关系维护) == == FAQ == === 不配置 cascade 是否可以进行“更新”? === 不配置 cascade 则:不可以级联更新,但可以通过“同时保存一端和多端的对象”完成更新操作。 具体说明如下: 1、如果仅保存任意一方的对象,则将导致 save 操作失败。 ——由于“瞬时对象异常”(<span style="color: green">org.hibemate.TransientObjectException</span>) 示例:(“双向关联:一对多/多对一”) : <syntaxhighlight lang="Java" highlight=""> @Test // 保存联系人,并级联保存其客户 public void demo3() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("老张"); // 建立关联关系 customer.getLinkMans().add(linkMan); linkMan.setCustomer(customer); // 保存 session.save(customer); // session.save(linkMan); tx.commit(); } </syntaxhighlight> 如上代码,保存任意一方的对象,都会由于“持久态对象关联了瞬时态对象”,造成以下错误: : <syntaxhighlight lang="Java" highlight=""> org.hibemate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: ... </syntaxhighlight> # “session.save(customer);”:保存 customer 时,由于不能级联保存,其中的 linkMans 集合属性的元素对象(瞬时态)未被保存,从而导致该异常; # “session.save(linkMan);”:保存 linkMan 时,由于不能级联保存,其中的 customer 属性的对象(瞬时态)未被保存,从而导致该异常; 2、如果“同时保存一端和多端的对象”,则可以完成更新(代码更复杂,执行的 SQL 也更多)。 ——【'''非级联更新'''】 示例:(“双向关联:一对多/多对一”) : <syntaxhighlight lang="Java" highlight=""> @Test // 非级联保存:一个客户,及其两个联系人 public void demo() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkManl = new LinkMan(); linkManl.setLkm_name("老张"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("老李"); // 建立关联关系 customer.getLinkMans().add(linkManl); customer.getLinkMans().add(linkMan2); linkManl.setCustomer(customer); linkMan2.setCustomer(customer); // 保存 session.save(customer); session.save(linkManl); session.save(linkManl); tx.commit(); } </syntaxhighlight> 如上代码: : 对 Customer 对象进行了保存,也分别对 LinkMan 对象进行了保存; * 从控制台打印出的 SQL 语句可以看出: *# 执行了三次 insert(对 customer、linkManl、linkMan2) *# 执行了两次 update(对 linkManl、linkMan2 更新外键) *: <syntaxhighlight lang="Java" highlight=""> Hibernate: insert into cst_customer (cust_name, cust_industry, cust_level, cust_phone) values (?, ?, ?, ?) Hibernate: insert into cst_linkMan (1km_name, lkm_phone, lkm_cust_id) values (?, ?, ?, ?) Hibernate: insert into cst_linkMan (1km_name, lkm_phone, lkm_cust_id) values (?, ?, ?, ?) Hibernate: update cst_linkMan set lkm_cust_id where lkm_id=? Hibernate: update cst_linkMan set lkm_cust_id where lkm_id=? </syntaxhighlight> === 不配置 cascade 是否可以进行“删除”? === 不配置 cascade 则:可以完成删除,但未必是业务需要的效果。 示例: : <syntaxhighlight lang="Java" highlight=""> @Test // 非级联删除:一个客户 public void demo3() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = session.get(Customer.class, 1); session.delete(customer); tx.commit(); } </syntaxhighlight> 如上代码: # 先将该 customer 关联的所有 LinkMan 的外键置为 null; # 再删除该 customer。
返回至“
Hibernate笔记 5:实体关联关系
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息