--------------------siwuxie095
Hibernate 一对多操作
以客户和联系人为例,客户是一,联系人是多
即一个客户里面有多个联系人,一个联系人只能属于一个客户
注意:这里的客户是公司级的,即公司,联系人即公司里的员工
(一)一对多映射配置
第一步:创建两个实体类,客户和联系人
第二步:让两个实体类之间互相表示
(1)在客户实体类中表示多个联系人
(2)在联系人实体类中表示所属客户
第三步:配置映射关系
「一般一个实体类对应一个映射配置文件」
(1)配置基本的映射
(2)配置关联关系的映射(一对多关系)
1)在客户的映射配置文件中,表示所有联系人
2)在联系人的映射配置文件中,表示所属客户
第四步:在核心配置文件中引入映射配置文件
(二)一对多级联保存
如:添加客户,为这个客户添加一个联系人
(1)复杂写法
/**
* 一对多级联保存的复杂写法
*
*
* 手动加上 @Test 以进行单元测试(将自动导入 JUnit 4 的 jar 包)
*
* 选中方法名,右键->Run As->JUint Test
*/
@Test
publicvoid testSave(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//添加一个客户,为这个客户添加一个联系人
//(1)
//创建客户和联系人对象
Customer customer=new Customer();
customer.setCustName("百度");
customer.setCustLevel("VIP");
customer.setCustSource("网络");
customer.setCustPhone("110");
customer.setCustMobile("114");
LinkMan linkMan=new LinkMan();
linkMan.setLkmName("小明");
linkMan.setLkmGender("男");
linkMan.setLkmPhone("111");
//(2)
//建立客户对象和联系人对象的关系
//
//在客户实体类中表示联系人,在联系人实体类中表示客户
//
//具体:
//把联系人对象放到客户对象的 Set 集合中
//把客户对象放到联系人对象中
customer.getLinkManSet().add(linkMan);
linkMan.setCustomer(customer);
//(3)
//保存到数据库(级联保存)
session.save(customer);
session.save(linkMan);
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
(2)简化写法
先在客户的映射配置文件中的set 标签添加 cascade 属性,并
将其值设置为save-update,再进行具体实现
/**
* 一对多级联保存的简化写法
*
* 在客户的映射配置文件中的set 标签
* 添加 cascade 属性,并将其值设置为
* save-update
*/
@Test
publicvoid testSaveX(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//添加一个客户,为这个客户添加一个联系人
//(1)
//创建客户和联系人对象
Customer customer=new Customer();
customer.setCustName("谷歌");
customer.setCustLevel("普通");
customer.setCustSource("网络");
customer.setCustPhone("911");
customer.setCustMobile("995");
LinkMan linkMan=new LinkMan();
linkMan.setLkmName("小强");
linkMan.setLkmGender("男");
linkMan.setLkmPhone("999");
//(2)
//建立客户对象和联系人对象的关系
//
//在客户实体类中表示联系人
//
//具体:
//把联系人对象放到客户对象的 Set 集合中
customer.getLinkManSet().add(linkMan);
//(3)
//保存到数据库(级联保存)
session.save(customer);
//简化所在:不用把客户对象放到联系人对象
//中,且最后不用保存联系人对象
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
(三)一对多级联删除
如:删除某个客户,把客户里面的所有联系人都删除
(1)具体写法
先在客户的映射配置文件中的set 标签添加 cascade 属性,并
将其值设置为delete,再进行具体实现
/**
* 一对多级联删除
*
* 在客户的映射配置文件中的 set 标签
* 添加 cascade 属性,并将其值设置为
* delete
*/
@Test
publicvoid testDelete(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//删除一个客户,并将这个客户中的所有联系人删除
//(1)
//根据 id 查询客户对象
Customer customer=session.get(Customer.class, 1);
//(2)
//调用 Session 的 delete() 方法实现级联删除
session.delete(customer);
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
(2)执行过程
1)根据id 查询客户
2)根据外键id 值查询联系人
3)把联系人外键设置为null
4)删除联系人和客户
(四)一对多修改
如:让某联系人不再属于原客户,而属于新客户
(1)主要问题
因为 Hibernate 双向维护外键,即 在客户和联系人中都要配置外键
所以在修改客户时会修改一次外键,修改联系人时也会修改一次外键,
也就是说产生了多余的sql 语句,使得效率低下
(2)解决方式
一对多修改中,让其中一方放弃外键维护,一般是让一的一方放弃
(3)具体写法
先在客户的映射配置文件中的 set 标签添加 inverse 属性,并
将其值设置为true,再进行具体实现
/**
* 一对多修改
*
* 在客户的映射配置文件中的 set 标签
* 添加 inverse 属性,并将其值设置为
* true
*/
@Test
publicvoid testUpdate(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//在百度工作的小明跳槽到谷歌,修改联系人表中对应的外键
//(1)
//根据 id 分别查询客户(谷歌)和联系人(小明)
Customer customer=session.get(Customer.class, 2);
LinkMan linkMan=session.get(LinkMan.class, 1);
//(2)
//设置持久态对象的值:
//1)把联系人对象放到客户对象的 Set 集合中
//2)把客户对象放到联系人对象中
customer.getLinkManSet().add(linkMan);
linkMan.setCustomer(customer);
//持久态对象可以自动更新数据库,所以下面的代码不用写
//session.update(customer);
//session.update(linkMan);
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
工程结构目录如下:
HibernateUtils.java:
package com.siwuxie095.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtils {
static Configuration cfg=null;
static SessionFactory sessionFactory=null;
//或:加上 private final 亦可,不过此时不能等于 null
// private static final Configuration cfg;
// private static final SessionFactory sessionFactory;
//静态代码块
static {
//加载核心配置文件
cfg=new Configuration();
cfg.configure();
sessionFactory=cfg.buildSessionFactory();
}
//提供方法返回 sessionFactory
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
//提供方法返回与本地线程绑定的 Session
public static Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
Customer.java:
package com.siwuxie095.entity;
import java.util.HashSet;
import java.util.Set;
//客户实体类(客户是公司级的)
public class Customer {
private Integer cid; //客户 id
private String custName; //客户名称
private String custLevel; //客户级别
private String custSource; //客户来源
private String custPhone; //客户电话
private String custMobile; //客户手机
//在客户实体类中表示多个联系人,即一个客户里面有多个联系人
//
//Hibernate 中要求使用 Set 集合表示"多"的数据
private Set<LinkMan> linkManSet=new HashSet<LinkMan>();
public Set<LinkMan> getLinkManSet() {
return linkManSet;
}
publicvoid setLinkManSet(Set<LinkMan> linkManSet) {
this.linkManSet = linkManSet;
}
public Integer getCid() {
return cid;
}
publicvoid setCid(Integer cid) {
this.cid = cid;
}
public String getCustName() {
return custName;
}
publicvoid setCustName(String custName) {
this.custName = custName;
}
public String getCustLevel() {
return custLevel;
}
publicvoid setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustSource() {
return custSource;
}
publicvoid setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustPhone() {
return custPhone;
}
publicvoid setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustMobile() {
return custMobile;
}
publicvoid setCustMobile(String custMobile) {
this.custMobile = custMobile;
}
}
LinkMan.java:
package com.siwuxie095.entity;
//联系人实体类
public class LinkMan {
private Integer lid; // 联系人 id
private String lkmName; // 联系人姓名
private String lkmGender; // 联系人性别
private String lkmPhone; // 联系人电话
//在联系人实体类中表示所属客户,即一个联系人只能属于一个客户
private Customer customer;
public Customer getCustomer() {
return customer;
}
publicvoid setCustomer(Customer customer) {
this.customer = customer;
}
public Integer getLid() {
return lid;
}
publicvoid setLid(Integer lid) {
this.lid = lid;
}
public String getLkmName() {
return lkmName;
}
publicvoid setLkmName(String lkmName) {
this.lkmName = lkmName;
}
public String getLkmGender() {
return lkmGender;
}
publicvoid setLkmGender(String lkmGender) {
this.lkmGender = lkmGender;
}
public String getLkmPhone() {
return lkmPhone;
}
publicvoid setLkmPhone(String lkmPhone) {
this.lkmPhone = lkmPhone;
}
}
HibernateOneToMany.java:
package com.siwuxie095.hibernatetest;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;
import com.siwuxie095.entity.Customer;
import com.siwuxie095.entity.LinkMan;
import com.siwuxie095.utils.HibernateUtils;
//一对多操作
public class HibernateOneToMany {
/**
* 一对多级联保存的复杂写法
*
*
* 手动加上 @Test 以进行单元测试(将自动导入 JUnit 4 的 jar 包)
*
* 选中方法名,右键->Run As->JUint Test
*/
@Test
publicvoid testSave(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//添加一个客户,为这个客户添加一个联系人
//(1)
//创建客户和联系人对象
Customer customer=new Customer();
customer.setCustName("百度");
customer.setCustLevel("VIP");
customer.setCustSource("网络");
customer.setCustPhone("110");
customer.setCustMobile("114");
LinkMan linkMan=new LinkMan();
linkMan.setLkmName("小明");
linkMan.setLkmGender("男");
linkMan.setLkmPhone("111");
//(2)
//建立客户对象和联系人对象的关系
//
//在客户实体类中表示联系人,在联系人实体类中表示客户
//
//具体:
//把联系人对象放到客户对象的 Set 集合中
//把客户对象放到联系人对象中
customer.getLinkManSet().add(linkMan);
linkMan.setCustomer(customer);
//(3)
//保存到数据库(级联保存)
session.save(customer);
session.save(linkMan);
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
/**
* 一对多级联保存的简化写法
*
* 在客户的映射配置文件中的 set 标签
* 添加 cascade 属性,并将其值设置为
* save-update
*/
@Test
publicvoid testSaveX(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//添加一个客户,为这个客户添加一个联系人
//(1)
//创建客户和联系人对象
Customer customer=new Customer();
customer.setCustName("谷歌");
customer.setCustLevel("普通");
customer.setCustSource("网络");
customer.setCustPhone("911");
customer.setCustMobile("995");
LinkMan linkMan=new LinkMan();
linkMan.setLkmName("小强");
linkMan.setLkmGender("男");
linkMan.setLkmPhone("999");
//(2)
//建立客户对象和联系人对象的关系
//
//在客户实体类中表示联系人
//
//具体:
//把联系人对象放到客户对象的 Set 集合中
customer.getLinkManSet().add(linkMan);
//(3)
//保存到数据库(级联保存)
session.save(customer);
//简化所在:不用把客户对象放到联系人对象
//中,且最后不用保存联系人对象
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
/**
* 一对多级联删除
*
* 在客户的映射配置文件中的 set 标签
* 添加 cascade 属性,并将其值设置为
* delete
*/
@Test
publicvoid testDelete(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//删除一个客户,并将这个客户中的所有联系人删除
//(1)
//根据 id 查询客户对象
Customer customer=session.get(Customer.class, 1);
//(2)
//调用 Session 的 delete() 方法实现级联删除
session.delete(customer);
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
/**
* 一对多修改
*
* 在客户的映射配置文件中的 set 标签
* 添加 inverse 属性,并将其值设置为
* true
*/
@Test
publicvoid testUpdate(){
SessionFactory sessionFactory=null;
Session session=null;
Transaction tx=null;
try {
//得到 SessionFactory 对象
sessionFactory=HibernateUtils.getSessionFactory();
//创建 Session 对象
session=sessionFactory.openSession();
//开启事务
tx=session.beginTransaction();
//在百度工作的小明跳槽到谷歌,修改联系人表中对应的外键
//(1)
//根据 id 分别查询客户(谷歌)和联系人(小明)
Customer customer=session.get(Customer.class, 2);
LinkMan linkMan=session.get(LinkMan.class, 1);
//(2)
//设置持久态对象的值:
//1)把联系人对象放到客户对象的 Set 集合中
//2)把客户对象放到联系人对象中
customer.getLinkManSet().add(linkMan);
linkMan.setCustomer(customer);
//持久态对象可以自动更新数据库,所以下面的代码不用写
//session.update(customer);
//session.update(linkMan);
//提交事务
tx.commit();
} catch (Exception e) {
//回滚事务
tx.rollback();
} finally {
//关闭资源
session.close();
sessionFactory.close();
}
}
}
Customer.hbm.xml:
<?xmlversion="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>
<!--
(1)
class 标签:配置实体类和数据库表的对应;
name 属性:实体类的全路径,即全限定名;
table 属性:数据库表的名称(数据库表由 Hibernate 自动生成)
-->
<classname="com.siwuxie095.entity.Customer"table="t_customer">
<!--
(2)
id 标签:配置实体类 id 和表 id 对应(主键);
name 属性:实体类里 id 属性名称;
column 属性:生成表中 id 字段名称
-->
<idname="cid"column="cid">
<!-- native:设置主键 id 自动增长 -->
<generatorclass="native"></generator>
</id>
<!--
(3)
property 标签:配置其它属性和表中字段对应;
name 属性:实体类属性名称;
column 属性:生成表中字段名称
-->
<propertyname="custName"column="cust_name"></property>
<propertyname="custLevel"column="cust_level"></property>
<propertyname="custSource"column="cust_source"></property>
<propertyname="custPhone"column="cust_phone"></property>
<propertyname="custMobile"column="cust_mobile"></property>
<!--
(4)
set 标签:配置关联关系的映射(配置关联对象),代表一个 Set 集合;
name 属性:"多"的一方的对象的 Set 集合的名称(在客户实体类中声明);
cascade 属性:save-update 表示级联保存,delete 表示级联删除(逗号隔开);
inverse 属性:true 表示放弃关系维护(放弃外键的维护权)(默认为 false)
注意:inverse="true" 主要用于修改操作,防止产生多余的 sql 语句,但如果
同时配置了 cascade="save-update" 和 inverse="true",将会导致在级联保存
操作后,没有外键(为 null)
-->
<setname="linkManSet"cascade="save-update,delete"inverse="true">
<!--
一对多建表,有外键。Hibernate 的机制
是双向维护外键(即都配置外键)
key 标签:配置"多"的一方的外键
column 属性:"多"的一方的外键名称
-->
<keycolumn="clid"></key>
<!--
one-to-many 标签:配置实体类的一对多关联
class 属性:"多"的一方的类的全路径,即联系人实体类的全限定名
-->
<one-to-manyclass="com.siwuxie095.entity.LinkMan"/>
</set>
</class>
</hibernate-mapping>
LinkMan.hbm.xml:
<?xmlversion="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>
<classname="com.siwuxie095.entity.LinkMan"table="t_linkman">
<idname="lid"column="lid">
<generatorclass="native"></generator>
</id>
<propertyname="lkmName"column="lkm_name"></property>
<propertyname="lkmGender"column="lkm_gender"></property>
<propertyname="lkmPhone"column="lkm_phone"></property>
<!-- 配置关联关系的映射(配置关联对象) -->
<!--
many-to-one 标签:配置实体类的多对一关联;
name 属性:"一"的一方的对象的名称(在联系人实体类中声明);
class 属性:"一"的一方的类的全路径,即客户实体类的全限定名;
column 属性:表中的外键名称
-->
<many-to-onename="customer"class="com.siwuxie095.entity.Customer"column="clid"></many-to-one>
</class>
</hibernate-mapping>
hibernate.cfg.xml:
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 第一部分:配置数据库信息(必须) -->
<propertyname="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 或使用 jdbc:mysql:///hibernate_db 代替,省略 localhost:3306 -->
<propertyname="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_db</property>
<propertyname="hibernate.connection.username">root</property>
<propertyname="hibernate.connection.password">8888</property>
<!-- 第二部分:配置 Hibernate 信息(可选) -->
<!-- 输出底层 sql 语句 -->
<propertyname="hibernate.show_sql">true</property>
<!-- 输出底层 sql 语句格式 -->
<propertyname="hibernate.format_sql">true</property>
<!--
Hibernate 帮助创建表,不是自动创建,而需要配置之后。
update:如果已经有表,就更新,如果没有,就自动创建
-->
<propertyname="hibernate.hbm2ddl.auto">update</property>
<!--
配置数据库方言,让 Hibernate 框架识别不同数据库自己特有的语句
如:在 MySQL 中实现分页的关键字 limit,只能在 MySQL 中使用,而
在 Oracle 中实现分页的关键字则是 rownum
-->
<propertyname="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 配置 Session 绑定本地线程 -->
<propertyname="hibernate.current_session_context_class">thread</property>
<!-- 第三部分:引入映射配置文件,把映射配置文件放到核心配置文件(必须) -->
<mappingresource="com/siwuxie095/entity/Customer.hbm.xml"/>
<mappingresource="com/siwuxie095/entity/LinkMan.hbm.xml"/>
</session-factory>
</hibernate-configuration>
【made by siwuxie095】
