zoukankan      html  css  js  c++  java
  • 不使用外键,尝试多对多查询

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

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


    一:工具:

      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(),为什么,全部打印出来会死循环,好好想想为什么

  • 相关阅读:
    mybaits不能出现小于号
    结合rpyc使用python实现动态升级的方法
    secureCRT使用小贴士
    大数据的实时技术
    gnuplot使用
    Python3.4使用MySql
    Linux内存分配----SLAB
    WinInet:HTTPS 请求出现无效的证书颁发机构的处理
    正则表达式学习
    C++中static的全部作用
  • 原文地址:https://www.cnblogs.com/shadowdoor/p/9111694.html
Copyright © 2011-2022 走看看