1. 环境准备

1.1 数据库准备

  1. -- 创建数据库
  2. CREATE database i_boot;
  3. -- 创建表
  4. DROP TABLE IF EXISTS `t_user`;
  5. CREATE TABLE `t_user` (
  6. `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  7. `name` varchar(255) NOT NULL,
  8. `age` tinyint(4) unsigned DEFAULT NULL,
  9. `email` varchar(255) DEFAULT NULL,
  10. PRIMARY KEY (`id`)
  11. ) ENGINE=InnoDB AUTO_INCREMENT=2;
  12. INSERT INTO `t_user` VALUES ('1', 'jack', '18', 'jack@126.com');

1.2 创建项目

image.png
不选择任何依赖,手动导入:
image.png
image.png

1.3 数据源配置

pom.xml 文件添加基本依赖:

<!-- mysql驱动 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- 数据库连接池 -->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.2.4</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

application.yml 配置数据库信息:

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/i_boot?serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver

1.4 日志配置

application.yml 添加如下信息:

logging:
  pattern:
    console: "%m %n"
  level:
    com.imcode: debug
    com.alibaba: error

2. 整合 MyBatis-plus

4.1 MyBatis-plus 简介

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
官网:https://baomidou.com/
image.png

基友搭配,效率翻倍

4.2 依赖配置

<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.5.2</version>
</dependency>

4.3 Mapper 接口扫描配置

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 接口:

package com.imcode;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.imcode.*.mapper")
public class IBootMybatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(IBootMybatisPlusApplication.class, args);
    }

}

4.4 Mapper 映射文件配置

在 application.yml 文件中配置Mapper映射文件位置。

# 该配置可以省略,默认MyBatis-plus读取的配置文件就是这个位置
# classpath: 表示当前工程类路径下的资源
# classpath*: 表示当前工程类路径下的资源 + 工程引入的其它jar中的资源
# 多模块项目开发时候,建议使用classpath*

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml

3. 快速入门

image.png

3.1 编写实体类 User.java

package com.imcode.sys.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

@TableName("t_user")
public class User {
    // 主键自增
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    // 属性名称和数据库字段名称一样,该注解可以省略
    @TableField(value = "name")
    private String name;
    private Integer age;
    private String email;
    .......
    }
}

3.2 编写 UserMapper 接口

继承 MyBatis-plus 的 BaseMapper 接口。

package com.imcode.sys.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.imcode.sys.entity.User;

public interface UserMapper extends BaseMapper<User> {

}

3.3 基本 CRUD

package com.imcode.sys.mapper;

import com.imcode.sys.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void insert() {
        User user = new User();
        user.setName("test");
        user.setEmail("test@126.com");
        user.setAge(18);
        userMapper.insert(user);
    }

    @Test
    void selectById() {
        System.out.println(userMapper.selectById(3));
    }

    @Test
    void updateById() {
        User user = userMapper.selectById(3);
        user.setName("test007");
        userMapper.updateById(user);
    }

    @Test
    void deleteById() {
        userMapper.deleteById(3);
    }
}

3.5 常用查询

MyBatisPlus 将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。
Wrapper 类,这个类就是用来构建查询条件的,如下图所示:
image.png

构造查询条件

在进行查询的时候,我们的入口是在 Wrapper 这个类上,因为它是一个接口,所以我们需要去找它对应的实现类,关于实现类也有很多,有多种构建查询条件对象的方式:
image.png
QueryWrapper 和 LambdaQueryWrapper 基本使用:
查询年龄小于20岁的所有用户:

@Test
void query01() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // SELECT id,name,age,email FROM t_user WHERE (age < ?)
    wrapper.lt("age",20);
    List<User> list = userMapper.selectList(wrapper);
    System.out.println(list);
}

@Test
void query02() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // SELECT id,name,age,email FROM t_user WHERE (age < ?)
    wrapper.lt(User::getAge,20);
    List<User> list = userMapper.selectList(wrapper);
    System.out.println(list);
}

多条件查询

and:查询年龄在15 到 20 之间的用户

// 查询年龄在15 到 20 之间的用户,包括15岁和20岁
@Test
void test03() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // SELECT id,name,age,email FROM t_user WHERE (age <= 20 AND age >= 15)
    // wrapper.le(User::getAge,20).ge(User::getAge,15);

    // SELECT id,name,age,email FROM t_user WHERE (age BETWEEN ? AND ?)
    wrapper.between(User::getAge,15,20);

    List<User> list = userMapper.selectList(wrapper);
    for (User user : list) {
        System.out.println(user);
    }
}

or:查询年龄等于18岁,或者 用户名 包含 rose 的用户

@Test
void query04() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // SELECT id,name,age,email FROM t_user WHERE (age > ? OR name = ?) 
    wrapper.gt(User::getAge,17).or().eq(User::getName,"rose");
    List<User> list = userMapper.selectList(wrapper);
    System.out.println(list);
}

动态 SQL

image.png
两个都不输入查询所有
两个都输入是and关系

@Test
void query05() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    User param = new User();
    param.setName("jack");
    param.setEmail("jack@126.com");
    wrapper
        .like(param.getName() != null, User::getName, param.getName())
        .like(param.getEmail() != null, User::getEmail, param.getEmail());
    List<User> list = userMapper.selectList(wrapper);
    System.out.println(list);
}

查询指定字段

@Test
void query06() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // SELECT name,email FROM t_user WHERE (age BETWEEN ? AND ?) 
    wrapper.select(User::getName,User::getEmail);
    wrapper.between(User::getAge,10,20);
    List<User> list = userMapper.selectList(wrapper);
    System.out.println(list);
}

聚合查询

需求:聚合函数查询,完成count、max、min、avg、sum的使用
count:总记录数
max:最大值
min:最小值
avg:平均值
sum:求和

@Test
void query07() {
    QueryWrapper<User> wrapper = new QueryWrapper<User>();
    // SELECT count(*),max(age),min(age),sum(age),avg(age) FROM t_user
    wrapper.select("count(*),max(age),min(age),sum(age),avg(age)");
    List<Map<String, Object>> userList = userMapper.selectMaps(wrapper);
    System.out.println(userList);
}

模糊查询

@Test
void query08() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // SELECT id,name,age,email FROM t_user WHERE (name LIKE ?) 
    wrapper.like(User::getName,"jack");
    List<User> list = userMapper.selectList(wrapper);
    System.out.println(list);
}
  • like(): 前后加百分号,如 %jack%
  • likeLeft():前面加百分号,如 %jack
  • likeRight():后面加百分号,如 jack%

    排序

    @Test
    void query09() {
      LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    
      /**
           * condition :条件,返回boolean 
           * 当condition为true,进行排序,如果为false,则不排序
           * isAsc:是否为升序,true为升序,false为降序
           * columns:需要操作的列
           */
      // SELECT id,name,age,email FROM t_user ORDER BY age ASC
      wrapper.orderBy(true,true,User::getAge);
      List<User> list = userMapper.selectList(wrapper);
      System.out.println(list);
    }
    
  • orderBy排序

    • condition:条件,true则添加排序,false则不添加排序
    • isAsc:是否为升序,true升序,false降序
    • columns:排序字段,可以有多个
  • orderByAsc/Desc(单个column):按照指定字段进行升序/降序
  • orderByAsc/Desc(多个column):按照多个字段进行升序/降序
  • orderByAsc/Desc
    • condition:条件,true添加排序,false不添加排序
    • 多个columns:按照多个字段进行排序

除了上面介绍的这几种查询条件构建方法以外还会有很多其他的方法,比如isNull,isNotNull,in,notIn等等方法可供选择,具体参考官方文档的条件构造器来学习使用,具体的网址为:
https://mp.baomidou.com/guide/wrapper.html#abstractwrapper

3.4 分页查询

分页插件配置

添加配置类:
image.png

package com.imcode.config;

import com.baomidou.mybatisplus.annotation.DbType;
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;

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor
            .addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

分页实现

@Test
void selectPage() {
    //1 创建IPage分页对象,设置分页参数,1为当前页码,3为每页显示的记录数
    IPage<User> page = new Page<>(1, 3);
    //2 执行分页查询
    userMapper.selectPage(page,null);
    //3 获取分页结果
    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());
}

4. 快速开发

4.1 Lombok

简单Java对象一般包含以下内容:

  • 私有属性
  • setter…getter…方法
  • toString方法
  • 构造方法

虽然这些内容不难,同时也都是通过IDEA工具生成的,但是过程还是必须得走一遍,那么对于模型类的编写有没有什么优化方法?就是我们接下来要学习的 Lombok。
Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。

IDEA 安装 Lombok 插件

新版本IDEA已经内置了该插件,如果删除setter和getter方法程序有报红,则需要安装插件
image.png

添加 Lombok 依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

使用 Lombok 注解

Lombok常见的注解有:

  • @Setter:为模型类的属性提供setter方法
  • @Getter:为模型类的属性提供getter方法
  • @ToString:为模型类的属性提供toString方法
  • @EqualsAndHashCode:为模型类的属性提供equals和hashcode方法
  • @Data:是个组合注解,包含上面的注解的功能
  • @NoArgsConstructor:提供一个无参构造函数
  • @AllArgsConstructor:提供一个包含所有参数的构造函数

Lombok 的注解还有很多,上面标红的三个是比较常用的,其他的大家后期用到了,再去补充学习。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String email;
}

Lombok只是简化模型类的编写,我们之前的方法也能用,比如有人会问:我如果只想要有name和email的构造方法,该如何编写?

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Integer id;
    private String name;
    private Integer age;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
}

日志输出

public class Log4jTest {

    private static final Logger logger 
        = LoggerFactory.getLogger(Log4jTest.class);

    public static void main(String[] args) {
        logger.debug("debug");
        logger.warn("warm");
        logger.error("error");
    }
}
package com.imcode;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

@Slf4j
public class LogTest {
    @Test
    void test() {
        log.info("hello: {}", "jack");
        log.debug("hello: {}", "jack");
        log.warn("hello: {}", "jack");
        log.error("系统错误");
    }
}

4.2 自动代码生成

添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
</dependency>

代码生成器

image.png

package com.imcode;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class MyBatisPlusGenerator {

    public static void main(String[] args) {

        FastAutoGenerator.create("jdbc:mysql://localhost:3306/i_boot", "root", "123456")
                // 全局配置
                .globalConfig((scanner, builder) -> builder
                        .author("jack")
                        // .enableSwagger()  // 开启 swagger 模式
                        .fileOverride()   // 覆盖已生成文件
                        .outputDir("D:\\mybatis") // // 指定输出目录
                )
                // 包配置
                .packageConfig((scanner, builder) -> builder
                        .parent(scanner.apply("请输入包名:"))
                        .moduleName(scanner.apply("请输入模块名:"))
                        .pathInfo(Collections
                                // 设置mapperXml生成路径
                                .singletonMap(OutputFile.xml, "D:\\mybatis"))
                )
                // 策略配置
                .strategyConfig((scanner, builder) -> builder
                        .addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔,所有输入 all")))
                        .addTablePrefix("t_") // 过滤表前缀
                        .controllerBuilder().enableRestStyle()
                        .serviceBuilder().formatServiceFileName("%sService")
                        .entityBuilder().enableLombok()
                        .mapperBuilder().enableBaseResultMap()

                        .build())
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }

    // 处理 all 情况
    protected static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }
}

生成代码

image.png
将生成的代码复制到工程中测试。