Day09.上课提示
0.实战重要性说明
# 这个案例要求每个人必须独立完成,因为后面web综合案例后台就是这里的代码,web阶段不会在讲解,后期可以直接使用你当前书写的代码,后期我们会将大部分时间放到前端上,因为前端是我们的弱项,重点攻克前端。
1. 项目需求
1.模块分析
学习目标
- 掌握用户角色权限表关系以及设计原则
内容讲解
1.我们今天完成的项目整体分为三个模块:
用户模块 功能:增删改查
角色模块 功能:增删改查
权限模块 功能:增删改查
2.分析:
用户和角色的关系:
一个用户具有多个角色。举例:张三用户可以是QQ黄钻 绿钻 等
一个角色可以对应多个用户。举例:QQ黄钻可以是张三 李四 等
#用户和角色属于多对多的关系,根据表设计原则,多对多关系创建中间表,在中间表中起码要有另外两张主表用户和角色的主键作为外键进行关联
角色和权限的关系:
一个角色可以有多种权限。举例:QQ黄钻:可以查看被挡访客,可以装扮空间等
一种权限可以对应多个角色。举例:可以查看被挡访客的权限可以是QQ黄钻,也可以是绿钻
#角色和权限属于多对多的关系,根据表设计原则,多对多关系创建中间表,在中间表中起码要有另外两张主表权限和角色的主键作为外键进行关联
#注意:用户 角色 权限具有经典的五张表
内容小结
1.用户和角色具有多对多关系,创建中间表
2.角色和权限具有多对多关系,创建中间表
2.使用ER图描述五张表的关系
学习目标
- 能够使用ER图描述五张表的关系
内容讲解
内容小结
注意:
1.表之间是多对多需要创建中间表维护两张主表的关系
2.表之间是一对多关系:需要将一方的主键作为多方的外键
3.表之间是一对一关系:需要在任意一方将另一方的主键作为外键
3.创建表的sql语句
-- 1.用户表t_user
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- 用户编号
`username` varchar(32) DEFAULT NULL, -- 用户名字
`password` varchar(32) DEFAULT NULL, -- 用户密码
`remark` varchar(32) DEFAULT NULL, -- 用户备注
`email` varchar(32) DEFAULT NULL, -- 用户邮箱
`createTime` timestamp not NULL DEFAULT CURRENT_TIMESTAMP, -- 该用户创建时间
`updateTime` timestamp not NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 该用户修改时间
PRIMARY KEY (`id`) -- 设置主键
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- timestamp 表示时间戳 年月日 时分秒
-- date 年月日
-- mysql8.0以上timestamp换成datetime,或者修改my.ini 或者插入数据不要插入null,使用mysql时间函数now()
-- ----------------------------
-- Records of t_user
-- ----------------------------
insert into t_user values(null,'admin','123',null,'admin@163.com',null,null)
,(null,'zhansan','123',null,'zhansan@163.com',null,null),(null,'lisi','123',null,'lisi@163.com',null,null),(null,'wangwu','123',null,'wangwu@163.com',null,null),(null,'zhaoliu','123',null,'zhaoliu@163.com',null,null),(null,'tianqi','123',null,'tianqi@163.com',null,null),(null,'柳岩','123',null,'liuyan@163.com',null,null),(null,'杨幂','123',null,'yangmi@163.com',null,null),(null,'李沁','123',null,'liqin@163.com',null,null),(null,'赵丽颖','123',null,'zhaoliying@163.com',null,null);
-- 2.角色表t_role
CREATE TABLE `t_role` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- 角色id
`name` varchar(32) DEFAULT NULL, -- 角色名字
`keyword` varchar(64) DEFAULT NULL, -- 角色关键字
`description` varchar(128) DEFAULT NULL, -- 角色描述
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_role
-- ----------------------------
INSERT INTO `t_role` VALUES ('1', '管理员', 'ROLE_ADMIN', '这是管理员')
,('2', '会员', 'ROLE_MEMBER', '这是会员')
,('3', '游客', 'ROLE_VISITOR', '这是游客');
-- 3.权限表
CREATE TABLE `t_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- 权限编号
`name` varchar(32) DEFAULT NULL, -- 权限名字
`keyword` varchar(64) DEFAULT NULL, -- 权限关键字
`description` varchar(128) DEFAULT NULL, -- 权限描述
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_permission
-- ----------------------------
INSERT INTO `t_permission` VALUES ('1', '新增', 'ITEM_ADD', '这是新增权限')
,('2', '删除', 'ITEM_DELETE', '这是删除权限')
,('3', '编辑', 'ITEM_EDIT', '这是编辑权限')
,('4', '查询', 'ITEM_QUERY', '这是查询权限');
-- 4.用户角色中间表
CREATE TABLE `t_user_role` (
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`role_id`),
KEY `FK_Reference_8` (`role_id`),
CONSTRAINT `FK_Reference_7` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user_role
-- ----------------------------
INSERT INTO `t_user_role` VALUES ('1', '1'),('2', '2'),('1', '2'),('3', '2'),('4', '1'),('5', '2'),('6', '1'),('7', '3'),('8', '1'),('9', '3'),('10', '1'),('8', '2'),('9', '1'),('10', '2');
-- 5.角色权限中间表
CREATE TABLE `t_role_permission` (
`role_id` int(11) NOT NULL,
`permission_id` int(11) NOT NULL,
PRIMARY KEY (`role_id`,`permission_id`),
KEY `FK_Reference_12` (`permission_id`),
CONSTRAINT `FK_Reference_11` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`),
CONSTRAINT `FK_Reference_12` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_role_permission
-- ----------------------------
INSERT INTO `t_role_permission`
VALUES ('1', '1'),('1', '2'),('1', '3'),('1', '4'),('2', '3'),('2', '4'),('3', '4');
创建好表之间的关系图:
4.环境搭建
1.创建maven工程
2.导入依赖
<!--依赖-->
<dependencies>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!--<version>4.13</version>-->
<version>4.13</version>
<!--范围:测试存在-->
<!--<scope>test</scope>-->
</dependency>
<!--mybatis核心包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
</dependency>
<!--logback日志包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
</dependencies>
<!--导入插件-->
<!--配置maven的插件-->
<build>
<plugins>
<!--配置的是jdk编译器-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!-- put your configurations here -->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
3.导入配置文件
4.创建层次包
5.导入实体类
6.导入工具类
2.练习
1.分页查询用户和对应的角色信息
需求:分页查询用户和对应的角色信息,然后将信息输出到idea控制台。
【1】sql
/*
需求:分页查询用户和对应的角色信息
*/
-- 1.在用户表中分页查询用户信息 每页显示3条数据
-- 0 表示0索引对应第一行数据
-- 3 表示每页显示数据行数是3
-- 下面sql语句查询的是第一页
select *
from t_user
limit 0,3;
-- 2.将上述分页查询的用户信息和中间表t_user_role以及角色表t_role连接查询
select u.*,r.id as rid,r.name,r.keyword,r.description
from (select * from t_user limit 0,3) as u
inner join t_user_role as ur
inner join t_role as r on u.id=ur.user_id and ur.role_id=r.id;
【2】流程图
【3】代码实现
- 实体类
- web层
package com.itheima.sh.web;
import com.itheima.sh.pojo.User;
import com.itheima.sh.service.UserService;
import java.util.List;
import java.util.Scanner;
public class QueryAllUsersAndRolesByPage {
public static void main(String[] args) {
//1.创建键盘录入对象
Scanner sc = new Scanner(System.in);
//2.获取起始索引
System.out.println("请输入起始索引:");
int startIndex = sc.nextInt();//0
//3.获取每页显示条数
System.out.println("请输入每页显示条数:");
int pageSize = sc.nextInt();//3
//4.创建业务层对象
UserService userService = new UserService();
//5.使用业务层对象调用方法根据起始索引和每页显示数据条数查询数据
List<User> list = userService.queryAllUsersAndRolesByPage(startIndex,pageSize);
System.out.println(list.size()+"=====");
//6.输出
System.out.println("list = " + list);
}
}
- service层
package com.itheima.sh.service;
import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.pojo.User;
import com.itheima.sh.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class UserService {
//定义方法分页查询数据
public List<User> findAllUsersByPage(int startIndex, int pageSize) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//获取接口代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//使用接口代理对象调用分页查询用户和角色方法
List<User> list = mapper.findAllUsersByPage(startIndex, pageSize);
sqlSession.close();
return list;
}
}
- UserMapper接口
public interface UserMapper {
/*
分页查询所有的用户信息以及用户对应的角色信息
注意:
在mybatis中,如果方法形参类型含有多个参数,那么需要使用命名参数注解@Param进行声明,然后在映射文件的sql语句位置书写的内容按照命名
参数双引号内容进行配置:
select * from t_user limit #{startIndex},#{pageSize}
*/
List<User> findAllUsersByPage(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);
}
- UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.itheima.case2.dao.UserMapper">
<!--
1.id="findAllUsersByPageMap" : 表示唯一标识,通过属性值关联下面的select标签
2.type="user":接口中关联方法的返回值类型,是容器中的泛型类型
3.autoMapping="true":如果实体类和数据表的字段名一致那么会自动映射
-->
<resultMap id="findAllUsersByPageMap" type="user" autoMapping="true">
<!--配置t_user表和User实体类中的主键映射关系、
4.<id column="id" property="id"/> :column表示数据表的字段名 property 表示实体类中的成员变量名
-->
<id column="id" property="id"/>
<!--
在mybatis中没有多对多,配置一对多,一个用户对应多个角色,使用的标签是collection
5.ofType="role"表示List集合中保存的实体类类型
6.javaType="list"表示存储多个角色对象的容器类型
7.autoMapping="true":如果实体类和数据表的字段名一致那么会自动映射
8.property="roles":表示在User实体类集合容器对象名是roles
-->
<collection property="roles" javaType="list" autoMapping="true" ofType="role">
<!--
9.配置t_role表和实体类Role之间主键的映射关系:
1)column="rid" 表示数据表的字段名
2)property="id" 表示Role实体类的成员变量名
-->
<id column="rid" property="id"/>
</collection>
</resultMap>
<!--
查询语句
id: 接口中方法的名字 findAllUsersByPage
resultMap:如果是多表查询这里使用resultMap,然后创建resultMap标签,通过resultMap的属性值findAllUsersByPageMap进行关联
List<User> findAllUsersByPage(int startIndex, int pageSize);
-->
<select id="findAllUsersByPage" resultMap="findAllUsersByPageMap">
select u.*,r.id rid,r.name from
(select * from t_user limit #{startIndex},#{pageSize}) u
inner join t_user_role ur
inner join t_role r
on u.id = ur.user_id and
ur.role_id = r.id
</select>
</mapper>
2.分页查询角色以及对应的用户和权限信息(参考第一个练习自己完成)
【1】sql
-- 分页查询角色以及对应的用户和权限信息
-- 1.在角色表中分页查询角色信息
select * from t_role limit 0,2;
-- 2.查询的结果和用户以及权限表关联
select u.*,r.id rid,r.name,r.keyword,r.description,p.id pid,p.name,p.keyword,p.description
from t_user u
inner join
(select * from t_role limit 0,2) r
inner join t_user_role ur
inner join t_role_permission rp
inner join t_permission p
on u.id = ur.user_id and ur.role_id=r.id and r.id=rp.role_id and rp.permission_id=p.id
【2】代码实现
- 实体类
一个角色对应多个用户,角色实体类定义集合保存多个用户
一个角色具有多个权限,角色实体类中定义集合保存多个权限
- web层
package com.itheima.sh.web;
import com.itheima.sh.pojo.Role;
import com.itheima.sh.service.RoleService;
import java.util.List;
public class FindAllRolesByPage {
public static void main(String[] args) {
//创建业务层对象
RoleService roleService = new RoleService();
//使用业务层对象调用方法根据索引0和每页显示数据条数3查询第一页数据
List<Role> list = roleService.findAllRolesByPage(0, 2);
//输出分页查询的数据到控制台
System.out.println("list = " + list);
}
}
- service
package com.itheima.sh.service;
import com.itheima.sh.dao.RoleMapper;
import com.itheima.sh.pojo.Role;
import com.itheima.sh.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class RoleService {
public List<Role> findAllRolesByPage(int startIndex, int pageSize) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//获取接口代理对象
RoleMapper mapper = sqlSession.getMapper(RoleMapper.class);
//使用接口代理对象调用分页查询用户和角色方法
List<Role> list = mapper.findAllRolesByPage(startIndex, pageSize);
sqlSession.close();
return list;
}
}
- dao
package com.itheima.sh.dao;
import com.itheima.sh.pojo.Role;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface RoleMapper {
List<Role> findAllRolesByPage(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);
}
- RoleMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.itheima.sh.dao.RoleMapper">
<!--
1.id="findAllUsersByPageMap" : 表示唯一标识,通过属性值关联下面的select标签
2.type="user":接口中关联方法的返回值类型,是容器中的泛型类型
3.autoMapping="true":如果实体类和数据表的字段名一致那么会自动映射
-->
<resultMap id="findAllRolesByPageMap" type="role" autoMapping="true">
<!--配置t_role表和Role实体类中的主键映射关系、
4.<id column="rid" property="id"/> :column表示数据表的字段名 property 表示实体类中的成员变量名
-->
<id column="rid" property="id"/>
<!--
在mybatis中没有多对多,配置一对多,一个用户对应多个角色,使用的标签是collection
5.ofType="user"表示List集合中保存的实体类类型
6.javaType="list"表示存储多个角色对象的容器类型
7.autoMapping="true":如果实体类和数据表的字段名一致那么会自动映射
8.property="users":表示在User实体类集合容器对象名是users
-->
<collection property="users" javaType="list" autoMapping="true" ofType="user">
<!--
9.配置t_user表和实体类User之间主键的映射关系:
1)column="id" 表示数据表的字段名
2)property="id" 表示User实体类的成员变量名
-->
<id column="id" property="id"/>
</collection>
<collection property="permissions" javaType="list" autoMapping="true" ofType="Permission">
<!--
9.配置t_permission表和实体类Permission之间主键的映射关系:
1)column="pid" 表示数据表的字段名
2)property="id" 表示Permission实体类的成员变量名
-->
<id column="pid" property="id"/>
</collection>
</resultMap>
<!--
查询语句
id: 接口中方法的名字 findAllRolesByPage
resultMap:如果是多表查询这里使用resultMap,然后创建resultMap标签,通过resultMap的属性值findAllUsersByPageMap进行关联
List<Role> findAllRolesByPage(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);
-->
<select id="findAllRolesByPage" resultMap="findAllRolesByPageMap">
select u.*,r.id rid,r.name,r.keyword,r.description,p.id pid,p.name,p.keyword,p.description
from t_user u
inner join
(select * from t_role limit #{startIndex},#{pageSize}) r
inner join t_user_role ur
inner join t_role_permission rp
inner join t_permission p
on u.id = ur.user_id and ur.role_id=r.id and r.id=rp.role_id and rp.permission_id=p.id
</select>
</mapper>
3.分页查询角色以及权限信息
略
4.修改用户以及对应的角色信息(扩展,不要求所有人完成)
【1】需求:修改id是1的用户的密码为456,用户名为刘德华,扮演角色变为游客(提示:游客角色id是3)和会员(提示:会员角色id是2)
需求分析:
举例:
修改之前
用户:
1 admin 123 admin@163.com 2021-11-08 15:04:43 2021-11-08 15:04:43
此时admin用户扮演的角色是:管理员和会员
修改之后:
1 刘德华(用户名修改了) 345(密码修改了) admin@163.com 2021-11-08 15:04:43 2021-11-08 15:04:43
admin用户所扮演的角色更改为:会员和游客
注意:修改用户名和密码直接修改t_user表中的数据即可。
说明:
一个用户能够扮演怎样的角色是由中间表t_user_role决定的。
举例:
admin用户修改前中间表数据:
user_id role_id
1 1 管理员id
1 2 会员id
admin用户修改后中间表数据:
user_id role_id
1 2 会员id
1 3 游客id
注意:如何将admin在中间的数据变为修改之后的呢?
1)先根据当前用户id的值即1删除当前用户在中间表的所有数据
2)然后将修改后的user_id的值1和role_id的值2和3重新插入到中间表,注意事项是一个用户新的角色有可能是多个,所以需要遍历取出多个角色id
【2】代码实现
- web层
package com.itheima.sh.web;
import com.itheima.sh.pojo.User;
import com.itheima.sh.service.UserServiceImpl;
import java.util.ArrayList;
public class UpdateUserWeb {
public static void main(String[] args) {
/*
需求:修改id是1的用户的密码为456,用户名为刘德华,扮演角色变为游客(提示:游客角色id是3)和会员(提示:会员角色id是2)
*/
//1.创建实体类对象
User user = new User();
//2.向user对象中添加数据
user.setId("1");//修改用户的id
user.setUsername("刘德华");//用户的新用户名
user.setPassword("456");//用户的新密码
//创建集合保存当前用户新角色的id
ArrayList<String> list = new ArrayList<>();
list.add("2");//会员角色id是2
list.add("3");//游客角色id是3
user.setRoleIds(list);
//3.创建业务层对象
UserServiceImpl userService = new UserServiceImpl();
//4.调用修改方法
userService.updateUserById(user);
}
}
- service层
//修改用户方法
public void updateUserById(User user) {
//1.获取会话工厂对象
SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
//2.获取接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserRoleMapper userRoleMapper = sqlSession.getMapper(UserRoleMapper.class);
//3.使用UserMapper接口代理对象调用UserMapper接口中根据id更新用户的方法
userMapper.updateUserById(user);
//4.使用UserRoleMapper接口代理对象调用UserRoleMapper的方法根据用户id删除中间表t_user_role中有关当前用户id所有的信息
userRoleMapper.deleteUserAndRoleByUID(user.getId());
//5.使用UserRoleMapper接口代理对象调用UserRoleMapper的方法将当前用户id和新的角色id插入到中间表t_user_role中
//注意在User实体类中使用集合private List<String> roleIds;保存多个角色id,因为一个用户可以有多个角色
//这里需要遍历集合roleIds取出每个角色id
List<String> roleIds = user.getRoleIds();
//遍历
/*
admin(1) 原来角色 管理员(1) 会员(2)
admin(1) 新的角色 会员(2) 游客(3)
*/
for (String roleId : roleIds) {
//6.将当前用户的用户id和角色id插入到中间表t_user_role
userRoleMapper.addUserAndRoleID(user.getId(),roleId);
}
}
-
dao层
【UserMapper接口】
public interface UserMapper {
//根据用户id修改用户名称 密码
@Update("update t_user set username=#{username},password=#{password} where id=#{id}")
void updateUserById(User user);
}
【UserRoleMapper】
package com.itheima.sh.dao;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
public interface UserRoleMapper {
//根据用户id删除中间表数据
@Delete("delete from t_user_role where user_id=#{id}")
void deleteUserAndRoleByUID(@Param("id") String id);
//向中间表插入新的数据
@Insert("insert into t_user_role values(#{uid},#{roleId})")
void addUserAndRoleID(@Param("uid")String id, @Param("roleId")String roleId);
}