目录
项目业务
技术栈
数据库表的设计
数据库表关系图
关于字典表
前后端接口实现
1.初始化数据库、表、数据
2.数据库连接池配置
3.统一响应封装
4.统一异常处理
5.实现统一会话管理(登录)
5.通过Mybatis生成工具,生成mapper接口,xml文件,实体类
6.准备所有的Controller和servise类
7.实现接口
(1)登录接口
(2)实现注销接口
(3)实现数据字典下拉菜单
(4)获取班级信息
(5)获取学生信息
(6)班级管理页面-表格展示
说明
(7)新增接口
(8)获取数据详情(每一条)
(9)修改接口
(10) 删除接口
总结
使用者:图书馆管理员
业务:管理学校图书信息,记录并管理学生借阅图书信息
有以下几个模块
- SpringBoot
- SpringMVC
- MyBatis
数据字典表和数据字典标签表主要用在一些通用的下拉菜单选项。像本项目在班级表中的专业和毕业年份如果单独设计需要单独一张表,实际存放的数据也不会太多,可以考虑设计在整体的一张表中。
一般在设计上考虑为两张表:数据字典表和数据字典标签表(一对多关系)来保存。两张表都是 key、 value 的形式,字典表是父节点对应下拉菜单,字典标签表是子节点对应下拉菜单选项,下拉菜单通过父节点的 key 查询出所 有关联的子节点,再使用子节点的key、 value 进行下拉菜单选项的初始化。
我这里只给大家说后端接口的实现,前端大家可以在github上找
- 需要说明的是,接口的定义一般是前后端约定好的,所以也和前端代码息息相关,前端需要什么数据, 需要什么格式的数据,也会在接口中体现。
这里大家就创建以上那几个表,然后插入一些数据,在自己的MySQL执行一下
在pom.xml插入以下代码,并替换成自己的用户名和密码
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=***
spring.datasource.password=*******
一次http请求,调用Controller请求映射方法(返回值Object)
正常返回:ResponseResult(success=true,data=obj)
出现异常:ResponseResult(success=false,message=错误信息)
若程序内部错误:如语法错误、SQL写错,这种错是一大段英文,不能给用户看,这里设置返回中文的json字符串
@ExceptionHandler(Exception.class) @ResponseBody public Object handleException(Exception e){ log.error("系统出错啦", e); ResponseResult json = new ResponseResult(); json.setMessage("系统出错了,请联系管理员"); return json; }
还有一种是返回自定义异常:如用户名不存在、密码错误……
@ExceptionHandler(AppException.class) @ResponseBody public Object handleAppException(AppException e){ log.debug("自定义异常", e); ResponseResult json = new ResponseResult(); json.setMessage(e.getMessage()); return json; }
这里是用springMVC的HandlerInterceptor拦截器实现的,登录成功可以访问,未登录重定向到登录界面
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //判断是否登录 HttpSession session = request.getSession(false); if(session != null){//获取登录时保存的用户信息 User user = (User) session.getAttribute("user"); if(user != null){//登录,允许访问 return true; } } //未登陆,不允许访问 String servletPath = request.getServletPath();//服务路径 if(servletPath.startsWith("/api/")){//后端接口,未登陆返回401,json response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); response.setStatus(401); ResponseResult json = new ResponseResult(); json.setMessage("未登陆,不允许访问"); response.getWriter().println(objectMapper.writeValueAsString(json)); }else{//前端页面,未登陆,重定向到登录页面 String schema = request.getScheme();// http String host = request.getServerName();//服务端ip或域名 int port = request.getServerPort();//port String contextPath = request.getContextPath();//应用上下文路径 String basePath = schema+"://"+host+":"+port+contextPath; response.sendRedirect(basePath+"/index.html"); } return false; }
添加要拦截的前端页面,和所有后端接口,除了注册、登录
registry.addInterceptor(new LoginInterceptor(objectMapper)) //前端:添加要拦截的页面 .addPathPatterns("/page/main.html") //拦截所有后端接口,排除用户注册,登录 .addPathPatterns("/api/**") .excludePathPatterns("/api/user/login") .excludePathPatterns("/api/user/register");
每个实体类对应一个Controller和一个Service
例:
@RestController //注册到容器中 @RequestMapping("/book") //设置路径 public class BookController { @Autowired //注入 private BookService bookService;
@Service public class BookService { @Autowired private BookMapper bookMapper;
点击登录抓包可得:
可见请求路径为:POST http://localhost:8080/api/user/login
在UserController中写登录接口
//POST http://localhost:8080/api/user/login @RequestMapping("/login") public Object login(@RequestBody User user, HttpServletRequest request){ // 1.根据账号查询数据 User param=new User(); param.setUsername(user.getUsername()); User exist=userService.selectOne(param); // 未查询到用户 if(exist==null){ throw new AppException("账号不存在"); } // 2.若查询到用户校验密码是否正确 if(!exist.getPassword().equals(user.getPassword())){ throw new AppException("账号或密码错误"); } // 3.验证成功,创建session,保存用户信息 HttpSession session=request.getSession(); // 保存用户信息,和拦截器的键一致 session.setAttribute("user",exist); return null; }
然后生成selectOne方法,并返回 userMapper.selectOne();
public User selectOne(User param) { return userMapper.selectOne(param); }
登录后会创建JSESSIONID及session,返回JSESSIONID(set-Cookie:JSEESION=xxx)
每次请求会自动携带Cookie:JSESSIONID=xxx
实现注销功能只用删除session即可
// GET http://localhost:8080/api/user/logout
@RequestMapping("/logout")
public Object logout(HttpSession session){
session.invalidate();
return null;
}
// 实现数据字典接口: 获取下拉菜单的选项
// GET dict/tag/query?dictionaryKey=000002
@RequestMapping("query")
public Object query(String dictionaryKey){
List tags=dictionaryTagService.query(dictionaryKey);
return tags;
}
public List query(String dictionaryKey) {
return dictionaryTagMapper.queryTagsByKey(dictionaryKey);
}
Mybatis生成工具并未生成这个queryTagsByKey方法,需要自己写(关联查询)
// 获取班级(下拉菜单选项)信息接口
// GET http://localhost:8080/api/classes/queryAsDict
@RequestMapping("/queryAsDict")
public Object queryAsDict(){
List classes=classesService.queryAsDict();
return classes;
}
// 获取学生信息(下拉菜单)
// GET student/queryAsDict?dictionaryKey=2
@RequestMapping("/queryAsDict")
public Object queryAsDict(@RequestParam("dictionaryKey") String classesId){
List students=studentService.queryAsDict(classesId);
return students;
}
// 获取班级信息(表格展示)
// GET /api/classes/query
@RequestMapping("/query")
public Object query(Classes cla){
// 分页功能
PageHelper.startPage(cla);
List classes=classesService.query();
PageHelper.clearPage();
return classes;
}
每个页面都有5个接口 :
- 获取数据(表格展示)
- 新增
- 修改删除
- 获取数据详情
// 新增班级
// POST /api/classes/add
@RequestMapping("/add")
public Object add(@RequestBody Classes classes){
int n=classesService.add(classes);
return null;
}
public int add(Classes classes) {
return classesMapper.insertSelective(classes);
}
// 获取班级详情
// GET http://localhost:8080/api/classes/queryById?id=4
@RequestMapping("/queryById")
public Object queryById(@RequestParam Integer id){
Classes classes=classesService.queryById(id);
return classes;
}
public Classes queryById(Integer id) {
return classesMapper.selectByPrimaryKey(id);
}
// 修改信息
// POST http://localhost:8080/api/classes/update HTTP/1.1
// 抓包
// {"id":"4","classesName":"二年级(1)班","classesGraduateYear":"000001004","classesMajor":"000002001","classesDesc":"无"}
@RequestMapping("/update")
public Object update(@RequestBody Classes classes){
int n=classesService.update(classes);
return null;
}
接收json格式的数据 加@RequestBody注解---(抓包看接收啥数据格式)
// 删除数据
// GET http://localhost:8080/api/classes/delete?ids=2&ids=3
@RequestMapping("/delete")
public Object delete(@RequestParam List ids){
int n=classesService.delete(ids);
return null;
}
public int delete(List ids) {
return classesMapper.deleteByIds(ids);
}
项目到这里就结束了,其他四个页面大家可参考班级管理的代码实现,此外,还可以实现分页功能,搜索功能,忘记密码等等。如果大家想让自己的项目变得丰富起来的话,可以在网上找资料自己实现这些功能。
也可以来问我哦~~