• 【基础篇】五、基于SpringBoot来整合SSM的案例(上)


    接下来在SpringBoot下,把Spring、SpringMVC、MyBatis整合在一起,来实现一个简单的增删改查。

    请添加图片描述

    0、创建模块

    创建新模块,勾选spring-web(SpringMVC)以及MySQL的驱动,再手动导入MyBatisPlus以及Druid的依赖坐标:

    在这里插入图片描述

    1、实体类的快速开发Lombok

    Lombok,一个Java类库,提供了一组注解,用来简化POJO实体类开发。

    
    <dependency>
    	<groupId>org.projectlombokgroupId>
    	<artifactId>lombokartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    表结构:

    在这里插入图片描述

    实体类:

    @Data
    public class Book {
    	private Integer id;
    	private String type;
    	private String name;
    	private String description;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    @Data包括了get/set方法,toString方法,hashCode方法,equals方法等,但不包括有参和无参构造。

    2、数据层开发(CRUD)

    导入MyBatisPlus与Druid对应的starter:

    <dependency>
    	<groupId>com.baomidougroupId>
    	<artifactId>mybatis-plus-boot-starterartifactId>
    	<version>3.4.3version>
    dependency>
    
    <dependency>
    	<groupId>com.alibabagroupId>
    	<artifactId>druid-spring-boot-starterartifactId>
    	<version>1.2.6version>
    dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    配置数据源与MyBatisPlus对应的基础配置

    spring:
      datasource:
        druid:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ssm_db?servierTimezone=UTC
          username: root
          password: root
    mybatis-plus:
      global-config:
        db-config:
          table-prefix: t_ # 表名和实体类相比,表名的前缀,如此就不用再实体类中加注解指定表名了
          id-type: auto # id生成策略使用数据库自增策略(和我数据库中设置的保持一致)
    			        # 否则插入数据时MP使用雪花算法生成一个id
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    继承BaseMapper并指定泛型:

    //要么在mapper中加@Mapper,要么去启动类加@MapperScan
    @Mapper  
    public interface BookDao extends BaseMapper<Book> {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    为方便调试可以开启MyBatisPlus的日志,StdOutImpl即标准输出,即打印到控制台上

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    
    • 1
    • 2
    • 3
    • 4

    单元测试看效果:

    @SpringBootTest
    public class BookDaoTest {
    
    	@Autowired
    	private BookDao bookDao;
    	
    	@Test
    	void testSave() {
    		Book book = new Book();
    		book.setName("测试数据");
    		book.setType("测试类型");
    		book.setDescription("测试描述数据");
    		bookDao.insert(book);
    	}
    	...
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3、分页

    分页操作需要设定分页对象IPage,其实现类为Page

    @Test
    void testGetPage(){
    	IPage<Book> page = new Page<>(1,5);
    	bookDao.selectPage(page,null);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    IPage对象中封装了分页操作中的所有数据,包括:

    • 数据
    • 当前页码值
    • 每页数据总量
    • 最大页码值
    • 数据总量

    分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,使用MyBatisPlus拦截器实现。

    @Configuration
    public class MpConfig {
    	@Bean
    	public MybatisPlusInterceptor mpInterceptor() {
    		//1.定义Mp拦截器
    		MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
    		//2.添加具体的拦截器
    		mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
    		return mpInterceptor;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4、条件查询

    关于条件查询:

    • 使用QueryWrapper对象封装查询条件,此时传的table的列名
    • 推荐使用LambdaQueryWrapper对象来封装查询条件
    • 查询条件支持动态条件拼装(方法的重载)

    在单元测试中分别演示下:

    @Test
    void testGetByCondition(){
    	QueryWrapper<Book> qw = new QueryWrapper<>();
    	qw.like("name","Spring");
    	bookDao.selectList(qw);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    实际开发,从前端获取的搜索关键字,如果为null而不过滤,则 where name like %null%,因此:

    @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");  //判空,为空则不拼SQL
    	bookDao.selectPage(page,lqw);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    到此,数据层的开发结束。

    5、业务层的标准开发

    写业务层之前,说下Mapper层和Service层接口方法名的命名,Mapper要起的偏数据库,Service则偏业务,Mapper接口的方法名看完就知道大概的SQL咋写的,Service层看方法名就知道是做啥业务的,以登录为例,如:

    //Mapper
    selectByUserNameAndPassword(String username,String password);
    
    • 1
    • 2
    //Service
    login(String username,String password);
    
    • 1
    • 2

    Service层接口定义:

    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);
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    写实现类:

    @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;
    	}
    	
    	public Book getById(Integer id) {
    		return bookDao.selectById(id);
    	}
    	
    	public List<Book> getAll() {
    		return bookDao.selectList(null);
    	}
    	
    	public IPage<Book> getByPage(int currentPage, int pageSize) {
    		IPage page = new Page<Book>(currentPage,pageSize);
    		return bookDao.selectPage(page,null);
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    6、业务层的快速开发(基于MyBatisPlus)

    继承IService接口,泛型传入实体类,即可有上面标准开发的那些方法。另外你业务需要的定制的方法,自己接着写就行。

    在这里插入图片描述

    此时的实现类:注意泛型中的M和T,M传Mapper类型,T是实体类

    @Service
    public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {
    }
    
    
    • 1
    • 2
    • 3
    • 4

    除了基础的MP提供的方法,其余你的定制业务方法,自己加就好:

    @Service
    public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {
    
    	@Autowired
    	private BookDao bookDao;
    	
    	public Boolean insertSome(Book book) {
    		return bookDao.insert(book) > 0;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    总结就是:

    • 使用MyBatisPlus提供有业务层通用接口(ISerivce)与业务层通用实现类(ServiceImpl
    • 在通用类基础上做功能重载或功能追加
    • 注意重载时不要覆盖原始操作,避免原始提供的功能丢失

    7、表现层开发

    基于Restful进行表现层接口开发:

    @RestController
    @RequestMapping("/books")
    public class BookController {
    
    	@Autowired
    	private IBookService bookService;
    	
    	@GetMapping
    	public List<Book> getAll(){
    		return bookService.list();
    	}
    
    	@PostMapping
    	public Boolean save(@RequestBody Book book){
    		return bookService.insert(book);
    	}
    	
    	@PutMapping
    	public Boolean update(@RequestBody Book book){
    		return bookService.modify(book);
    	}
    	
    	@DeleteMapping("/{id}")
    	public Boolean delete(@PathVariable Integer id){
    		return bookService.delete(id);
    	}
    
    	@GetMapping("/{currentPage}/{pageSize}")
    	public List<Book> getAll(@PathVariable Integer currentPage,@PathVariable Integer pageSize){
    		return bookService.getPage(currentPage,pageSize).getRecords();
    	}
    		
    }
    
    //接口功能实现,但返回结果不统一
    //待续...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    这里遇到个坑,注入Service层的Bean的时候,发现有两个可用的Bean而启动失败,参考【解决:缩小Mapper扫描范围,避免产生不必要的Bean

    8、表现层数据一致性:统一结果类R

    设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议。说白了,找个盛饭的碗,查出来的数据就是饭,找个统一结果类来装一下。

    //属性自己决定,平时用msg和data,这个flag看的我别扭
    @Data
    public class R{
    
    	private Boolean flag;
    	
    	private Object data;
    	
    	public R(){
    	}
    	
    	public R(Boolean flag){
    		this.flag = flag;
    	}
    	
    	public R(Boolean flag,Object data){
    		this.flag = flag;
    		this.data = data;
    	}
    	//也可以定义个静态方法,里面封装构造方法
    	public static R success(Object data){
    		return new R(ture,data)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    改下Controller层的返回类型和逻辑:

    @RestController
    @RequestMapping("/books")
    public class BookController {
    
    	@Autowired
    	private IBookService bookService;
    	
    	...
    	@GetMapping
    	public R getAll(){
    		List<Book> bookList = bookService.list();
    		return new R(true ,bookList);
    	}
    	
    	@GetMapping("/{currentPage}/{pageSize}")
    	public R getAll(@PathVariable Integer currentPage,@PathVariable Integer pageSize){
    		IPage<Book> page = bookService.getPage(currentPage, pageSize);
    		return new R(true,page);
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    01-Spring底层核心原理解析
    React Native调用Android的原生功能
    cgroup driver: cgroupfs还是systemd
    MySQL+Java实现父子级联查询
    JAVA计算机毕业设计在线直播平台Mybatis+源码+数据库+lw文档+系统+调试部署
    springmvc异常处理解析#ExceptionHandlerExceptionResolver
    openGauss学习笔记-239 openGauss性能调优-SQL调优-调优流程
    小杨哥陷入打假风波,会变成下一个辛巴吗?
    追寻上古文明毕业季研学营
    基于51单片机的多机交互串口通信Proteus仿真
  • 原文地址:https://blog.csdn.net/llg___/article/details/132943698