您的位置:首页 > 其它

Hibernate中多对多关系映射案例详解

2018-01-14 13:50 363 查看

前言

在项目开发中,在数据库中的设计中,一定会遇到多对多的关系。比如:老师与学生的关系,一个老师会有多个学生,一个学生也会有多个老师,这种关系称为多对对的关系,在之前的jdb中操作数据库,在满足三大范式的情况下,必须要保存三张表,而插入数据库的原生sql语句需要写很多条,Hibernate为我们封装了原生的sql语句,提供了对应的多对多映射关系的相关配置,我们只要会用它来进行实际的开发即可,大大提高了我们的开发效率。本文将根据具体的案例来学习Hibernate多对多映射配置,以及简单的保存查询操作。

案例

需求:项目与开发人员的关系,即一个项目对应多个开发人员,一个开发人员对应多个项目。

本次案例模拟了几份数据如下所示:

电商系统:

曹吉

王春

OA系统:

王春

老张

设计如下图所示:



从数据库设计来说:项目表、员工表、中间关系表。项目表主要有项目id、项目名称。员工表有员工id、员工名称、中间表保存项目id和员工id。这样就保存他们之间对应的多对多关系了。

从javaBean设计来说,除了保存各自的属性外,还要保存对方的信息,通过集合来维护对方的关系。

从映射文件来说,主要是在set中保存《many-to-many》的与另外一方的关系:主要内容如下:

例如在project项目多对多映射关系配置中:

1. 映射的集合属性: 如“developers”

2. 集合属性,对应的中间表: “t_relation”

3. 外键字段: prjId

4. 外键字段,对应的中间表字段: did

5. 集合属性元素的类型

具体实现:

Project.java

public class Project {
private int prj_id;
private String prj_name;
// 项目下的多个员工
private Set<Developer> developers = new HashSet<Developer>();

public int getPrj_id() {
return prj_id;
}
public void setPrj_id(int prjId) {
prj_id = prjId;
}
public String getPrj_name() {
return prj_name;
}
public void setPrj_name(String prjName) {
prj_name = prjName;
}
public Set<Developer> getDevelopers() {
return developers;
}
public void setDevelopers(Set<Developer> developers) {
this.developers = developers;
}

}


Project.hbm.xml:不加cascade和inverse属性

<hibernate-mapping package="com.nwpu.geeker.many2many">

<class name="Project" table="t_project">
<id name="prj_id">
<generator class="native"></generator>
</id>
<property name="prj_name" length="20"></property>
<!--
多对多映射:
1.  映射的集合属性: “developers”
2.  集合属性,对应的中间表: “t_relation”
3. 外键字段:  prjId
4. 外键字段,对应的中间表字段:  did
5.   集合属性元素的类型
-->
<set name="developers" table="t_relation" >
<key column="prjId"></key>
<many-to-many column="did" class="Developer"></many-to-many>
</set>

</class>
</hibernate-mapping>


Developer.java

public class Developer {
private int d_id;
private String d_name;
// 开发人员,参数的多个项目
private Set<Project> projects = new HashSet<Project>();

public int getD_id() {
return d_id;
}
public void setD_id(int dId) {
d_id = dId;
}
public String getD_name() {
return d_name;
}
public void setD_name(String dName) {
d_name = dName;
}
public Set<Project> getProjects() {
return projects;
}
public void setProjects(Set<Project> projects) {
this.projects = projects;
}
}


Developer.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.nwpu.geeker.many2many">

<class name="Developer" table="t_developer">
<id name="d_id">
<generator class="native"></generator>
</id>
f060

<property name="d_name" length="20"></property>

<!--
多对多映射配置: 员工方
name  指定映射的集合属性
table 集合属性对应的中间表
key   指定中间表的外键字段(引用当前表t_developer主键的外键字段)
many-to-many
column 指定外键字段对应的项目字段
class  集合元素的类型
-->
<set name="projects" table="t_relation">
<key column="did"></key>
<many-to-many column="prjId" class="Project"></many-to-many>
</set>

</class>

</hibernate-mapping>


测试-保存 App.java

public class App1_save {

private static SessionFactory sf;
static {
sf = new Configuration()
.configure()
.addClass(Project.class)
.addClass(Developer.class)   // 测试时候使用
.buildSessionFactory();
}

// 1. 多对多,保存  【只能通过一方维护另外一方,不能重复维护!】
@Test
public void save() {
Session session = sf.openSession();
session.beginTransaction();

/*
* 模拟数据:
电商系统(曹吉,王春)
OA系统(王春,老张)
*/
// 创建项目对象
Project prj_ds = new Project();
prj_ds.setPrj_name("电商系统");
Project prj_oa = new Project();
prj_oa.setPrj_name("OA系统");

// 创建员工对象
Developer dev_cj = new Developer();
dev_cj.setD_name("曹吉");
Developer dev_wc = new Developer();
dev_wc.setD_name("王春");
Developer dev_lz = new Developer();
dev_lz.setD_name("老张");
// 关系 【项目方】
prj_ds.getDevelopers().add(dev_cj);
prj_ds.getDevelopers().add(dev_wc); // 电商系统(曹吉,王春)
prj_oa.getDevelopers().add(dev_wc);
prj_oa.getDevelopers().add(dev_lz); // OA系统(王春,老张)

// 保存
session.save(dev_cj);
session.save(dev_wc);
session.save(dev_lz);

session.save(prj_ds);
session.save(prj_oa);   // 如果不需要上面三行代码,必须要设置cascade级联保存

session.getTransaction().commit();
session.close();
}
}


经过测试,一共执行了9条数据库语句,并成功保存了项目与开发人员的数据。

附加:inverse属性

设置inverse属性,在多对多种维护关联关系的影响?

1) 保存数据

有影响。

inverse=false ,有控制权,可以维护关联关系; 保存数据的时候会把对象关系插入中间表;

inverse=true, 没有控制权, 不会往中间表插入数据。

2) 获取数据

无。

3) 解除关系

// 有影响。

// inverse=false ,有控制权, 解除关系就是删除中间表的数据。

// inverse=true, 没有控制权,不能解除关系。

4) 删除数据

有影响。

// inverse=false, 有控制权。 先删除中间表数据,再删除自身。

// inverse=true, 没有控制权。 如果删除的数据有被引用,会报错! 否则,才可以删除

测试代码:App2.java

public class App2_inverse {

private static SessionFactory sf;
static {
sf = new Configuration()
.configure()
.addClass(Project.class)
.addClass(Developer.class)   // 测试时候使用
.buildSessionFactory();
}

// 多对多
//1. 设置inverse属性,对保存数据影响?
// 有影响。
// inverse=false ,有控制权,可以维护关联关系; 保存数据的时候会把对象关系插入中间表;
// inverse=true,  没有控制权, 不会往中间表插入数据。
@Test
public void save() {
Session session = sf.openSession();
session.beginTransaction();

/*
* 模拟数据:
电商系统(曹吉,王春)
OA系统(王春,老张)
*/
// 创建项目对象
Project prj_ds = new Project();
prj_ds.setPrj_name("电商系统");
Project prj_oa = new Project();
prj_oa.setPrj_name("OA系统");

// 创建员工对象
Developer dev_cj = new Developer();
dev_cj.setD_name("曹吉");
Developer dev_wc = new Developer();
dev_wc.setD_name("王春");
Developer dev_lz = new Developer();
dev_lz.setD_name("老张");
// 关系 【项目方】
prj_ds.getDevelopers().add(dev_cj);
prj_ds.getDevelopers().add(dev_wc); // 电商系统(曹吉,王春)
prj_oa.getDevelopers().add(dev_wc);
prj_oa.getDevelopers().add(dev_lz); // OA系统(王春,老张)

// 保存
//      session.save(dev_cj);
//      session.save(dev_wc);
//      session.save(dev_lz);

session.save(prj_ds);
session.save(prj_oa);   // 必须要设置级联保存

session.getTransaction().commit();
session.close();
}

//2 .设置inverse属性, 对获取数据影响?  无
@Test
public void get() {

Session session = sf.openSession();
session.beginTransaction();

Project prj = (Project) session.get(Project.class, 1);
System.out.println(prj.getPrj_name());
System.out.println(prj.getDevelopers());

session.getTransaction().commit();
session.close();
}

//3. 设置inverse属性, 对解除关系影响?
// 有影响。
// inverse=false ,有控制权, 解除关系就是删除中间表的数据。
// inverse=true, 没有控制权,不能解除关系。
@Test
public void removeRelation() {

Session session = sf.openSession();
session.beginTransaction();

Project prj = (Project) session.get(Project.class, 7);
prj.getDevelopers().clear();

session.getTransaction().commit();
session.close();
}

//3. 设置inverse属性,对删除数据的影响?
// inverse=false, 有控制权。 先删除中间表数据,再删除自身。
// inverse=true, 没有控制权。 如果删除的数据有被引用,会报错! 否则,才可以删除
@Test
public void deleteData() {

Session session = sf.openSession();
session.beginTransaction();

Project prj = (Project) session.get(Project.class, 1);
session.delete(prj);

session.getTransaction().commit();
session.close();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息