7.8.1 多表关系

我们在学习数据库的时候,了解到数据库中多表之前是存在关系的,而这种关系也是固定的,分为:一对多,多对一,一对一和多对多。那么学习完JDBC,JSP,Servlet后。如何在WEB项目中操作数据库呢?这就是我们今天研究的重点:如何使用JAVA代码实现多表关系操作。

多表操作之一对多

数据表

比较经典的一对多的关系就是学生表与年级表,两张表中,学生是多方,年级是一方。因为:一个年级可以有多名学生,但反过来一名学生只属于一个年级。先创建数据表

  1. create table student (
  2. stuid int primary key,
  3. stuname varchar(5),
  4. stuage int,
  5. gid int
  6. );
  7. create table grade(
  8. gradeid int primary key ,
  9. gname varchar(5)
  10. );
  11. insert into grade values(1,'一年级');
  12. insert into grade values(2,'二年级');
  13. insert into grade values(3,'三年级');
  14. insert into student values(1,'张三',18,1);
  15. insert into student values(2,'李四',14,2);
  16. insert into student values(3,'富贵',13,3);
  17. insert into student values(4,'王芳',17,1);
  18. insert into student values(5,'甜甜',15,2);

创建实体类

  1. package bean;
  2. public class Student {
  3. private int stuId;
  4. private String stuName;
  5. private int stuAge;
  6. private int gid;
  7. public Grade getGrade() {
  8. return grade;
  9. }
  10. public void setGrade(Grade grade) {
  11. this.grade = grade;
  12. }
  13. private Grade grade;
  14. @Override
  15. public String toString() {
  16. return "Student{" +
  17. "stuId=" + stuId +
  18. ", stuName='" + stuName + '\'' +
  19. ", stuAge=" + stuAge +
  20. ", gid=" + gid +
  21. '}';
  22. }
  23. public int getStuId() {
  24. return stuId;
  25. }
  26. public void setStuId(int stuId) {
  27. this.stuId = stuId;
  28. }
  29. public String getStuName() {
  30. return stuName;
  31. }
  32. public void setStuName(String stuName) {
  33. this.stuName = stuName;
  34. }
  35. public int getStuAge() {
  36. return stuAge;
  37. }
  38. public void setStuAge(int stuAge) {
  39. this.stuAge = stuAge;
  40. }
  41. public int getGid() {
  42. return gid;
  43. }
  44. public void setGid(int gid) {
  45. this.gid = gid;
  46. }
  47. }

Grade:

  1. package bean;
  2. import java.util.List;
  3. public class Grade {
  4. private int gradeId;
  5. private String gname;
  6. private List<Student> studentList; //数据库中使用外键列保证两表关系,实体类中使用属性保证两表关系
  7. @Override
  8. public String toString() {
  9. return "Grade{" +
  10. "gradeId=" + gradeId +
  11. ", gname='" + gname + '\'' +
  12. ", studentList=" + studentList +
  13. '}';
  14. }
  15. public int getGradeId() {
  16. return gradeId;
  17. }
  18. public void setGradeId(int gradeId) {
  19. this.gradeId = gradeId;
  20. }
  21. public String getGname() {
  22. return gname;
  23. }
  24. public void setGname(String gname) {
  25. this.gname = gname;
  26. }
  27. public List<Student> getStudentList() {
  28. return studentList;
  29. }
  30. public void setStudentList(List<Student> studentList) {
  31. this.studentList = studentList;
  32. }
  33. }

建立两表之间的属性关系

数据表是通过外键列来维系两表关系。实体类是通过属性来维系两表关系。在建立一对多关系时,我们分析到年级是一方,学生是多方。一对多,是以一方为主,所以我们在一方添加多方的一个属性。那这个属性是对象还是集合呢?这里记住一句话:一方存多方的集合,多方存一方的对象。所以需要在年级表中添加下列属性:
Grade新增代码:

  1. private List<Student> studentList;
  2. public List<Student> getStudentList() {
  3. return studentList;
  4. }
  5. public void setStudentList(List<Student> studentList) {
  6. this.studentList = studentList;
  7. }

创建Dao层接口代码和实现类,操作数据库

Dao层

  1. package dao;
  2. import bean.Grade;
  3. import bean.Student;
  4. import java.util.List;
  5. public interface GradeDao {
  6. //查询某个年级信息(要求同时查询出学生的信息)
  7. public Grade findById(int gid);
  8. //查询学生的信息(包含年级信息)
  9. public List<Student> findAll();
  10. }

实现类:在实现类中需要连接数据库,并且查询结果来自于多张表。此时如何存储数据呢?给大家一个思路:1.在不考虑两表的情况下,先存储各自表中的数据 2.结合上面步骤中添加属性的问题,考虑应该把哪个类添加到另外一个类的属性中。代码如下:

  1. package dao.impl;
  2. import bean.Grade;
  3. import bean.Student;
  4. import dao.GradeDao;
  5. import util.DruidUtil;
  6. import java.sql.Connection;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. public class GradeDaoImpl extends DruidUtil implements GradeDao {
  13. @Override
  14. public Grade findById(int gid) {
  15. Grade grade = new Grade();
  16. ArrayList<Student> students = new ArrayList<>();
  17. Connection connection = null;
  18. PreparedStatement preparedStatement = null;
  19. ResultSet resultSet = null;
  20. try {
  21. connection = getConnection();
  22. preparedStatement = connection.prepareStatement("select * from student s,grade g where s.gid=g.gradeid and g.gradeid=?");
  23. preparedStatement.setInt(1,gid);
  24. resultSet = preparedStatement.executeQuery();
  25. while (resultSet.next()){
  26. //1.将各自的数据信息进行存储
  27. grade.setGradeId(resultSet.getInt("gradeid"));
  28. grade.setGname(resultSet.getString("gname"));
  29. Student student = new Student();
  30. student.setStuName(resultSet.getString("stuname"));
  31. student.setStuAge(resultSet.getInt("stuage"));
  32. //2.将学生信息和年级中的属性进行关联
  33. //将学生放到一个集合中
  34. students.add(student);
  35. }
  36. //3.建立两者关系
  37. grade.setStudentList(students);
  38. } catch (SQLException throwables) {
  39. throwables.printStackTrace();
  40. } finally {
  41. close(connection,preparedStatement,resultSet);
  42. }
  43. return grade;
  44. }
  45. }

说明:这里比较难理解的是关于对象的创建以及属性赋值

测试类

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. GradeDao gradeDao = new GradeDaoImpl();
  4. Grade grade = gradeDao.getGradeById(1);
  5. System.out.println(grade.getGname());
  6. List<Student> studentList = grade.getStudentList();
  7. for (Student student : studentList) {
  8. System.out.println(student);
  9. }
  10. }
  11. }

多表操作之多对一

在上一步的基础上,完成多对一。学生是多方,秉持着“一方存多方的集合,多方存一方的对象”,那么我们就
需要在多的一方,添加一方的一个对象。此时学生类中需要添加下列代码

  1. private Grade grade;
  2. public Grade getGrade() {
  3. return grade;
  4. }
  5. public void setGrade(Grade grade) {
  6. this.grade = grade;
  7. }

在Dao层添加接口方法:

  1. package dao;
  2. import bean.Grade;
  3. import bean.Student;
  4. import java.util.List;
  5. public interface GradeDao {
  6. //查询某个年级信息(要求同时查询出学生的信息)
  7. public Grade findById(int gid);
  8. //查询学生的信息(包含年级信息)
  9. public List<Student> findAll();
  10. }

添加实现类:实现类中主要考虑如何建立两者关联

  1. package dao.impl;
  2. import bean.Grade;
  3. import bean.Student;
  4. import dao.GradeDao;
  5. import util.DruidUtil;
  6. import java.sql.Connection;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. public class GradeDaoImpl extends DruidUtil implements GradeDao {
  13. @Override
  14. public List<Student> findAll() {
  15. ArrayList<Student> students = new ArrayList<>();
  16. Connection connection = null;
  17. PreparedStatement preparedStatement = null;
  18. ResultSet resultSet = null;
  19. try {
  20. connection = getConnection();
  21. preparedStatement = connection.prepareStatement("select * from student s,grade g where s.gid=g.gradeid ");
  22. resultSet = preparedStatement.executeQuery();
  23. while(resultSet.next()){
  24. //1.各自存各自的数据
  25. Grade grade = new Grade();
  26. grade.setGradeId(resultSet.getInt("gradeid"));
  27. grade.setGname(resultSet.getString("gname"));
  28. Student student = new Student();
  29. student.setStuName(resultSet.getString("stuname"));
  30. student.setStuAge(resultSet.getInt("stuage"));
  31. //2.关联信息(将年级放在学生中,再将学生放在集合中)
  32. student.setGrade(grade);
  33. students.add(student);
  34. }
  35. } catch (SQLException throwables) {
  36. throwables.printStackTrace();
  37. } finally {
  38. close(connection,preparedStatement,resultSet);
  39. }
  40. return students;
  41. }
  42. }

多表操作之一对一

一对一在多表关系中存在场景不是很多,现在以妻子和丈夫的关系,模拟一对一的实现过程。

创建数据表

  1. create table wife(
  2. wifeid int PRIMARY key,
  3. wifename varchar(5)
  4. );
  5. create table husband(
  6. husid int PRIMARY KEY,
  7. husname varchar(5),
  8. wid int
  9. );
  10. insert into wife values(1,'黄晓明');
  11. insert into wife values(2,'邓超');
  12. insert into husband values(1,'baby',1);
  13. insert into husband values(2,'孙俪',2);

创建实体类

  1. public class Husband {
  2. private int husId;
  3. private String husName;
  4. private int wid;
  5. //setter and getter
  6. }
  1. public class Wife {
  2. private int wifeId;
  3. private String wifeName;
  4. //setter and getter
  5. }

建立实体类之间的一对一关系,还是依据“一方存多方的集合,多方存一方的对象”的原则,但是现在的问题是双方
都是一方数据,此时记住原则“一方存另一方的对象”。所以代码改成:
妻子一方添加丈夫的对象

  1. public class Wife {
  2. private int wifeId;
  3. private String wifeName;
  4. private Husband husband;
  5. //setter and getter
  6. }

丈夫一方添加妻子的对象

  1. public class Husband {
  2. private int husId;
  3. private String husName;
  4. private int wid;
  5. private Wife wife;
  6. //setter and getter
  7. }

添加Dao和实现类

Dao

  1. public interface WifeDao {
  2. //查询妻子信息(要求包含丈夫信息)
  3. public Wife getByWifeId(int wifeId);
  4. //查询丈夫信息(要求包含妻子信息)
  5. public Husband getByHusId(int husId);
  6. }

实现类:

  1. package dao.impl;
  2. import bean.Husband;
  3. import bean.Wife;
  4. import dao.WifeDao;
  5. import util.DruidUtil;
  6. import java.sql.Connection;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. public class WifeDaoImpl extends DruidUtil implements WifeDao {
  11. @Override
  12. public Wife findByWifeId(int wid) {
  13. Wife wife = new Wife();
  14. Connection connection = null;
  15. PreparedStatement preparedStatement = null;
  16. ResultSet resultSet = null;
  17. try {
  18. connection = getConnection();
  19. preparedStatement = connection.prepareStatement("select * from wife w,husband h where w.wifeid=h.wid and w.wifeid=?");
  20. preparedStatement.setInt(1,wid);
  21. resultSet = preparedStatement.executeQuery();
  22. while(resultSet.next()){
  23. //1.存各自的信息
  24. wife.setWifeName(resultSet.getString("wifename"));
  25. Husband husband = new Husband();
  26. husband.setHusName(resultSet.getString("husname"));
  27. //2.建立两者关系(将丈夫封装到妻子的对象中)
  28. wife.setHusband(husband);
  29. }
  30. } catch (SQLException throwables) {
  31. throwables.printStackTrace();
  32. } finally {
  33. close(connection,preparedStatement,resultSet);
  34. }
  35. return wife;
  36. }
  37. @Override
  38. public Husband findByHid(int hid) {
  39. Husband husband = new Husband();
  40. Connection connection = null;
  41. PreparedStatement preparedStatement = null;
  42. ResultSet resultSet = null;
  43. try {
  44. connection = getConnection();
  45. preparedStatement = connection.prepareStatement("select * from wife w,husband h where w.wifeid=h.wid and h.husid=?");
  46. preparedStatement.setInt(1,hid);
  47. resultSet = preparedStatement.executeQuery();
  48. while(resultSet.next()){
  49. //1.存各自的信息
  50. Wife wife = new Wife();
  51. wife.setWifeName(resultSet.getString("wifename"));
  52. husband.setHusName(resultSet.getString("husname"));
  53. //2.建立两者关系(将妻子封装到丈夫的对象中)
  54. husband.setWife(wife);
  55. }
  56. } catch (SQLException throwables) {
  57. throwables.printStackTrace();
  58. } finally {
  59. close(connection,preparedStatement,resultSet);
  60. }
  61. return husband;
  62. }
  63. }

测试类

  1. public static void main(String[] args) {
  2. WifeDao wifeDao = new WifeDaoImpl();
  3. Husband husband = wifeDao.getByHusId(1);
  4. System.out.println(husband.getHusName()+","+husband.getWife().getWifeName());
  5. Wife wife = wifeDao.getByWifeId(2);
  6. System.out.println(wife.getWifeName()+","+wife.getHusband().getHusName());
  7. }

多表操作之多对多

多对多在现实场景中也是不很多,比较特殊的就是权限列表的三表关系。菜单表和角色表之间属于多对多。某个功能菜单可以分配给多个角色,某个角色也可以拥有多个菜单,在这个分配过程中就是典型的多对多。在多对多中,表的创建也比较有特点,必须是基于三张表来实现。

创建数据表

  1. create table menu(
  2. menuid int primary key,
  3. menuname varchar(10)
  4. );
  5. create table role(
  6. roleid int primary key,
  7. rolename varchar(10)
  8. );
  9. create table middle(
  10. middleid int primary key,
  11. mid int,
  12. rid int
  13. );
  14. insert into menu values(1,'用户管理');
  15. insert into menu values(2,'菜单管理');
  16. insert into menu values(3,'角色管理');
  17. insert into role values(1,'超级管理员');
  18. insert into role values(2,'管理员');
  19. insert into role values(3,'总经理');
  20. insert into middle values(1,1,1);
  21. insert into middle values(2,2,1);
  22. insert into middle values(3,3,1);
  23. insert into middle values(4,1,2);
  24. insert into middle values(5,2,2);
  25. insert into middle values(6,1,3);

定义实体类:中间表不需要生成实体类

Menu:

  1. public class Menu {
  2. private int menuId;
  3. private String menuName;
  4. //getter and setter
  5. }

Role:

  1. public class Role {
  2. private int roleId;
  3. private String roleName;
  4. //getter and setter
  5. }

建立实体类之间的多对多关系,还是依据“一方存多方的集合,多方存一方的对象”的原则,但是现在的问题是双方都是多方数据,此时记住原则“多方存另一方的集合”。代码如下:
Menu:

  1. public class Menu {
  2. private int menuId;
  3. private String menuName;
  4. private List<Role> roleList;
  5. //getter and setter
  6. }

Role:

  1. public class Role {
  2. private int roleId;
  3. private String roleName;
  4. private List<Menu> menuList;
  5. //getter and setter
  6. }

定义接口和实现类

  1. public interface RoleDao {
  2. //查询某个角色信息(要求包含角色对应的菜单列表)
  3. public Role findByRoleId(int roleId);
  4. //查询某个菜单信息(要求包含菜单对应的角色列表)
  5. public Menu findByMenuId(int menuId);
  6. }
  1. package dao.impl;
  2. import bean.Menu;
  3. import bean.Role;
  4. import dao.RoleDao;
  5. import util.DruidUtil;
  6. import java.sql.Connection;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. import java.util.ArrayList;
  11. public class RoleDaoImpl extends DruidUtil implements RoleDao {
  12. @Override
  13. public Menu findByMenuId(int mid) {
  14. Menu menu = new Menu();
  15. ArrayList<Role> roles = new ArrayList<>();
  16. Connection connection = null;
  17. PreparedStatement preparedStatement = null;
  18. ResultSet resultSet = null;
  19. try {
  20. connection = getConnection();
  21. preparedStatement = connection.prepareStatement("select * from menu m ,role r, middle where m.menuid=middle.mid and r.roleid=middle.rid and m.menuid=?");
  22. preparedStatement.setInt(1,mid);
  23. resultSet = preparedStatement.executeQuery();
  24. while(resultSet.next()){
  25. //1.先各自存数据
  26. menu.setMenuName(resultSet.getString("menuname"));
  27. Role role = new Role();
  28. role.setRoleName(resultSet.getString("rolename"));
  29. //2.建立二者关系
  30. roles.add(role);
  31. }
  32. menu.setRoleList(roles);
  33. } catch (SQLException throwables) {
  34. throwables.printStackTrace();
  35. } finally {
  36. close(connection,preparedStatement,resultSet);
  37. }
  38. return menu;
  39. }
  40. @Override
  41. public Role findByRoleId(int roleid) {
  42. Role role = new Role();
  43. ArrayList<Menu> menuArrayList = new ArrayList<Menu>();
  44. Connection connection = null;
  45. PreparedStatement preparedStatement = null;
  46. ResultSet resultSet = null;
  47. try {
  48. connection = getConnection();
  49. preparedStatement = connection.prepareStatement("select * from menu m ,role r, middle where m.menuid=middle.mid and r.roleid=middle.rid and r.roleid=?");
  50. preparedStatement.setInt(1,roleid);
  51. resultSet = preparedStatement.executeQuery();
  52. while(resultSet.next()){
  53. //1.先各自存数据
  54. Menu menu = new Menu();
  55. menu.setMenuName(resultSet.getString("menuname"));
  56. role.setRoleName(resultSet.getString("rolename"));
  57. //2.建立二者关系
  58. menuArrayList.add(menu);
  59. }
  60. role.setMenuList(menuArrayList);
  61. } catch (SQLException throwables) {
  62. throwables.printStackTrace();
  63. } finally {
  64. close(connection,preparedStatement,resultSet);
  65. }
  66. return role;
  67. }
  68. }

测试类

  1. public static void main(String[] args) {
  2. RoleDao roleDao = new RoleDaoImpl();
  3. Role role = roleDao.findByRoleId(1);
  4. System.out.println(role.getRoleName());
  5. List<Menu> menuList = role.getMenuList();
  6. for (Menu menu : menuList) {
  7. System.out.println("\t"+menu.getMenuName());
  8. }
  9. Menu menu = roleDao.findByMenuId(1);
  10. System.out.println(menu.getMenuName());
  11. List<Role> roleList = menu.getRoleList();
  12. for (Role role1 : roleList) {
  13. System.out.println("\t"+role1.getRoleName());
  14. }
  15. }