非常对不起,老蝴蝶由于生活和工作上的事情太忙,无法及时更新,造成了将近三个月的停更。以后,会更加努力的。为了IT,加油。
上一章简单介绍了Hibernate的一对一映射(八),如果没有看过,请观看上一章
一.Hibernate的一对多的使用范围
在实际的生活和工作中,Hibernate的一对多的使用是最广的。如班级和学生,部门和员工,用户和购买商品,订单和商品等。这里用部门和员工进行举例说明。 运用部门的例子,主要是为下面的自关联做铺垫。一个部门可以有多个员工,但一个员工只能拥有一个部门。
二. 搭建Hibernate环境
跟一对一关联一样,这里就不进行说明了。具体可以参照以前写的博客。
三. 编写具体的实体类
三.一 编写Dept 部门类
package com.yjl.pojo;
/**
@author: yuejl
@date: 2019年2月16日 上午10:15:08
@Description 数据库中一的一方 部门实体
*/
public class Dept {
/**
* @param id 部门的编号
* @param name 部门的名称
* @param description 部门的描述
*/
private Integer id;
private String name;
private String description;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Dept [id=" + id + ", name=" + name + ", description=" + description + "]";
}
}
三.二 编写User 员工类
package com.yjl.pojo;
/**
@author:两个蝴蝶飞
@date: 2019年2月16日 上午10:20:08
@Description 多的一方,用户实体
*/
public class User {
/**
* @param id 标识符Id
* @param name 用户的名称
* @param sex 用户的性别
* @param age 用户的年龄
* @param description 描述
*/
private Integer id;
private String name;
private String sex;
private Integer age;
private String description;
/*通过组合的方面,将部门引入到用户实体中*/
/**
* @param dept 用户所在的那个部门
*/
private Dept dept;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", description=" + description+ "]";
}
}
四 编写具体的Xxx.hbm.xml文件
四.一 Dept.hbm.xml与以前的一样
<hibernate-mapping package="com.yjl.pojo" >
<!-- 具体的实体类 由于前面指定了package包名。这里只需要写Dept即可。否则写com.yjl.pojo.Dept 全限定名称-->
<class name="Dept" table="dept">
<!-- 主键 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!-- 其余属性 这里type运用的是java类型,并不是Hibernate和数据库的-->
<property name="name" length="20" type="java.lang.String"></property>
<property name="description" length="100" type="java.lang.String"></property>
</class>
</hibernate-mapping>
四.二 User.hbm.xml 多添加一个标签
普通的属性,如id,name,sex,age,description与以前的方式一样。 但dept这个字段是不一样的。要用 标签。多对一。
<hibernate-mapping package="com.yjl.pojo" >
<!-- 具体的实体类 -->
<class name="User" table="user">
<!-- 主键 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!-- 其余属性 这里type运用的是java类型,并不是Hibernate和数据库的-->
<property name="name" length="20" type="java.lang.String"></property>
<property name="sex" length="2" type="java.lang.String"></property>
<property name="age" length="3" type="java.lang.Integer"></property>
<property name="description" length="100" type="java.lang.String"></property>
<!-- 开始进行多对一的关联。 n对m,其中n指的是自己的一方。 m指的是要关联的一方。用户对部门,是多对一。
部门对用户,是一对多。实际上Dept与package连用,构成全限定名称 -->
<many-to-one name="dept" column="deptId" class="Dept"></many-to-one>
</class>
</hibernate-mapping>
五.修改hibernate.cfg.xml的引用约束文件
<!-- 引入相应的约束文件 ctrl点击时可以正确进入-->
<mapping resource="com/yjl/pojo/Dept.hbm.xml"/>
<mapping resource="com/yjl/pojo/User.hbm.xml"/>
六. 编写测试文件
六.一 编写测试保存方法
/*单向测试保存*/
@Test
public void singleTest(){
/*1。通过工具类构建Session*/
Session session=HibernateUtil.getSession();
/*2.实例化Dept类*/
Dept dept=new Dept();
dept.setName("开发部");
dept.setDescription("一切为了开发");
/*3. 实例化User类,并设置与部门的关系*/
User user=new User();
user.setName("两个蝴蝶飞");
user.setSex("男");
user.setAge(24);
user.setDescription("一个充满希望的程序员");
/*设置与部门的关系*/
user.setDept(dept);
/*4.进行保存*/
session.save(dept);
session.save(user);
}
控制台依次输出了五个内容:
- 创建表dept
- 创建表user, 但没有外键引用
- 修改表user,引入dept的id字段做为外键
- 插入数据表dept表
- 插入数据表user表
六.二 编写测试查询方法
/*单向的查询测试*/
@Test
public void singleSearchTest(){
/*1。通过工具类构建Session*/
Session session=HibernateUtil.getSession();
/*2.查询 部门的search*/
Dept dept=session.get(Dept.class,1);
System.out.println("部门自己查询的:"+dept);
/*3.查询员工的*/
User user=session.get(User.class,1);
System.out.println("员工自己查询的:"+user);
/*4.通过员工找到他所在的部门的信息*/
Dept dept2=user.getDept();
System.out.println("通过员工找到他所在的部门:"+dept2);
}
六.三 扩充
1.能不能通过员工直接找到输出他所在的部门。即 将User类中的toString()方法改成:
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", description=" + description
+ ", dept=" + dept + "]";
}
/*2.查询员工的*/
User user=session.load(User.class,1);
System.out.println("员工自己查询的:"+user);
这个时候再运行的话:
也将部门信息查询了出来。
顺序是:
- 先修改user表中的外键
- 根据传递进去的参数员工编号,从user表中,将员工的信息查询出来。包括部门的编号。
- 根据部门的编号,从dept表中,将部门的信息查询出来。
- 将保存部门和员工的顺序进行改变,即先保存员工,后保存部门.
- 这个时候,员工和部门均可以正常的保存。但是,员工是没有部门的。因为是先插入的员工,此时部门还没有插入。所以,应该先保存部门,然后根据部门的标识符插入员工表。
七. 单向的一对多的缺点
其实,实际开发中用单向的多一点。 但还是说一下吧。 现在,是只能根据员工去找部门,并不能解决查询部门下有多少个员工的问题。开发中用表连接去查。 如果部门中有一个保存员工的集合就好了,那么直接遍历这个集合就可以了。 这就是多向关联。
八.一对多的双向关联
不但要在User中表存储关于Dept的字段。还要在Dept中存储关于User的字段。 只不过,这个时候是集合。 一般用List,不用Set.
八.一 对Dept实体的改变
- 在Dept表中添加一个集合字段,实现setter和getter方法。 需要进行初始化。 添加的是Set集合,并不是List集合。切记
/**
* @param users 存储员工集合
*/
private Set<User> users=new HashSet<User>();
- 在Dept.hbm.xml中添加set的标签,在description 后面添加即可.
<!-- 添加一对多的关联 采用的是set标签,不是list. 这里面才有one-to-many. class标签里面没有-->
<set name="users">
<!-- 这个deptId 要与User.hbm.xml中的dept属性中的column保持一致 -->
<key column="deptId"></key>
<one-to-many class="User"/>
</set>
不用对User.hbm.xml中进行改变。
九. 编写测试的方法
九.一 双向保存测试的方法
/*双向测试保存*/
/*双向测试保存*/
@Test
public void bothTest(){
/*1。通过工具类构建Session*/
Session session=HibernateUtil.getSession();
Dept dept=new Dept();
dept.setName("开发部");
dept.setDescription("一切为了开发");
/*3. 实例化User类,并设置与部门的关系*/
User user=new User();
user.setName("两个蝴蝶飞");
user.setSex("男");
user.setAge(24);
user.setDescription("一个有梦想的程序员");
User user1=new User();
user1.setName("两个蝴蝶飞1");
user1.setSex("男");
user1.setAge(24);
user1.setDescription("一个有梦想的程序员");
User user2=new User();
user2.setName("两个蝴蝶飞2");
user2.setSex("男");
user2.setAge(24);
user2.setDescription("一个有梦想的程序员");
/*设置与部门的关系*/
user.setDept(dept);
user1.setDept(dept);
user2.setDept(dept);
session.save(dept);
/*级联保存后,就不需要多次保存了。但是要用dept的方法,进行设置关系*/
session.save(user);
session.save(user1);
session.save(user2);
}
步骤与单向的一对多的步骤一样。
- 创建表dept
- 创建表user, 但没有外键引用
- 修改表user,引入dept的id字段做为外键
- 插入数据表dept表
- 插入数据表user表
九.二 双向查询的方法
/*双向的查询测试*/
@Test
public void bothSearchTest(){
/*1。通过工具类构建Session*/
Session session=HibernateUtil.getSession();
/*2.查询 部门的search*/
Dept dept=session.get(Dept.class,1);
System.out.println("部门自己查询的:"+dept);
/*3.查询员工的*/
User user=session.get(User.class,1);
System.out.println("员工自己查询的:"+user);
/*4.通过部门找到他的员工*/
Set<User> userList=dept.getUsers();
System.out.println("通过部门找到他所有的员工:");
for (User user2 : userList) {
System.out.println(user2);
}
/*5.通过员工找到他所在的部门的信息*/
Dept dept2=user.getDept();
System.out.println("通过员工找到他所在的部门:"+dept2);
}
这个时候,查询员工时,员工所在的部门也是可以查询出来的。
九.三 扩充
- 上面查询的方法, 如果按钮dept2继续查询呢? 即:
/*5.通过员工找到他所在的部门的信息*/
Dept dept2=user.getDept();
System.out.println("通过员工找到他所在的部门:"+dept2);
Set<User> userList2=dept2.getUsers();
System.out.println("再次通过部门找到他所有的员工:");
for (User user2 : userList2) {
System.out.println(user2);
}
也是可以正常查询的。
2. 如果 部门的toString() 方法中添加输出users呢? 注意:上面的Dept类toString()方法中没有users的输出。另外,一定要注意,User类中toString() 方法中有dept的输出。 此时,输出dept时—>users中输出user---->找到dept2---->输出此时dept2中的users, 这是一个环,即递归调用的死循环。
运行的结果是:
栈溢出了。是Errror,不是Exception。 注意,调用的时候,千万不要形成一个环。
3. 按照2的思路继续思考,此时将User中toString()去除掉dept的输出,保留dept中users的输出呢。
此刻显示的是正常的。 用的时候,一定要注意这一点。4.一对多中set标签,如果column不一样呢,会造成什么呢?
以前是deptId,现在改成deptId1, 即Dept中的column与User中的column关于外键的列名不一样。
运行的是bothTest() 方法后,没有报错。但,这是什么啊? 故,切记: Hibernate运行生成表之后,一定要看一下,生成的表是不是自己想要的。并不是生成表,无报错,就是OK了。
谢谢!!!