赞
踩
个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~
个人主页:.29.的博客
学习社区:进去逛一逛~
案例实现方案分析:
实体类开发 —— 使用Lombok快速制作实体类
Dao开发 —— 整合MyBatisPlus,制作数据层测试类
Service开发 —— 整合MyBatisPlus进行增量开发,制作业务层测试类
Controller开发 —— 基于Restful开发,使用PostMan测试接口功能
Controller开发 —— 前后端开发协议制作
页面开发 —— 基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
项目异常处理
按条件查询 —— 页面功能调整、Controller修正功能、Service修正功能
1.快速搭建
:
2.勾选需要的起步依赖
:
Lombok
:一个Java类库,提供了一组注解,简化了POJO实体类开发pom.xml导入lombok依赖
:<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
快速利用lombok开发实体类
:@Data注解
:自动创建实体类属性的Getter、Setter方法import lombok.Data;
/**
* @author .29.
* @create 2023-03-27 20:58
*/
@Data
public class Book {
private int id;
private String type;
private String name;
private String description;
}
手动导入springboot未提供勾选的起步依赖
:<!--手动添加MyBatisPlus的起步依赖--> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <!--手动添加Druid数据库连接池的起步依赖--> <!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> </dependencies>
springboot配置文件
:将配置文件修改为yml
格式后,配置相关数据源信息
# 配置端口号 server: port: 8080 # 配置dataSource(数据源) spring: datasource: druid: username: root password: abc123 url: jdbc:mysql://localhost:3306/springboot?useSSL=false&useUnicode=true&CharacterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver # 配置MyBatisPlus # table-prefix:配置表格前缀,避免无法找到表格的问题 # id-type: 配置id类型,设置为auto以配合数据库表格的自增列,若不设置,在新增时不指定自增列会报错 mybatis-plus: global-config: db-config: table-prefix: tbl_ id-type: auto # 开启MyBatisPlus日志功能(注意:配置在mybatis-plus:的下一级) configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Mapper接口
import com.haojin.springboot.springbootssmp.domain.Book;
import org.apache.ibatis.annotations.Mapper;
/**
* @author .29.
* @create 2023-03-27 21:11
*/
@Mapper
public interface BookMapper extends BaseMapper<Book> {
//只需要让Mapper接口继承BaseMapper<>,以实体类类型作为泛型,就能直接使用MyBatisPlus提供好的CRUD方法
}
MyBatisPlus提供的相关方法参考:
MyBatisPlus——分页功能
:分页操作需要设置分页对象IPage
IPage
对象中封装了分页操作的所有数据:
@Test
public void testSelectPage(){
//实现分页功能的测试
IPage page = new Page(1, 5);
bookMapper.selectPage(page,null);
//测试 获取IPage对象中封装的数据:
System.out.println(page.getCurrent());//当前页码
System.out.println(page.getSize()); //每页数据总量
System.out.println(page.getPages()); //最大页码值
System.out.println(page.getTotal()); //数据总量
System.out.println(page.getRecords());//数据
}
只是上述调用MyBatisPlus中提供的分页功能相关的方法还无法真正实现分页功能,MyBatisPlu是通过拦截器来实现分页的,所以需要配置拦截器。
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author .29. * @create 2023-03-27 22:36 */ //拦截器配置类 @Configuration public class MPConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ //创建MP拦截器的容器 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); //往容器中添加需要的拦截器,这里是实现分页功能的拦截器 mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mybatisPlusInterceptor; } }
配置了拦截器,上文测试的分页相关功能就能顺利实现啦~
MyBatisPlus——条件查询功能
:QueryWrapper
对象封装查询条件实现条件查询功能,这里推荐使用LambdaQueryWrapper
对象——所有查询操作封装成方法调用。 @Test
public void testGetByCondition(){
//设置分页规则(第一页,每页五条)
IPage page = new Page(1, 5);
//将需要查询的类型作为泛型,LambdaQueryWrapper封装了所有查询操作,可直接调用相关方法
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
//like方法封装条件进行查询
lqw.like(Book::getName,"Spring");
//查询结果分页
bookMapper.selectPage(page,lqw);
}
注意
:
IService<T>
快速开发ServiceServiceImpl<M,T>
快速开发ServiceImpl业务层接口
:import com.haojin.springboot.springbootssmp.domain.Book; import java.util.List; /** * @author .29. * @create 2023-03-28 19:41 */ //业务层接口 public interface BookService { //增 Boolean save(Book book); //改 Boolean update(Book book); //删 Boolean delete(Integer id); //查 Book getById(Integer id); List<Book> getAll(); //分页 IPage getPage(int currPage,int pageSize); }
接口实现类
:import com.haojin.springboot.springbootssmp.domain.Book; import com.haojin.springboot.springbootssmp.mapper.BookMapper; import com.haojin.springboot.springbootssmp.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * @author .29. * @create 2023-03-28 19:46 */ //业务层接口实现类 @Service public class BookServiceImpl implements BookService { @Autowired private BookMapper bookMapper; //业务层方法 与 数据层方法名 尽量不相同 //新增 @Override public Boolean save(Book book) { return bookMapper.insert(book) > 0; } //修改 @Override public Boolean update(Book book) { return bookMapper.updateById(book) > 0; } //删除 @Override public Boolean delete(Integer id) { return bookMapper.deleteById(id) > 0; } //根据id查询 @Override public Book getById(Integer id) { return bookMapper.selectById(id); } //查询所有 @Override public List<Book> getAll() { return bookMapper.selectList(null); } //分页 @Override public IPage getPage(int currPage, int pageSize) { IPage page = new Page(currPage, pageSize); bookMapper.selectPage(page,null); return page; } }
创建测试类,测试业务层功能是否能正常运行
:import com.haojin.springboot.springbootssmp.domain.Book; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @author .29. * @create 2023-03-28 19:56 */ @SpringBootTest public class testServiceImpl { @Autowired private BookService bookService; @Test public void testSave(){ Book book = new Book(); book.setType("计算机理论"); book.setName("数据结构与算法"); book.setDescription("小白的算法入门推荐书籍"); System.out.println(bookService.save(book)); } @Test public void testUpdate(){ Book book = new Book(); book.setId(8); book.setType("测试更新 类型"); book.setName("测试更新 书名"); book.setDescription("测试更新 简介"); System.out.println(bookService.update(book)); } @Test public void testDelete(){ System.out.println(bookService.delete(8)); } @Test public void testGetById(){ System.out.println(bookService.getById(1)); } @Test public void testGetAll(){ System.out.println(bookService.getAll()); } @Test public void testGetPage(){ IPage page = bookService.getPage(1, 5); System.out.println(page.getCurrent());//当前页码 System.out.println(page.getSize()); //每页数据总量 System.out.println(page.getPages()); //最大页码值 System.out.println(page.getTotal()); //数据总量 System.out.println(page.getRecords());//数据 } }
基于MyBatisPlus快速构建业务层接口
:import com.baomidou.mybatisplus.extension.service.IService; import com.haojin.springboot.springbootssmp.domain.Book; /** * @author .29. * @create 2023-03-28 20:25 */ //业务层快速开发(基于MyBatisPlus构建) public interface IBookService extends IService<Book> { //已经提供了大量通用方法 //不通用的业务层方法继续在这里定义开发即可 //分页 IPage<Book> getPage(int currPage,int pageSize); //条件查询分页 IPage<Book> getPage(int currPage,int pageSize,Book book); }
MyBatisPlus提供的通用方法
:
基于MyBatisPlus快速构建接口实现类
:import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.haojin.springboot.springbootssmp.domain.Book; import com.haojin.springboot.springbootssmp.mapper.BookMapper; import com.haojin.springboot.springbootssmp.service.IBookService; import org.apache.logging.log4j.util.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author .29. * @create 2023-03-28 20:34 */ @Service //基于MyBatisPlus快速构建业务层接口实现类 public class MPBookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService { @Autowired BookMapper bookMapper; //自己定义的 //分页 @Override public IPage<Book> getPage(int currPage, int pageSize) { IPage page = new Page(currPage, pageSize); bookMapper.selectPage(page,null); return page; } //条件查询分页 @Override public IPage<Book> getPage(int currPage, int pageSize,Book book) { LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>(); 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(currPage, pageSize); bookMapper.selectPage(page,lqw); return page; } }
表现层接口功能实现
:import com.baomidou.mybatisplus.core.metadata.IPage; import com.haojin.springboot.springbootssmp.domain.Book; import com.haojin.springboot.springbootssmp.service.IBookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author .29. * @create 2023-03-28 21:00 */ @RestController @RequestMapping(value = "/books") public class BookController { @Autowired private IBookService bookService; //Get方式,表示查询 @GetMapping public List<Book> getAll(){ return bookService.list(); } //Post方式,表示新增 @PostMapping public Boolean save(@RequestBody Book book){ return bookService.save(book); } //Put方式,表示修改 @PutMapping public Boolean update(@RequestBody Book book){ return bookService.updateById(book); } //Delete方式,表示删除 @DeleteMapping("/{id}") public Boolean delete(@PathVariable Integer id){ return bookService.removeById(id); } //Get方式,"/{}"表示传入的参数,根据参数查询 @GetMapping("/{id}") public Book getById(@PathVariable Integer id){ return bookService.getById(id); } //分页 @GetMapping("/{currPage}/{pageSize}") public IPage<Book> getPage(@PathVariable int currPage,@PathVariable int pageSize){ return bookService.getPage(currPage,pageSize); } }
表现层消息一致性处理
:解决方案
:
前后端数据协议
模型类:
import lombok.Data; /** * @author .29. * @create 2023-03-28 22:11 * * 控制层返回结果的模型类,用于后端与前端进行数据格式统一,也可以称为`前后端数据协议` */ //使用lombok快速开发 @Data public class Result { private boolean flag; private Object data; private String msg; public Result(boolean flag){ this.flag = flag; } public Result(boolean flag, Object data) { this.flag = flag; this.data = data; } public Result() { } public Result(String msg){ this.flag = false; this.msg = msg; } public Result(boolean flag,String msg){ this.flag = flag; this.msg = msg; } }
定义SpringMVC异常处理类,让异常信息也以统一的格式获得:
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; /** * @author .29. * @create 2023-03-29 19:14 */ //作为SpringMVC的异常处理器 @RestControllerAdvice public class ProjectExceptionAdvice { //拦截异常信息 @ExceptionHandler public Result doException(Exception ex){ //记录日志 //通知运维 //通知开发 //... //打印异常内容 ex.printStackTrace(); //返回消息 return new Result("服务器故障,请稍后再试!"); } }
消息一致性处理后的表现层接口:
import com.baomidou.mybatisplus.core.metadata.IPage; import com.haojin.springboot.springbootssmp.domain.Book; import com.haojin.springboot.springbootssmp.service.IBookService; import com.haojin.springboot.springbootssmp.utils.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * @author .29. * @create 2023-03-28 21:00 */ @RestController @RequestMapping(value = "/books") public class BookController { @Autowired private IBookService bookService; //Get方式,表示查询 @GetMapping public Result getAll(){ return new Result(true, bookService.list()); } //Post方式,表示新增 @PostMapping public Result save(@RequestBody Book book){ boolean flag = bookService.save(book); return new Result(flag,flag?"添加成功":"添加失败"); } //Put方式,表示修改 @PutMapping public Result update(@RequestBody Book book){ return new Result(bookService.updateById(book)); } //Delete方式,表示删除 @DeleteMapping("/{id}") public Result delete(@PathVariable Integer id){ return new Result(bookService.removeById(id)); } //Get方式,"/{}"表示传入的参数,根据参数查询 @GetMapping("/{id}") public Result getById(@PathVariable Integer id){ return new Result(true,bookService.getById(id)) ; } //分页 @GetMapping("/{currPage}/{pageSize}") public Result getPage(@PathVariable int currPage,@PathVariable int pageSize){ IPage<Book> page = bookService.getPage(currPage, pageSize); //如果当前页码值大于总页码值,重写执行查询操作,使用最大页码值作为当前页码值 if(currPage > page.getPages()){ page = bookService.getPage((int)page.getPages(), pageSize); } return new Result(true,page); } }
小结
:
前后端协议联调
:
books.html
<!DOCTYPE html> <html> <head> <!-- 页面meta --> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>基于SpringBoot整合SSM案例</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> <body class="hold-transition"> <div id="app"> <div class="content-header"> <h1>图书管理</h1> </div> <div class="app-container"> <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> <!--分页组件--> <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> <!-- 新增标签弹层 --> <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> <!-- 编辑标签弹层 --> <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> <!-- 引入组件库 --> <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:6,//每页显示的记录数 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; // }); // }, //分页查询 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); //发送异步请求 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; }); }, //切换页码 handleCurrentChange(currentPage) { //修改页码值为当前选中的页码值 this.pagination.currentPage = currentPage; //执行查询 this.getAll(); }, //弹出添加窗口 handleCreate() { this.dialogFormVisible = true; this.resetForm(); }, //重置表单 resetForm() { this.formData = {}; }, //添加 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(); }); }, //取消 cancel(){ this.dialogFormVisible = false; this.dialogFormVisible4Edit = false; this.$message.info("当前操作取消"); }, // 删除 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("取消操作"); }); }, //弹出编辑窗口 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(); }); }, //修改 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(); }); }, //条件查询 } }) </script> </html>
数据库表格结构
:
需要完整代码的,可以在此下载
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。