不使用外鍵,嘗試多對多查詢

個人上兩篇博客總結了一下不使用外鍵的優缺點mysql

可是我還沒試過,今天嘗試了一下,用難一點的多對多關係實驗spring


一:工具:sql

  springboot數據庫

  mybatisspringboot

  mysqlmybatis

二:材料:app

  五張表:ide

    user--用戶表工具

    role--角色表ui

    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(),爲何,所有打印出來會死循環,好好想一想爲何

相關文章
相關標籤/搜索