--------------------siwuxie095
Hibernate 一对多操作
以客户和联系人为例,客户是一,联系人是多
即
一个客户里面有多个联系人,一个联系人只能属于一个客户
注意:这里的客户是公司级的,即
公司,联系人
即
公司里的员工
(一)一对多映射配置
第一步:创建两个实体类,客户和联系人
第二步:让两个实体类之间互相表示
(1)在客户实体类中表示多个联系人
(2)在联系人实体类中表示所属客户
第三步:配置映射关系
「一般一个实体类对应一个映射配置文件」
(1)配置基本的映射
(2)配置关联关系的映射(一对多关系)
1)在客户的映射配置文件中,表示所有联系人
2)在联系人的映射配置文件中,表示所属客户
第四步:在核心配置文件中引入映射配置文件
(二)一对多级联保存
如:添加客户,为这个客户添加一个联系人
(1)复杂写法
/** * 一对多级联保存的复杂写法 * * * 手动加上 @Test 以进行单元测试(将自动导入 JUnit 4 的 jar 包) * * 选中方法名,右键->Run As->JUint Test */ @Test public 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,再进行具体实现
/** * 一对多级联保存的简化写法 * * 在客户的映射配置文件中的 * 添加 cascade 属性,并将其值设置为 * save-update */ @Test public 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 public 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 public 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 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; } public this.linkManSet = linkManSet; } public Integer getCid() { return cid; } public this.cid = cid; } public String getCustName() { return custName; } public this.custName = custName; } public String getCustLevel() { return custLevel; } public this.custLevel = custLevel; } public String getCustSource() { return custSource; } public this.custSource = custSource; } public String getCustPhone() { return custPhone; } public this.custPhone = custPhone; } public String getCustMobile() { return custMobile; } public 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; } public this.customer = customer; } public Integer getLid() { return lid; } public this.lid = lid; } public String getLkmName() { return lkmName; } public this.lkmName = lkmName; } public String getLkmGender() { return lkmGender; } public this.lkmGender = lkmGender; } public String getLkmPhone() { return lkmPhone; } public 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 public 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 public 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 public 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 public 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:
<?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> <!-- (1) class 标签:配置实体类和数据库表的对应; name 属性:实体类的全路径,即 table 属性:数据库表的名称(数据库表由 Hibernate 自动生成) --> <class <!-- (2) id 标签:配置实体类 id 和表 id 对应(主键); name 属性:实体类里 id 属性名称; column 属性:生成表中 id 字段名称 --> <id <!-- native:设置主键 id 自动增长 --> <generator </id> <!-- (3) property 标签:配置其它属性和表中字段对应; name 属性:实体类属性名称; column 属性:生成表中字段名称 --> <property <property <property <property <property <!-- (4) set 标签:配置关联关系的映射(配置关联对象),代表一个 Set 集合; name 属性:"多"的一方的对象的 Set 集合的名称(在客户实体类中声明); cascade 属性:save-update 表示级联保存,delete 表示级联删除(逗号隔开); inverse 属性:true 表示放弃关系维护(放弃外键的维护权)(默认为 false) 注意:inverse="true" 主要用于修改操作,防止产生多余的 sql 语句,但如果 同时配置了 cascade="save-update" 和 inverse="true",将会导致在级联保存 操作后,没有外键(为 null) --> <set <!-- 一对多建表,有外键。Hibernate 的机制 是双向维护外键(即 key 标签:配置"多"的一方的外键 column 属性:"多"的一方的外键名称 --> <key <!-- one-to-many 标签:配置实体类的一对多关联 class 属性:"多"的一方的类的全路径,即 --> <one-to-many </set> </class> </hibernate-mapping> |
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> <class <id <generator </id> <property <property <property <!-- 配置关联关系的映射(配置关联对象) --> <!-- many-to-one 标签:配置实体类的多对一关联; name 属性:"一"的一方的对象的名称(在联系人实体类中声明); class 属性:"一"的一方的类的全路径,即 column 属性:表中的外键名称 --> <many-to-one </class> </hibernate-mapping> |
hibernate.cfg.xml:
<?xml version="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> <!-- 第一部分:配置数据库信息(必须) --> <property <!-- 或使用 jdbc:mysql:///hibernate_db 代替,省略 localhost:3306 --> <property <property <property <!-- 第二部分:配置 Hibernate 信息(可选) --> <!-- 输出底层 sql 语句 --> <property <!-- 输出底层 sql 语句格式 --> <property <!-- Hibernate 帮助创建表,不是自动创建,而需要配置之后。 update:如果已经有表,就更新,如果没有,就自动创建 --> <property <!-- 配置数据库方言,让 Hibernate 框架识别不同数据库自己特有的语句 如:在 MySQL 中实现分页的关键字 limit,只能在 MySQL 中使用,而 在 Oracle 中实现分页的关键字则是 rownum --> <property <!-- 配置 Session 绑定本地线程 --> <property <!-- 第三部分:引入映射配置文件,把映射配置文件放到核心配置文件(必须) --> <mapping <mapping </session-factory> </hibernate-configuration> |
【made by siwuxie095】