数据库中多表之间存在着三种关系,也即系统设计中的三种实体关系:多对多,一对多,一对一。
数据库中实体表之间的关系映射是采用外键来描述的,如下:
【一对多】
建表原则:在多的一方创建外键指向一的一方的主键
【多对多】
建表原则:创建一个中间表,中间表中至少两个字段作为外键分别指向多对多双方的主键
【一对一】
建表原则有两种:
*唯一外间对应:假设一对一种的任意一方为多,在多的一方创建外键指向一的一方的主键,然后将外间设置为唯一
*逐渐对应:一方的主键作为另一方的主键
在Hibernate中采用java对象关系来描述数据表之间的关系,如下图
【一对一】:在本类中定义对方类型的对象。
【一对多】:图中是一个A对应多个B类类型的情况,需在A类以Set集合的方式引入B类型的对象,在B类中定义A类类型的属性A
【多对多】:在本类中定义对方类型的Set集合。
Hibernate的一对多关联映射:
创建实体:
一的一方的实体:
public class Customer {
private Long cust_id;//客户编号(主键)
private String cust_name;//客户名称(公司名称)
private String cust_source;//客户信息来源
private String cust_industry;//客户所属行业
private String cust_level;//客户级别
private String cust_phone;//固定电话
private String cust_mobile;//移动电话
//通过ORM方式表示:一个客户对应多个联系人
//放置的多的一方的集合,Hibernate默认使用的是Set集合。
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
......
}
多的一方的实体:
public class LinkMan {
private Long lkm_id;//联系人编号(主键)
private String lkm_name;//联系人姓名
private String lkm_gender;//联系人性别
private String lkm_phone;//联系人办公电话
private String lkm_mobile;//联系人手机
private String lkm_email;//联系人邮箱
private String lkm_qq;//联系人qq
private String lkm_position;//联系人职位
private String lkm_memo;//联系人备注
//通过ORM方式表示:一个联系人只能属于一个客户
//放置的是一的一方的对象。
private Customer customer;
......
}
创建映射:
多的一方的映射的创建:
<?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>
<!-- 建立与表的映射 -->
<class name="com.ithou.hibernate.domain.LinkMan" table="cst_linkman">
<!-- 建立类中的属性与表中的主键映射 -->
<id name="lkm_id" column="lkm_id">
<!-- 主键生成策略 -->
<generator class="native"/>
</id>
<!-- 建立类中的普通属性与表中的字段对应 -->
<property name="lkm_name" column="lkm_name"/>
<property name="lkm_gender" column="lkm_gender"/>
<property name="lkm_phone" column="lkm_phone"/>
<property name="lkm_mobile" column="lkm_mobile"/>
<property name="lkm_email" column="lkm_email"/>
<property name="lkm_qq" column="lkm_qq"/>
<property name="lkm_position" column="lkm_position"/>
<property name="lkm_memo" column="lkm_memo"/>
<!-- 配置多对一的关系:放置的是一的一方的对象 -->
<!--
many-to-one标签
*name:一的一方的对象对象的属性名称
*class:一的一方的类的全路径
*column:在多的一方的表的外键的名称
-->
<many-to-one name="customer" class="com.ithou.hibernate.domain.Customer" column="lkm_cust_id"></many-to-one>
</class>
</hibernate-mapping>
一的一方的映射的创建:
<?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>
<!-- 建立与表的映射 -->
<class name="com.ithou.hibernate.domain.Customer" table="cst_customer">
<!-- 建立类中的属性与表中的主键映射 -->
<id name="cust_id" column="cust_id">
<!-- 主键生成策略 -->
<generator class="native"/>
</id>
<!-- 建立类中的普通属性与表中的字段对应 -->
<property name="cust_name" column="cust_name"/>
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
<!-- 配置一对多的映射:放置的多的一方的集合 -->
<!--
set标签
*name:多的一方的对象集合的属性名称
-->
<set name="linkMans">
<!--
key标签:
*column:多的一方的外键的名称
-->
<key column="lkm_cust_id"></key>
<!--
one-to-many标签:
*class:多的一方的类的全路径
-->
<one-to-many class="com.ithou.hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
编写测试类:
public class HibernateDemo1 {
/**
* 保存2个客户和3个联系人
*/
@Test
public void demo1() {
Session session = HibernateUtils.getCurrentSession();
Transaction ts = session.beginTransaction();
Customer customer1 = new Customer();
customer1.setCust_name("zhangsan");
Customer customer2 = new Customer();
customer2.setCust_name("lisi");
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("rufeng");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("zhangjian");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("libin");
//设置关系
customer1.getLinkMans().add(linkMan1);
customer1.getLinkMans().add(linkMan2);
customer2.getLinkMans().add(linkMan3);
linkMan1.setCustomer(customer1);
linkMan2.setCustomer(customer1);
linkMan3.setCustomer(customer2);
//保存数据
session.save(customer1);
session.save(customer2);
session.save(linkMan1);
session.save(linkMan2);
session.save(linkMan3);
ts.commit();
}
}
Hibernate一对多相关操作:
一对多关系只保存一边是否可以:不可以,报一个瞬时态对象异常:持久态对象关联了一个瞬时态对象
一对多的级联操作:
级联:操作一个对象的时候,是否会同时操作其关联的对象
级联是有方向性的:
*操作一的一方的时候,是否操作到多的一方
*操作多的一方的时候,是否操作到一的一方
级联保存或更新:
/**
* 级联保存或更新
* 保存客户级联联系人,操作的是客户对象,需要在Customer.hbm.xml中进行配置
* <set name="linkMans" cascade="save-update">
*/
@Test
public void demo2() {
Session session = HibernateUtils.getCurrentSession();
Transaction ts = session.beginTransaction();
Customer customer = new Customer();
customer.setCust_name("zhaohong");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("ruhua");
//设置关系
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
//保存数据
session.save(customer);
ts.commit();
}
/**
* 级联保存或更新
* 保存联系人级联客户,操作的是联系人对象,需要在LinkMan.hbm.xml中进行配置
* <many-to-one name="customer" cascade="save-update" class="com.ithou.hibernate.domain.Customer" column="lkm_cust_id"></many-to-one>
*/
@Test
public void demo3() {
Session session = HibernateUtils.getCurrentSession();
Transaction ts = session.beginTransaction();
Customer customer = new Customer();
customer.setCust_name("xiaolang");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("xiaohong");
//设置关系
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
//保存数据
session.save(linkMan);
ts.commit();
}
级联删除:
删除一边的时候,同时将另一方的数据也一并删除
/**
* 级联删除
* 删除客户级联联系人,删除的主体是客户,需要在Customer.hbm.xml中配置
* <set name="linkMans" cascade="delete">
*/
@Test
public void demo4() {
Session session = HibernateUtils.getCurrentSession();
Transaction ts = session.beginTransaction();
//没有设置级联删除,默认情况:修改了联系人的外键,删除客户
/*Customer customer = session.get(Customer.class, 9l);
session.delete(customer);*/
Customer customer = session.get(Customer.class, 13l);
session.delete(customer);
ts.commit();
}
删除联系人级联客户(基本不用)
/**
* 级联删除:
* 删除联系人级联客户,删除的主体是联系人,需要在LinkMan.hbm.xml中配置
* <many-to-one name="customer" cascade="delete">
*/
@Test
public void demo5() {
Session session = HibernateUtils.getCurrentSession();
Transaction ts = session.beginTransaction();
LinkMan linkMan = session.get(LinkMan.class, 2l);
session.delete(linkMan);
ts.commit();
}
一对多设置了双向关联产生多余的SQL语句:
解决多余的SQL语句:
*单向维护
*使一方放弃外键维护权:一的一方放弃。在set上配置inverse=“true”
区分cascade和inverse
cascade是操作关联对象的,inverse是控制一的一方是否控制外键的。