赞
踩
Spring Web + MySQL Driver + Dev Tools添加后三个选项
修改配置文件为yml格式
端口设置为80 方便访问
手动导入Mybatis Plus 和 Druid连接池
这里遇到一个问题,就是修改MP或druid版本号会报错?

开发实体类
@Data替代了get/set方法,toString方法,hasCode方法,equals方法等

主流的数据层技术:Mybatis、MP(Mybatis Plus)、Hibernate
(1)导入MP和Druid对应的依赖(第一步已完成)
完成druid的配置:
yml中输入data然后提示中选择driver-class-name,然后前面添加druid即可

然后再对MP的表前缀进行配置(Mybatis plus完成配置):
- mybatis-plus:
- global-config:
- db-config:
- table-prefix: tbl_
(2)然后创建数据层接口:
New新建 dao.BookDao接口
- @Mapper
- public interface BookDao extends BaseMapper<Book> {
- }
- //泛型 填写对应的实体类
然后在Test文件夹下,建立相应dao的测试类,类上添加注解:@SpringBootTest
New dao.BookDaoTestCase

在这个测试类中测试了CRUD:
- import com.bo.pojo.Book;
- import lombok.AllArgsConstructor;
- import org.junit.jupiter.api.Test;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
-
- /**
- *
- * @date 2022/4/26 - 14:34
- */
- @SpringBootTest
- public class BookDaoTestCase {
- @Autowired
- private BookDao bookDao;
-
- //查询操作
- //根据id查询
- @Test
- void testGetById(){
- System.out.println(bookDao.selectById(2));
- }
-
-
- //增加记录
- //不设置id字段会报错,mybatis系统异常,MP默认生成id策略由他自己设置,雪花算法
- //我们想要使用表的自增策略,需要在yml中配置 id-type: auto
- @Test
- void testSave(){
- Book book = new Book();
- book.setType("测试数据123");
- book.setName("测试数据123");
- book.setDescription("测试数据123");
- bookDao.insert(book);
- }
-
- //更新操作,修改操作
- @Test
- void testUpdate(){
- Book book = new Book();
- book.setId(2);
- book.setType("测试数据Update");
- book.setName("测试数据Update");
- book.setDescription("测试数据Update");
- bookDao.updateById(book);
- }
-
- //删除
- @Test
- void testDelete(){
- bookDao.deleteById(2);
- }
-
- //查询所有的数据
- //所有查询都以select开头
- @Test
- void testGetAll(){
- System.out.println(bookDao.selectList(null));
- }
- }

(3)在yml文件中开启MP运行日志:
- mybatis-plus:
- global-config:
- db-config:
- table-prefix: tbl_
- id-type: auto
- configuration:
- log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

辅助debug,服务器上线时不要开启!!!
(4)实现分页查询
IPage是一个接口,Page是它的实现类,新建IPage接口对象,第一个参数为第几页(从1开始),第二个参数介绍每页有多少行记录(从1开始)
page对象中封装了分页操作中的所有数据:
数据、当前页码、每页数据总量、最大页码值、数据总量
最主要的是添加MP的拦截器!!!才能实现分页功能(也就是为sql语句拼接上limit)
- @Test
- void testGetPage(){
- //IPage是一个接口
- //哪一页的数据,一页显示多少数据(1,5) 第一页,一页显示5条
- //分页功能实现 select * from tbl_book limit ?.?
- //分页若想使用,必须使用拦截器实现
- //返回值还是IPage对象 page
- //返回的数据存在了page对象内
- IPage page = new Page(2,5);
- bookDao.selectPage(page,null);
- //需要调用page对象的方法取出
- System.out.println(page.getCurrent());
- System.out.println(page.getSize());
- System.out.println(page.getTotal());
- System.out.println(page.getPages());
- System.out.println(page.getRecords());
- }

MP的分页拦截器:
New config.MPConfig
功能就是为SQL语句拼接部分内容 limit ....
- @Configuration
- public class MPConfig {
-
- //交给spring一个bean
- //bean是MP的拦截器
- //这个拦截器里面里面添加了PaginationInnerInterceptor,添加了一个分页的子拦截器
- @Bean
- public MybatisPlusInterceptor mybatisPlusInterceptor(){
- MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
- return interceptor;
- }
- }
上面的所有方法都可以添加查询条件!
- @Test
- void testGetBy(){
- //QueryWrapper<>就是查询条件 泛型可写可不写QueryWrapper与QueryWrapper<>均可
- //设置条件
- //like 包括 就是sql中的like
- //eq 等于
- //ne 不等于
- //lt 小于
- //...
- // QueryWrapper<Book> qw = new QueryWrapper<Book>();
- //问题一:
- // "name"是手写的,假如写错了怎么解决,属性容易写错
- // qw.like("name","Spring"); name写错了会出错
- //解决方法:
- // qw.like(Book::getName(),name);
- // 调用Book的getName方法
- //问题二:
- //如果name为null,会直接把null当作"null"拼接
- //方法一:if判断再连接 if(name!=null) lqw.like(Book::getName,name);
- //方法二:lqw.like(name!=null,Book::getName,name);
- String name = null;
- LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
- lqw.like(name!=null,Book::getName,name);
- bookDao.selectList(lqw);
- }

业务层接口关注的是业务的名称,login(String username, String password)适合业务层的登录业务,而相同功能的selectByUserNamePassword(String username, String password)适合数据层接口。
如果是业务方法,要定义为业务名,如果只是一般的增删改查,就写增删改查即可。
创建业务层接口-->创建业务层实现类-->在实现的方法调用dao中的方法(而该方法又是BaseMapper定义好的)
New service.BookService 接口
- /**
- * @date 2022/4/26 - 16:35
- */
- //业务方法就叫业务名login()
- //一些操作就叫操作名
- public interface BookService {
-
- Boolean save(Book book);
- Boolean update(Book book);
- Boolean delete(Integer id);
- Book getById(Integer id);
- List<Book> getAll();
- IPage<Book> getPage(int currentPage, int pageSize);
- }
定义完业务层接口,接着创建实现类,在service包下新建impl包,在该包下面:
New BookServiceImpl类
- /**
- * @date 2022/4/26 - 16:39
- */
- @Service //定义为业务层对应的bean
- public class BookServiceImpl implements BookService {
- @Autowired
- private BookDao bookDao;
-
- @Override
- public Boolean save(Book book) {
- //大于0操作成功
- //小于=0操作失败
- return bookDao.insert(book) > 0;
- }
-
- @Override
- public Boolean update(Book book) {
- return bookDao.updateById(book) > 0;
- }
-
- @Override
- public Boolean delete(Integer id) {
- return bookDao.deleteById(id) > 0;
- }
-
- @Override
- public Book getById(Integer id) {
- return bookDao.selectById(id);
- }
-
- @Override
- public List<Book> getAll() {
- return bookDao.selectList(null);
- }
-
- @Override
- public IPage<Book> getPage(int currentPage, int pageSize) {
- IPage page = new Page(currentPage,pageSize);
- return bookDao.selectPage(page,null);
- }

下面进行测试:
Test文件夹下 New service.BookServiceTestCase
- /**
- * @date 2022/4/26 - 16:44
- */
- @SpringBootTest
- public class BookServiceTestCase {
-
- @Autowired
- private BookService bookService;
- @Test
- void getById(){
- System.out.println(bookService.getById(4));
- }
- @Test
- void testSave(){
- Book book = new Book();
- book.setType("测试数据123");
- book.setName("测试数据123");
- book.setDescription("测试数据123");
- System.out.println(bookService.save(book));
- }
-
- //更新操作
- @Test
- void testUpdate(){
- Book book = new Book();
- book.setId(3);
- book.setType("id2AAAA");
- book.setName("测试数据Update");
- book.setDescription("测试数据Update");
- System.out.println(bookService.update(book));
- }
-
- //删除
- @Test
- void testDelete(){
- System.out.println(bookService.delete(3));
- }
-
- //查询所有的数据
- //所有查询都以select开头
- @Test
- void testGetAll(){
- System.out.println(bookService.getAll());
- }
-
- @Test
- void testGetPage(){
- IPage<Book> page = bookService.getPage(2, 5);
- //需要调用page对象的方法取出
- System.out.println(page.getCurrent());
- System.out.println(page.getSize());
- System.out.println(page.getTotal());
- System.out.println(page.getPages());
- System.out.println(page.getRecords());
- }
- }

快速开发方案:使用MyBatisPlus提供业务层通用接口(IService<T>)与业务层通用实现类(ServiceImpl<M,T>),在通用类基础上做功能重载或功能追加,注意重载时不要覆盖原始操作,避免原始提供的功能丢失。
第一步--创建service接口,继承IServic<xxx> xxx填写对应的实体类

第二步--实现类定义

在该层完成表现层处理:
- @RestController
- @RequestMapping("/books")
- public class BookController {
- @Autowired
- private IBookService iBookService;
-
- @GetMapping
- public List<Book> getAll(){
- return iBookService.list();
- }
-
- //传的是json数据
- @PostMapping
- public Boolean save(@RequestBody Book book){
- return iBookService.save(book);
- }
-
- @PutMapping
- public Boolean update(@RequestBody Book book){
- return iBookService.modify(book);
- }
-
- @DeleteMapping("{id}")
- public Boolean delete(@PathVariable Integer id){
- return iBookService.removeById(id);
- }
-
- @GetMapping("{id}")
- public Book getById(@PathVariable Integer id){
- return iBookService.getById(id);
- }
-
- @GetMapping("{currentPage}/{pageSize}")
- public IPage<Book> getPage(@PathVariable int currentPage, @PathVariable int pageSize){
- return iBookService.getPage(currentPage,pageSize);
- }
- }

但是返回给前端的数据格式多种多样,前端处理起来很麻烦,所以需要统一一下后端的返回格式:

这里在utils包下面新增R类(Result),作为我们返回值的统一格式;
然后对Controller做修改:
- @RestController
- @RequestMapping("/books")
- public class BookController2 {
- @Autowired
- private IBookService iBookService;
-
- @GetMapping
- public R getAll(){
- return new R(true,iBookService.list());
- }
-
- //传的是json数据
- @PostMapping
- public R save(@RequestBody Book book){
- return new R(iBookService.save(book));
- }
-
- @PutMapping
- public R update(@RequestBody Book book){
- return new R(iBookService.modify(book));
- }
-
- @DeleteMapping("{id}")
- public R delete(@PathVariable Integer id){
- return new R(iBookService.removeById(id));
- }
-
- @GetMapping("{id}")
- public R getById(@PathVariable Integer id){
- return new R(true, iBookService.getById(id));
- }
-
- @GetMapping("{currentPage}/{pageSize}")
- public R getPage(@PathVariable int currentPage, @PathVariable int pageSize){
- return new R(true,iBookService.getPage(currentPage,pageSize));
- }
- }

前后端分离结构设计中页面归属前端服务器,这里暂时采用了单体工程
单体工程中页面放置在resour目录下的static目录中(建议执行clean)

在vue的数据模型中,dataList存储当前页要展示的列表数据;下面两个控制表单是否可见,formData用来记录表单数据,修改和新增用同一个表单数据。
导入前端页面后,然后在vue页面中利用axios发起异步请求,这里测试能否发起一个请求,在钩子函数created()中调用getAll()方法,刷新页面时自动执行。axios.xxx()发送请求,then接受数据后做处理。


首先要弹出添加对话框,同时还要清除formData里面的旧数据

然后,点击确定之后,提交表单数据到后台,实现添加功能(当前操作失败的时候,不要关闭对话框):

点击取消按钮,会关闭添加或编辑窗口:


相当于列表功能+新增功能组合,首先要弹出编辑窗口,并且要将准备修改的数据展示出来:

然后修改之后,提交到后台接口,确定提交:

如果后台出现异常,则会产生另一种消息格式给前端:

尽管代码出异常,我们也要返回给前端统一的格式,在springMVC中提供的专用的异常处理器:
可以使用ControllerAdvice注解或者RestControllerAdvice注解;
运行处异常之后,被异常拦截器拦截,然后返回一个R对象,所以修改R对象,添加一个msg消息:

R.class
- @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;
- }
- }

现在页面上的消息,有在页面写的,还有在代码上写的,所以需要统一在后端添加:

现在所有的消息统一由后台处理,保存消息前端展示,前端使用res.data.msg使用即可,方便后面进行国际化。


直接在getAll()方法里面改:
点击修改页码,响应换页操作:

某一页只有最后一条数据,删除之后依然在本页面,如何解决?如果要查看页码大于总页码值,重新执行查询操作,使用最大页码值作为当前页码值,修改Controller:

在执行分页的时候这些条件都得带走:
这几个数据在哪?

这三个数据是跟着分页走的,每次分页查询这三个条件都得带走,所以绑定在分页数据模型中:

修改分页查询操作,

修改Controller接口,但是缺少getPage(currentPage,pageSize, book)接口,所以需要添加

修改service实现类:

对于条件查询,把它作为分页中的一部分数据,当前页码值和size是条件,type和name等也是查询条件。请求的路径参数,直接写参数(对应名称)就能接,如果有对应的实体类,直接封装为实体类。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。