不使用外键,尝试多对多查询

我的上两篇博客总结了一下不使用外键的优缺点

但是我还没试过,今天尝试了一下,用难一点的多对多关系实验


一:工具:

  springboot

  mybatis

  mysql

二:材料:

  五张表:

    user--用户表

    role--角色表

    permission--权限表

    user-role表

    permission-role表

  其中,user-role表和permission-role表是意义上的中间表,就是没有外键的,其他三张是基本表

 

sql语句:

  1 /*
  2 Navicat MySQL Data Transfer
  3 
  4 Source Server         : root
  5 Source Server Version : 50549
  6 Source Host           : localhost:3306
  7 Source Database       : shiro
  8 
  9 Target Server Type    : MYSQL
 10 Target Server Version : 50549
 11 File Encoding         : 65001
 12 
 13 Date: 2018-05-30 14:42:06
 14 */
 15 
 16 SET FOREIGN_KEY_CHECKS=0;
 17 
 18 -- ----------------------------
 19 -- Table structure for permission
 20 -- ----------------------------
 21 DROP TABLE IF EXISTS `permission`;
 22 CREATE TABLE `permission` (
 23   `pid` int(11) NOT NULL AUTO_INCREMENT,
 24   `name` varchar(255) NOT NULL DEFAULT '',
 25   `url` varchar(255) DEFAULT '',
 26   PRIMARY KEY (`pid`)
 27 ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
 28 
 29 -- ----------------------------
 30 -- Records of permission
 31 -- ----------------------------
 32 INSERT INTO `permission` VALUES ('1', 'add', '');
 33 INSERT INTO `permission` VALUES ('2', 'delete', '');
 34 INSERT INTO `permission` VALUES ('3', 'edit', '');
 35 INSERT INTO `permission` VALUES ('4', 'query', '');
 36 
 37 -- ----------------------------
 38 -- Table structure for permission_role
 39 -- ----------------------------
 40 DROP TABLE IF EXISTS `permission_role`;
 41 CREATE TABLE `permission_role` (
 42   `rid` int(11) NOT NULL,
 43   `pid` int(11) NOT NULL,
 44   KEY `idx_rid` (`rid`),
 45   KEY `idx_pid` (`pid`)
 46 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 47 
 48 -- ----------------------------
 49 -- Records of permission_role
 50 -- ----------------------------
 51 INSERT INTO `permission_role` VALUES ('1', '1');
 52 INSERT INTO `permission_role` VALUES ('1', '2');
 53 INSERT INTO `permission_role` VALUES ('1', '3');
 54 INSERT INTO `permission_role` VALUES ('1', '4');
 55 INSERT INTO `permission_role` VALUES ('2', '1');
 56 INSERT INTO `permission_role` VALUES ('2', '4');
 57 
 58 -- ----------------------------
 59 -- Table structure for role
 60 -- ----------------------------
 61 DROP TABLE IF EXISTS `role`;
 62 CREATE TABLE `role` (
 63   `rid` int(11) NOT NULL AUTO_INCREMENT,
 64   `rname` varchar(255) NOT NULL DEFAULT '',
 65   PRIMARY KEY (`rid`)
 66 ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
 67 
 68 -- ----------------------------
 69 -- Records of role
 70 -- ----------------------------
 71 INSERT INTO `role` VALUES ('1', 'admin');
 72 INSERT INTO `role` VALUES ('2', 'customer');
 73 
 74 -- ----------------------------
 75 -- Table structure for user
 76 -- ----------------------------
 77 DROP TABLE IF EXISTS `user`;
 78 CREATE TABLE `user` (
 79   `uid` int(11) NOT NULL AUTO_INCREMENT,
 80   `username` varchar(255) NOT NULL DEFAULT '',
 81   `password` varchar(255) NOT NULL DEFAULT '',
 82   PRIMARY KEY (`uid`)
 83 ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
 84 
 85 -- ----------------------------
 86 -- Records of user
 87 -- ----------------------------
 88 INSERT INTO `user` VALUES ('1', 'admin', '123');
 89 INSERT INTO `user` VALUES ('2', 'demo', '123');
 90 
 91 -- ----------------------------
 92 -- Table structure for user_role
 93 -- ----------------------------
 94 DROP TABLE IF EXISTS `user_role`;
 95 CREATE TABLE `user_role` (
 96   `uid` int(11) NOT NULL,
 97   `rid` int(11) NOT NULL,
 98   KEY `idx_uid` (`uid`),
 99   KEY `idx_rid` (`rid`)
100 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
101 
102 -- ----------------------------
103 -- Records of user_role
104 -- ----------------------------
105 INSERT INTO `user_role` VALUES ('1', '1');
106 INSERT INTO `user_role` VALUES ('2', '2');

这里补充一下,两张中间表我既没有设外键,也没有设置主键

因为我觉得,用外键时,他们都是联合主键,现在我把外键去了,主键给谁都不对,如果另设一个属性作为主键我觉得也没必要

这里大家有建议欢迎提出,我也是第一次这样做,或许我觉得没必要的地方还是有必要的。

 

三:直接使用mybatis逆向工程生成bean和mapper

 

 

 

四:如何表示多对多关系?

  我的上两篇博客中提到了,如果放弃外键,那么需要自己手动维护表与表之间的关联关系。

  比如说,User实体类中应该有一个roles的集合,而Role实体类里也应该有一个users的集合。

  但在数据库表中,user表和role表并没有相应的属性。因为多对多要通过中间表关联嘛。可惜,中间表user-role和这两表没有任何联系。至少现在是这样的。

  那么,怎么搞?

  要roles集合,我就给你呗!

  在User实体类中加上下面这个不就行了,联系不就有了,找的时候set进去不ok了嘛

 private Set<Role> roles = new HashSet<>(); 

  这样真的好吗?

  不好吧!上面已经说过,所有的bean和mapper都是mybatis逆向工程自动生成。等哪天,我数据库表一变,比如某些表加个其他属性,我又重新逆向工程生成全部bean和mapper,我又之前忘记了在这张表加了个什么,那张表加了什么,东西很难回去了,这就麻烦了。

  所以,建议:

    创建包装类,包装实体类,再在包装类增加其他属性

 1 public class UserVo extends User {
 2 
 3     private Set<Role> roles = new HashSet<>();
 4 
 5     public Set<Role> getRoles() {
 6         return roles;
 7     }
 8 
 9     public void setRoles(Set<Role> roles) {
10         this.roles = roles;
11     }
12 }

  这样,不管实体类怎么变化,包装类就继承你,什么也不用另外变化

  同理,其他也这样弄

-----------------------------------------分割线---------------------------------------------

  等等,这里,我先捋一下关系

  user和role多对多

  permission和role多对多

  意思是说:

    user里要有roles集合;

    role里要有users集合,role里还要有permissions集合;

    permission里要有roles集合;

 

  关键的地方来了,我在User实体类里不能放  Set<Role> roles = new HashSet<>() 

  为什么?

  我是要在roles里取到permissions或者users的,那样写代码大概是 user.getRoles().......getPermissions()

  这是逼我在Role实体类里加一个 private Set<Permission> permissions= new HashSet<>()  啊,这不是上面刚说的不能这样写的吗?

  怎么办?

  我能放一个实体类作为泛型的类型,为什么不能放一个包装类作为泛型的类型,两者本质没有任何区别!

  User实体类的包装类:

 1 public class UserVo extends User {
 2 
 3     private Set<RoleVo> roleVos = new HashSet<>();
 4 
 5     public UserVo() {
 6     }
 7 
 8     public Set<RoleVo> getRoleVos() {
 9         return roleVos;
10     }
11 
12     public void setRoleVos(Set<RoleVo> roleVos) {
13         this.roleVos = roleVos;
14     }
15 
16     @Override
17     public String toString() {
18         return "UserVO{" +
19                 "uid=" + super.getUid() +
20                 ", username=" + super.getUsername() +
21                 ", password=" + super.getPassword() +
22                 ", roleVos.size=" + roleVos.size() +
23                 '}';
24     }
25 }

  Role实体类的包装类

 1 public class RoleVo extends Role {
 2 
 3     private Set<PermissionVo> permissionVos = new HashSet<>();
 4 
 5     private Set<UserVo> userVos = new HashSet<>();
 6 
 7     public Set<PermissionVo> getPermissionVos() {
 8         return permissionVos;
 9     }
10 
11     public void setPermissionVos(Set<PermissionVo> permissionVos) {
12         this.permissionVos = permissionVos;
13     }
14 
15     public Set<UserVo> getUserVos() {
16         return userVos;
17     }
18 
19     public void setUserVos(Set<UserVo> userVos) {
20         this.userVos = userVos;
21     }
22 
23     @Override
24     public String toString() {
25         return "RoleVo{" +
26                 "rid=" + super.getRid() +
27                 ", rname=" + super.getRname() +
28                 ", permissionVos=" + permissionVos +
29                 ", userVos.size=" + userVos.size() +
30                 '}';
31     }
32 }

  Permission实体类的包装类

 1 public class PermissionVo extends Permission {
 2 
 3     private Set<RoleVo> roleVos = new HashSet<>();
 4 
 5     public Set<RoleVo> getRoleVos() {
 6         return roleVos;
 7     }
 8 
 9     public void setRoleVos(Set<RoleVo> roleVos) {
10         this.roleVos = roleVos;
11     }
12 
13     @Override
14     public String toString() {
15         return "PermissionVo{" +
16                 "pid=" + super.getPid() +
17                 ", pname=" + super.getName() +
18                 ", url=" + super.getUrl() +
19                 ", roleVos.size=" + roleVos.size() +
20                 '}';
21     }
22 }

 

 五:实现需求:

  查询一个用户,并把当前用户拥有的角色和角色所具有的权限一并查出

 1   @Test
 2     public void fun() {
 3         // 创建三个包装类
 4         UserVo userVo = new UserVo();
 5         RoleVo roleVo = null;
 6         PermissionVo permissionVo = null;
 7 
 8         // 根据用户uid查询用户
 9         User user = userMapper.selectByPrimaryKey(1);
10 
11         // 装配user属性-- id, username, password,还有一个roleVos在下面
12         BeanUtils.copyProperties(user, userVo);
13 
14         // 通过user的 uid 查询中间表 user-role表
15         UserRoleExample userRoleExample = new UserRoleExample();
16         userRoleExample.createCriteria().andUidEqualTo(user.getUid());
17         // 得到该用户的角色集合
18         List<UserRole> userRoles = userRoleMapper.selectByExample(userRoleExample);
19         // 遍历角色集合
20         for (UserRole userRole : userRoles) {
21             // 通过角色rid, 查询role表中对应的每一个角色
22             Role role = roleMapper.selectByPrimaryKey(userRole.getRid());
23 
24             // 装配roleVo属性 -- rid, rname,还有一个permissionVos在下面
25             roleVo = new RoleVo();
26             BeanUtils.copyProperties(role, roleVo);
27 
28             // 又通过每一个role的rid,查询中间表 permission_role 表
29             PermissionRoleExample permissionRoleExample = new PermissionRoleExample();
30             permissionRoleExample.createCriteria().andRidEqualTo(role.getRid());
31             // 得到该角色具有的权限集合
32             List<PermissionRole> permissionRoles =
33                     permissionRoleMapper.selectByExample(permissionRoleExample);
34             // 遍历权限集合
35             for (PermissionRole permissionRole : permissionRoles) {
36                 // 通过permission的pid,查询 permission表 对应的权限
37                 Permission permission = permissionMapper.selectByPrimaryKey(permissionRole.getPid());
38 
39                 // 装配permissionVo属性 -- pid, name, url, roleVos
40                 permissionVo = new PermissionVo();
41                 BeanUtils.copyProperties(permission, permissionVo);
42                 permissionVo.getRoleVos().add(roleVo);
43 
44                 // 装配roleVo里的permissionVos
45                 roleVo.getPermissionVos().add(permissionVo);
46             }
47             // 装配userVo属性roleVos
48             userVo.getRoleVos().add(roleVo);
49         }
50         System.out.println(userVo);
51         System.out.println(roleVo);
52         System.out.println(permissionVo);
53     }

  结果:

 1   UserVO{
 2         uid=1, 
 3         username=admin, 
 4         password=123, 
 5         roleVos.size=1
 6     }
 7 
 8     RoleVo{
 9         rid=1, 
10         rname=admin,
11         permissionVos=[
12             PermissionVo{pid=4, pname=query, url=, roleVos.size=1}, 
13             PermissionVo{pid=2, pname=delete, url=, roleVos.size=1}, 
14             PermissionVo{pid=1, pname=add, url=, roleVos.size=1}, 
15             PermissionVo{pid=3, pname=edit, url=, roleVos.size=1}
16         ], 
17         userVos.size=0
18     }
19 
20     PermissionVo{
21         pid=4, 
22         pname=query, 
23         url=, 
24         roleVos.size=1
25     }

 


 

小小总结一下:

  1. 别看上面实现的代码挺多的,去掉注释也就30行左右,只要不绕进去,逻辑还是不难的(废话)

  2.善用包装类

  3.注意看三个包装类的toString方法,有些我是只给打印长度的,比如roleVos.size(),为什么,全部打印出来会死循环,好好想想为什么

 

posted @ 2018-05-30 16:32  shadowdoor  阅读(3705)  评论(1编辑  收藏  举报