首页 > 代码库 > Hibernate一对多操作
Hibernate一对多操作
--------------------siwuxie095
Hibernate 一对多操作
以客户和联系人为例,客户是一,联系人是多
即 一个客户里面有多个联系人,一个联系人只能属于一个客户
注意:这里的客户是公司级的,即 公司,联系人 即 公司里的员工
(一)一对多映射配置
第一步:创建两个实体类,客户和联系人
第二步:让两个实体类之间互相表示
(1)在客户实体类中表示多个联系人
(2)在联系人实体类中表示所属客户
第三步:配置映射关系
「一般一个实体类对应一个映射配置文件」
(1)配置基本的映射
(2)配置关联关系的映射(一对多关系)
1)在客户的映射配置文件中,表示所有联系人
2)在联系人的映射配置文件中,表示所属客户
第四步:在核心配置文件中引入映射配置文件
(二)一对多级联保存
如:添加客户,为这个客户添加一个联系人
(1)复杂写法
/** * 一对多级联保存的复杂写法 * * * 手动加上 @Test 以进行单元测试(将自动导入 JUnit 4 的 jar 包) * * 选中方法名,右键->Run As->JUint Test */ @Test public void 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 public void 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 public void 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 public void 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; } public void setLinkManSet(Set<LinkMan> linkManSet) { this.linkManSet = linkManSet; }
public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; }
public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; }
public String getCustLevel() { return custLevel; } public void setCustLevel(String custLevel) { this.custLevel = custLevel; }
public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; }
public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; }
public String getCustMobile() { return custMobile; } public void 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; } public void setCustomer(Customer customer) { this.customer = customer; }
public Integer getLid() { return lid; } public void setLid(Integer lid) { this.lid = lid; }
public String getLkmName() { return lkmName; } public void setLkmName(String lkmName) { this.lkmName = lkmName; }
public String getLkmGender() { return lkmGender; } public void setLkmGender(String lkmGender) { this.lkmGender = lkmGender; }
public String getLkmPhone() { return lkmPhone; } public void 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 public void 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 public void 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 public void 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 public void 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:
<?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 name="com.siwuxie095.entity.Customer" table="t_customer">
<!-- (2) id 标签:配置实体类 id 和表 id 对应(主键); name 属性:实体类里 id 属性名称; column 属性:生成表中 id 字段名称 --> <id name="cid" column="cid"> <!-- native:设置主键 id 自动增长 --> <generator class="native"></generator> </id>
<!-- (3) property 标签:配置其它属性和表中字段对应; name 属性:实体类属性名称; column 属性:生成表中字段名称 --> <property name="custName" column="cust_name"></property> <property name="custLevel" column="cust_level"></property> <property name="custSource" column="cust_source"></property> <property name="custPhone" column="cust_phone"></property> <property name="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) --> <set name="linkManSet" cascade="save-update,delete" inverse="true">
<!-- 一对多建表,有外键。Hibernate 的机制 是双向维护外键(即 都配置外键)
key 标签:配置"多"的一方的外键 column 属性:"多"的一方的外键名称 --> <key column="clid"></key>
<!-- one-to-many 标签:配置实体类的一对多关联 class 属性:"多"的一方的类的全路径,即 联系人实体类的全限定名 --> <one-to-many class="com.siwuxie095.entity.LinkMan"/> </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 name="com.siwuxie095.entity.LinkMan" table="t_linkman">
<id name="lid" column="lid"> <generator class="native"></generator> </id>
<property name="lkmName" column="lkm_name"></property> <property name="lkmGender" column="lkm_gender"></property> <property name="lkmPhone" column="lkm_phone"></property>
<!-- 配置关联关系的映射(配置关联对象) -->
<!-- many-to-one 标签:配置实体类的多对一关联; name 属性:"一"的一方的对象的名称(在联系人实体类中声明); class 属性:"一"的一方的类的全路径,即 客户实体类的全限定名; column 属性:表中的外键名称 --> <many-to-one name="customer" class="com.siwuxie095.entity.Customer" column="clid"></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 name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <!-- 或使用 jdbc:mysql:///hibernate_db 代替,省略 localhost:3306 --> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_db</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">8888</property>
<!-- 第二部分:配置 Hibernate 信息(可选) -->
<!-- 输出底层 sql 语句 --> <property name="hibernate.show_sql">true</property> <!-- 输出底层 sql 语句格式 --> <property name="hibernate.format_sql">true</property> <!-- Hibernate 帮助创建表,不是自动创建,而需要配置之后。 update:如果已经有表,就更新,如果没有,就自动创建 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 配置数据库方言,让 Hibernate 框架识别不同数据库自己特有的语句 如:在 MySQL 中实现分页的关键字 limit,只能在 MySQL 中使用,而 在 Oracle 中实现分页的关键字则是 rownum --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 配置 Session 绑定本地线程 --> <property name="hibernate.current_session_context_class">thread</property>
<!-- 第三部分:引入映射配置文件,把映射配置文件放到核心配置文件(必须) -->
<mapping resource="com/siwuxie095/entity/Customer.hbm.xml"/> <mapping resource="com/siwuxie095/entity/LinkMan.hbm.xml"/>
</session-factory> </hibernate-configuration> |
【made by siwuxie095】
Hibernate一对多操作