基于Spring Boot 2 + Vue的书籍管理系统(2021版,附源码教程)
一个简单的图书管理系统,周期为5天的一个系统,一个可以作为的模板的系统。有一定Java+前端基础的学习者,想快速了解SpringBoot项目框架的学习者和开发者,想回忆起某一技术的开发与运维人员。
目录
1、项目简介
一个简单的图书管理系统,周期为5天的一个系统,一个可以作为的模板的系统。
2、适用人群
有一定Java+前端基础的学习者,想快速了解Spring Boot项目框架的学习者和开发者,想回忆起某一技术的开发与运维人员。
3、使用技术
前端:
Vue+Element-UI+axios
后端:
Spring Boot 2 + MybatisPlus + SpringMVC + Juint + Druid+Lombook
数据库:
MySql5.5+
测试:
Restful+Postman
4、功能简介
图书管理的查询、添加、修改、删除、异常处理与分页操作。
5、工具介绍
若您未使用过 Spring Boot 工程或不会创建 Spring Boot项目,请参考此篇文章,有详细介绍
《Spring Boot开发实践》之Spring Boot 入门_初尘屿风的博客-CSDN博客
Java框架入门——SpringBoot框架_初尘屿风的博客-CSDN博客
(1)mevan项目搭建
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
(2)Spring Boot项目搭建
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
</parent>
(3)Java版本显示
<properties>
<java.version>1.8</java.version>
</properties>
(4)Lombook项目搭建
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
(5)日志配置
spring.main.banner-mode=off
(6)端口设置(三种方式)
1、默认方式
server.port=80
2、.yml:推荐使用
server:
port:
3、.yaml
server:
port:
2与3是有一定的差别,2是主流;
注意事项:
-
大小写敏感
-
属性层级关系使用多行描述,每行结尾使用冒号结束
-
使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
-
属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
-
核心规则:数据前面要加空格与冒号隔开
(7)Junit项目搭建
@SpringBootTest
class Springboot07JunitApplicationTests {
@Autowired
private BookService bookService;
@Test
public void testSave(){
bookService.save();
} }
(8)Mybatis项目搭建(IDEA系统自带)
<artifactId>springboot_05_mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_05_mybatis</name>
<description>Demo project for Spring Boot</description>
<properties>
(9)MybatisPlus项目搭建(手动配置)
<groupId>com.test</groupId>
<artifactId>springboot_06_mybatis_plus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_06_mybatis_plus</name>
<description>Demo project for Spring Boot</description>
<properties>
(10)项目搭建
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
(11)Vue介绍
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。
(12)Element介绍
Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。
(13)Postman介绍
Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
(14)Axios 介绍
Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js。
(15)SpringMVC 介绍
Spring web MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。
(16)Springboot介绍
官网:
Spring | Home
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
(17)……
等等
6、后端相关代码介绍
目录
(1)拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
//作为springmvc的异常处理器
//@ControllerAdvice
@RestControllerAdvice
public class ProjectExceptionAdvice {
//拦截所有的异常信息
@ExceptionHandler(Exception.class)
public R doException(Exception ex){
//记录日志
//通知运维
//通知开发
ex.printStackTrace();
return new R("服务器故障,请稍后再试!");
}
(2)分页操作
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize,Book book){
// System.out.println("参数==>"+book);
IPage<Book> page = bookService.getPage(currentPage, pageSize,book);
//如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
if( currentPage > page.getPages()){
page = bookService.getPage((int)page.getPages(), pageSize,book);
}
return new R(true, page);
}
@Test
void testGetPage(){
IPage page = new Page(1,5);
bookDao.selectPage(page,null);
}
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage,pageSize);
bookDao.selectPage(page,null);
return page;
}
@Override
public IPage<Book> getPage(int currentPage, int pageSize, Book book) {
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Strings.isNotEmpty(book.getType()),Book::getType,book.getType());
lqw.like(Strings.isNotEmpty(book.getName()),Book::getName,book.getName());
lqw.like(Strings.isNotEmpty(book.getDescription()),Book::getDescription,book.getDescription());
IPage page = new Page(currentPage,pageSize);
bookDao.selectPage(page,lqw);
return page;
}
(3)实体类开发
import lombok.Data;
@Data
public class Book {
private Integer id;
private String type;
private String name;
private String description;
}
(4)交互功能
@PostMapping
public R save(@RequestBody Book book) throws IOException {
// R r = new R();
// boolean flag = bookService.save(book);
// r.setFlag(flag);
if (book.getName().equals("123") ) throw new IOException();
boolean flag = bookService.save(book);
return new R(flag, flag ? "添加成功^_^" : "添加失败-_-!");
}
@PutMapping
public R update(@RequestBody Book book) throws IOException {
if (book.getName().equals("123") ) throw new IOException();
boolean flag = bookService.modify(book);
return new R(flag, flag ? "修改成功^_^" : "修改失败-_-!");
}
@DeleteMapping("{id}")
public R delete(@PathVariable Integer id){
return new R(bookService.delete(id));
}
@GetMapping("{id}")
public R getById(@PathVariable Integer id){
return new R(true, bookService.getById(id));
}
(5)数据层开发
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version>
</dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version>
</dependency>
(6)数据库应用
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: 123
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
(7)查询操作
@Test
void testGetByCondition(){
IPage page = new Page(1,10);
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Book::getName,"Spring");
bookDao.selectPage(page,lqw);
}
@Test
void testGetByCondition(){
QueryWrapper<Book> qw = new QueryWrapper<Book>();
qw.like("name","Spring");
bookDao.selectList(qw);
}
支持动态拼写查询条件:
@Test
void testGetByCondition(){
String name = "Spring";
IPage page = new Page(1,10);
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Strings.isNotEmpty(name),Book::getName,"Spring");
bookDao.selectPage(page,lqw);
}
(8)业务层开发
public interface BookService {
boolean save(Book book);
boolean delete(Integer id);
boolean update(Book book);
Book getById(Integer id);
List<Book> getAll();
IPage<Book> getByPage(int currentPage,int pageSize);
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public Boolean save(Book book) {
return bookDao.insert(book) > 0; }
public Boolean delete(Integer id) {
return bookDao.deleteById(id) > 0; }
public Boolean update(Book book) {
return bookDao.updateById(book) > 0; } }
(9)表现层开发
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
@GetMapping
public List<Book> getAll(){
return bookService.list();
} }
初始界面:
测试界面:
(10)前后端数据协议:
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
import lombok.Data;
@Data
public class R {
private Boolean flag;
private Object data;
private String msg;
public R(){}
public R(Boolean flag){
this.flag = flag;
}
public R(Boolean flag,Object data){
this.flag = flag;
this.data = data;
}
public R(Boolean flag,String msg){
this.flag = flag;
this.msg = msg;
}
public R(String msg){
this.flag = false;
this.msg = msg;
}
}
(11)异常处理
@RestControllerAdvice
public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class)
public R doOtherException(Exception ex){
//记录日志
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
ex.printStackTrace();
return new R(false,null,"系统错误,请稍后再试!");
} }
//添加
handleAdd () {
//发送ajax请求
axios.post("/books",this.formData).then((res)=>{
//如果操作成功,关闭弹层,显示数据
if(res.data.flag){
this.dialogFormVisible = false;
this.$message.success("添加成功");
}else {
this.$message.error(res.data.msg);
}
}).finally(()=>{
this.getAll();
});
},
@PostMapping
public R save(@RequestBody Book book) throws IOException {
Boolean flag = bookService.insert(book);
return new R(flag , flag ? "添加成功^_^" : "添加失败-_-!"); }
目的:国际化
7、前端相关代码介绍
目录:
(1)头部定义
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>SpringBoot2案例</title>
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/style.css">
</head>
(2)盒子定义
<div class="box">
<div class="filter-container">
<el-input placeholder="图书类别" v-model="pagination.type" style="width: 200px;" class="filter-item"></el-input>
<el-input placeholder="图书名称" v-model="pagination.name" style="width: 200px;" class="filter-item"></el-input>
<el-input placeholder="图书描述" v-model="pagination.description" style="width: 200px;" class="filter-item"></el-input>
<el-button @click="getAll()" class="dalfBut">查询</el-button>
<el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>
</div>
<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
<el-table-column type="index" align="center" label="序号"></el-table-column>
<el-table-column prop="type" label="图书类别" align="center"></el-table-column>
<el-table-column prop="name" label="图书名称" align="center"></el-table-column>
<el-table-column prop="description" label="描述" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
(3)分页组件
<!--分页组件-->
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
(4)新增标签弹层
<!-- 新增标签弹层 -->
<div class="add-form">
<el-dialog title="新增图书" :visible.sync="dialogFormVisible">
<el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel()">取消</el-button>
<el-button type="primary" @click="handleAdd()">确定</el-button>
</div>
</el-dialog>
</div>
(5)编辑标签弹层
<!-- 编辑标签弹层 -->
<div class="add-form">
<el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit">
<el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel()">取消</el-button>
<el-button type="primary" @click="handleEdit()">确定</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</body>
(6)引入组件库
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data:{
dataList: [],//当前页要展示的列表数据
dialogFormVisible: false,//添加表单是否可见
dialogFormVisible4Edit:false,//编辑表单是否可见
formData: {},//表单数据
rules: {//校验规则
type: [{ required: true, message: '图书类别为必填项', trigger: 'blur' }],
name: [{ required: true, message: '图书名称为必填项', trigger: 'blur' }]
},
pagination: {//分页相关模型数据
currentPage: 1,//当前页码
pageSize:10,//每页显示的记录数
total:0,//总记录数
type: "",
name: "",
description: ""
}
},
//钩子函数,VUE对象初始化完成后自动执行
created() {
//调用查询全部数据的操作
this.getAll();
},
methods: {
//列表
// getAll() {
// //发送异步请求
// axios.get("/books").then((res)=>{
// // console.log(res.data);
// this.dataList = res.data.data;
// });
// },
(7)分页查询
//分页查询
getAll() {
//组织参数,拼接url请求地址
// console.log(this.pagination.type);
param = "?type="+this.pagination.type;
param +="&name="+this.pagination.name;
param +="&description="+this.pagination.description;
// console.log(param);
(8)发送异步请求
//发送异步请求
axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize+param).then((res)=>{
this.pagination.pageSize = res.data.data.size;
this.pagination.currentPage = res.data.data.current;
this.pagination.total = res.data.data.total;
this.dataList = res.data.data.records;
});
(9)切换页码
//切换页码
handleCurrentChange(currentPage) {
//修改页码值为当前选中的页码值
this.pagination.currentPage = currentPage;
//执行查询
this.getAll();
},
(10)弹出添加窗口
//弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
this.resetForm();
},
this.$message.success("修改成功");
}else{
this.$message.error("修改失败");
}
}).finally(()=>{
//2.重新加载数据
this.getAll();
});
(11)重置表单
//重置表单
resetForm() {
this.formData = {};
},
(12)添加
//添加
handleAdd () {
axios.post("/books",this.formData).then((res)=>{
//判断当前操作是否成功
if(res.data.flag){
//1.关闭弹层
this.dialogFormVisible = false;
this.$message.success(res.data.msg);
}else{
this.$message.error(res.data.msg);
}
}).finally(()=>{
//2.重新加载数据
this.getAll();
});
},
(13)取消
//取消
cancel(){
this.dialogFormVisible = false;
this.dialogFormVisible4Edit = false;
this.$message.info("当前操作取消");
},
(14)删除
// 删除
handleDelete(row) {
// console.log(row);
this.$confirm("此操作永久删除当前信息,是否继续?","提示",{type:"info"}).then(()=>{
axios.delete("/books/"+row.id).then((res)=>{
if(res.data.flag){
this.$message.success("删除成功");
}else{
this.$message.error("数据同步失败,自动刷新");
}
}).finally(()=>{
//2.重新加载数据
this.getAll();
});
}).catch(()=>{
this.$message.info("取消操作");
});
},
(15)弹出编辑窗口
//弹出编辑窗口
handleUpdate(row) {
axios.get("/books/"+row.id).then((res)=>{
if(res.data.flag && res.data.data != null ){
this.dialogFormVisible4Edit = true;
this.formData = res.data.data;
}else{
this.$message.error("数据同步失败,自动刷新");
}
}).finally(()=>{
//2.重新加载数据
this.getAll();
});
},
(16)修改
//修改
handleEdit() {
axios.put("/books",this.formData).then((res)=>{
//判断当前操作是否成功
if(res.data.flag){
//1.关闭弹层
this.dialogFormVisible4Edit = false;
this.$message.success("修改成功");
}else{
this.$message.error("修改失败");
}
}).finally(()=>{
//2.重新加载数据
this.getAll();
});
比较详细,以为后来编辑其他项目打基础,做备份。
8、项目展示及相关测试
界面:
跳转页面:
删除数据:
新增数据:
查询数据:
9、项目打包,
可以在无IDEA等环境下运行
基础操作完成。
页面比较简单,但技术很新,有巨大潜在价值,当然作为一个基础与备份的后台系统,足够了。
若您发现了错误,还请不吝赐教,谢谢。
最后,若您也也需要此系统,关注公众号:计算机基础与编程
主页回复“基础项目”;
如果您觉得文章不错,可以点赞+收藏+转发
更多推荐
所有评论(0)