一.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实体的改变
1.在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了。
谢谢!!!