效果如图
1.前言
根据需求,搭建一个springboot项目,分为登录注册界面,用户管理,角色管理模块
然后因为前后端分离,所以我们使用jwt作为我们用户身份凭证。
2.后端接口开发
2.1环境配置
2.11 创建springboot

2.12 导入相应的依赖+配置tkmapper插件
<!--spring-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombook-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- swaggerUi-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<!--引入tkMapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- test starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--druid-starter-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!-- jwt相关-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
tkampper插件
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.4.4</version>
</dependency>
</dependencies>
</plugin>
插件结构如下
2.13 配置application.yml
server:
port: 9999
servlet:
context-path:
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/spring_test?characterEncoding=utf-8&serverTimezone=GMT
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mvc:
view:
prefix: /
suffix: .jsp
mybatis:
mapper-locations: classpath:mappers/*Mapper.xml
type-aliases-package: com.itheima.role_user_demo.pojo
2.14 在resources下新建mappers目录和generator目录,在generator下新建generatorConfig.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 引入数据库连接配置 -->
<!-- <properties resource="jdbc.properties"/>-->
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!-- 配置 GeneralDAO -->
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.itheima.role_user_demo.general.GeneralDao"/>
</plugin>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/spring_test"
userId="root" password="123456">
</jdbcConnection>
<!-- 配置实体类存放路径 -->
<javaModelGenerator targetPackage="com.itheima.role_user_demo.pojo" targetProject="src/main/java"/>
<!-- 配置 XML 存放路径 -->
<sqlMapGenerator targetPackage="/" targetProject="src/main/resources/mappers"/>
<!-- 配置 DAO 存放路径 -->
<javaClientGenerator targetPackage="com.itheima.role_user_demo.dao" targetProject="src/main/java" type="XMLMAPPER"/>
<!-- 配置需要指定生成的数据库和表,% 代表所有表 -->
<!--<table tableName="%"></table>-->
<!-- <!–字段命名策略过程: table标签对应数据库中的table表–>-->
<table tableName="sys_user" domainObjectName="User"></table>
<table tableName="sys_role" domainObjectName="Role"></table>
<table tableName="sys_user_role" domainObjectName="userAndRole"></table>
</context>
</generatorConfiguration>
2.15 新建generalDao接口+配置启动类
package com.itheima.role_user_demo.general;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
public interface GeneralDao<T> extends Mapper<T>,MySqlMapper<T> {
}
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.itheima.role_user_demo.dao")
public class RoleUserDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RoleUserDemoApplication.class, args);
}
}

2.16 创建数据库
CREATE DATABASE spring_test;
USE `spring_test`;
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`roleName` varchar(50) DEFAULT NULL,
`roleDesc` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_idnamedesc` (`id`,`roleName`,`roleDesc`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
insert into `sys_role`(`id`,`roleName`,`roleDesc`) values (1,'院长老婆2','负责全面工作'),(2,'研究员','课程研发工作'),(3,'讲师','授课工作'),(4,'助教','协助解决学生的问题'),(5,'就业指导','讲找工作的jjjjj'),(6,'篮球教练','打酱油的');
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`password` varchar(80) DEFAULT NULL,
`phoneNum` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8;
insert into `sys_user`(`id`,`username`,`email`,`password`,`phoneNum`) values (2,'mj1234','2286170708@qq.com','8888','1876661611'),(3,'swifties270','2983593131@qq.com','123','123dffggggghh'),(3,'储飞','1314','2425','12121'),(5,'迈克尔乔丹','27228282@qq.com','4444','1282882'),(6,'吃','1123','1213','2311234'),(7,'韦德','1123','1213','2311234');
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`userId` bigint(50) NOT NULL,
`roleId` bigint(50) NOT NULL,
PRIMARY KEY (`userId`,`roleId`),
KEY `roleId` (`roleId`),
CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `sys_user` (`id`),
CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `sys_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `sys_user_role`(`userId`,`roleId`) values (22,1),(26,1),(2,2),(4,2),(22,2),(24,2),(26,2),(2,3),(27,3),(4,4),(24,4),(27,5);
点击mybatis-generator:generator 一键生成DAO,POJO层
2.17 生成swagger()
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/*swagger会帮助我们生成接口文档
* 1:配置生成的文档信息
* 2: 配置生成规则*/
/*Docket封装接口文档信息*/
@Bean
public Docket getDocket(){
//创建封面信息对象
ApiInfoBuilder apiInfoBuilder = new ApiInfoBuilder();
apiInfoBuilder.title("志愿者服务后端接口说明")
.description("此文档详细说明了志愿者服务项目后端接口规范....")
.version("v 2.0.1")
.contact(new Contact("储飞","www.baidu.com","liangge@wang.com") );
ApiInfo apiInfo = apiInfoBuilder.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo) //指定生成的文档中的封面信息:文档标题、版本、作者
.select()
.apis(RequestHandlerSelectors.basePackage("com.itheima.role_user_demo.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
}
2.18 统一结果封装
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "响应给VO对象",description = "封装接口返回给前端的数据")
public class ResultVo {
//响应给前端的状态码
@ApiModelProperty(value = "响应状态码",dataType = "int")
private int code;
//响应给前端的提示信息
@ApiModelProperty("响应提示信息")
private String msg;
//响应给前端的数据
@ApiModelProperty("响应数据")
private Object data;
}
public class ResStatus {
public static final int OK=10000;
public static final int NO=10001;
public static final int LOGIN_SUCCESS = 2000; //认证成功
public static final int LOGIN_FAIL_NOT = 20001; //用户未登录
public static final int LOGIN_FAIL_OVERDUE = 20002; //用户登录失效
}
项目结构如下

2.2 业务实现
2.21 角色管理(crud)
Service层接口
public interface RoleService {
//查找所有的角色
public ResultVo findAll();
//增加角色
public ResultVo addRole(Role role);
//修改角色
public ResultVo updateRole(Role role);
//根据角色id查出角色
public ResultVo findRoleById(Long id);
//删除角色
public ResultVo deleteRoleById(Long id);
//根据用户id获取角色
public ResultVo findRolesByUserID(Long id);
}
其中的根据用户id获取角色是为了用户管理界面,查询所有用户并展示出该用户所具有的角色所服务的,需要在Rolemapper层自定义接口
@Repository
public interface RoleMapper extends GeneralDao<Role> {
List<Role> findRoleByUserId(Long id);
}
roleMapper.xml
<resultMap id="BaseResultMap" type="com.itheima.role_user_demo.pojo.Role">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="roleName" jdbcType="VARCHAR" property="rolename" />
<result column="roleDesc" jdbcType="VARCHAR" property="roledesc" />
</resultMap>
<select id="findRoleByUserId" resultMap="BaseResultMap">
SELECT r.id,r.roleName,r.roleDesc
FROM sys_role r
INNER JOIN sys_user_role ur ON r.id=ur.roleId AND ur.userId=#{id}
</select>
Service层实现类 这里以删除角色和根据用户id查询角色举例
值得注意的是,角色的删除不能像前面的操作那么简单 由于外键约束的作用 sys_role里的数据删除了自然sys_role_user表里数据也要改变

删除有deleteByExample(example)和deleteByPrimaryKey(keyValue)两种方法,建议使用前者 tkmmaper的基本用法
@Override
public ResultVo deleteRoleById(Long id) {
//1.删除中间表sys_user_role 中的数据
Example example = new Example(UserAndRole.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("roleid",id);
//deleteByExample 传入的是example对象
userAndRoleMapper.deleteByExample(example);
//userAndRoleMapper.deleteByPrimaryKey(id);
//2.删除sys_role表中的数据
int i = roleMapper.deleteByPrimaryKey(id);
if(i>0) return new ResultVo(ResStatus.OK,"success",i);
else return new ResultVo(ResStatus.NO,"fail",null);
}
@Override
public ResultVo findRolesByUserID(Long id) {
List<Role> roles = roleMapper.findRoleByUserId(id);
System.out.println("roles"+roles);
return new ResultVo(ResStatus.OK,"success",roles);
}
单元测试 选中待测类,快捷键ctrl + shift + t,选择Create New Test

controller层
@RestController
@CrossOrigin
@RequestMapping("/role")
@Api(tags = "角色管理")
public class RoleController {
@Autowired
private RoleService roleService;
@ApiOperation("查看所有角色")
@GetMapping("/findAllRole")
public ResultVo findAllRole(){
return roleService.findAll();
}
@PostMapping(value = "/addRole",produces="application/jsons;charset=UTF-8")
@ApiOperation("增加角色")
public ResultVo addRole(@RequestBody Role role) { return roleService.addRole(role); }
@PostMapping(value = "/updateRole" , produces="application/jsons;charset=UTF-8")
@ApiOperation("修改角色")
public ResultVo updateRole(@RequestBody Role role) { return roleService.updateRole(role);}
@PostMapping(value = "/delRole")
@ApiOperation("删除角色")
public ResultVo deleteRole(@RequestBody Role role){ return roleService.deleteRoleById(role.getId()); }
}
这里以最容易出错的删除举例子

2.22 用户管理(crud)
接口

findAll()方法的实现
@Override
public ResultVo findAll(){
List<User> users = userMapper.selectAll();//这个user里是没有该用户拥有的角色
System.out.println("user的个数"+users.size());
for (User user : users) {
Long id = user.getId();
//根据用户的id获取该用户所具有的角色
List<Role> roles = roleMapper.findRoleByUserId(id);
user.setRoleList(roles);
}
return new ResultVo(ResStatus.OK,"successs",users);
}
addUser()方法的实现
@Override
public ResultVo addUser(User user,String roleIds){
//前端传给后端的数据往往是字符串的格式以逗号隔开
//1.在sys_user表中添加数据
String[] ids = roleIds.split(",");
List<Long>roleList=new ArrayList<>();
for (String roleId : ids) {
roleList.add(Long.parseLong(roleId));
}
int key = userMapper.insertUseGeneratedKeys(user);
Long userId=user.getId();
// System.out.println("userId="+userId);
//2.获得返回的主键Id即userId,再更新sys_user_role表
UserAndRole userAndRole=new UserAndRole();
userAndRole.setUserid(userId);
for (Long roleId : roleList) {
userAndRole.setRoleid(roleId);
userAndRoleMapper.insert(userAndRole);
}
return new ResultVo(ResStatus.OK,"success","null");
}
deleteUserById方法的实现
@Override
public ResultVo deleteUserById(Long id) {
//删除用户自然也会对sys_user_role产生影响
//1.先删除sys_user_role中的数据
Example example=new Example(UserAndRole.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("userid",id);
//deleteByPrimaryKey(example)
userAndRoleMapper.deleteByExample(example);
//2.删除sys_user表中的数据
int i = userMapper.deleteByPrimaryKey(id);
if(i>0) return new ResultVo(ResStatus.OK,"success",i);
else return new ResultVo(ResStatus.NO,"fail",null);
}
controlller层实现
@RestController
@CrossOrigin
@RequestMapping("/user")
@Api(tags="用户管理")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findAllUser")
@ApiOperation("查找所有用户")
public ResultVo findAllUser(){ return userService.findAll();}
@PostMapping("/addUser")
@ApiOperation("增加用户")
public ResultVo addUser(@RequestBody User user,String ids){ return userService.addUser(user,ids); }
@PostMapping("/updateUser")
@ApiOperation("修改用户")
public ResultVo updateUser(@RequestBody User user){return userService.updateUser(user); }
@PostMapping("/delUser")
@ApiOperation("删除用户")
public ResultVo delUser(@RequestBody User user){return userService.deleteUserById(user.getId()); }
}
用户管理接口测试不作赘述
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决