【SSM框架(下)-SpringBoot、 MyBatisPlus】

SpringBoot

SpringBoot工程入门案例开发步骤

【简介】:SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程。 使用了 Spring 框架后已经简化了我们的开发。而 SpringBoot 又是对 Spring 开发进行简化的,可想而知 SpringBoot使用的简单及广泛性。既然 SpringBoot 是用来简化 Spring 开发的,那我们就先回顾一下,以 SpringMVC 开发为例:

步骤1. 创建工程,并在 pom.xml 配置文件中配置所依赖的坐标
在这里插入图片描述
步骤2. 编写 web3.0 的配置类,作为 web 程序, web3.0 的配置类不能缺少,而这个配置类还是比较麻烦的,代码如下:
在这里插入图片描述
步骤3. 编写 SpringMVC 的配置类
在这里插入图片描述
做到这只是将工程的架子搭起来。要想被外界访问,最起码还需要提供一个 Controller 类,在该类中提供一个方法。

步骤4. 编写 Controller 类
在这里插入图片描述
从上面的 SpringMVC 程序开发可以看到,前三步都是在搭建环境,而且这三步基本都是固定的。 SpringBoot 就是对这三步进行简化了。 接下来我们通过一个入门案例来体现 SpingBoot 简化 Spring 开发。

【SpringBoot快速入门】:
步骤一:创建新模块

  • 点击 “+” 选择 New Module 创建新模块
    在这里插入图片描述
  • 选择 Spring Initializr ,用来创建 SpringBoot 工程(以前我们选择的是 Maven ,今天选择 Spring Initializr 来快速构建 SpringBoot 工程。而在 Module SDK 这一项选择我们安装的 JDK 版本。)
    在这里插入图片描述
  • 对 SpringBoot 工程进行相关的设置(我们使用这种方式构建的 SpringBoot 工程其实也是 Maven 工程,而该方式只是一种快速构建的方式而已。),注意:这里的打包方式需要设置为jar
    在这里插入图片描述
  • 选中 Web ,然后勾选 Spring Web(由于我们需要开发一个 web 程序,使用到了 SpringMVC 技术,所以按照下图红框进行勾选)
    在这里插入图片描述
  • 下图界面不需要任何修改,直接点击 Finish 完成 SpringBoot 工程的构建
    在这里插入图片描述
    经过以上步骤后就创建了如下结构的模块,它会帮我们自动生成一个 Application 类,而该类一会再启动服务器时会用到
    在这里插入图片描述

注意:

  1. 在创建好的工程中不需要创建配置类
  2. 创建好的项目会自动生成其他的一些文件,而这些文件目前对我们来说没有任何作用,所以可以将这些文件删除。可以删除的目录和文件如下:
  • .mvn
  • .gitignore
  • HELP.md
  • mvnw
  • mvnw.cmd

步骤二:创建 Controller(在 com.itheima.controller 包下创建 BookController ,代码如下:)

@RestController
@RequestMapping("/books")
public class BookController {
    @GetMapping("/{id}")
    public String getById(@PathVariable Integer id){
        System.out.println("id ==> "+id);
        return "hello , spring boot!";
   }
}

步骤三:启动服务器(运行 SpringBoot 工程不需要使用本地的 Tomcat 和 插件,只运行项目 com.itheima 包下的 Application 类,我们就可以在控制台看出如下信息)
在这里插入图片描述
步骤四:进行测试(使用 Postman 工具来测试我们的程序)
在这里插入图片描述

【SpringBoot入门案例的细节】:
通过上面的入门案例我们可以看到使用 SpringBoot 进行开发,使整个开发变得很简单,那它是如何做到的呢?

要研究这个问题,我们需要看看 Application 类pom.xml 都书写了什么。先看看 Applicaion 类,该类内容如下:
在这里插入图片描述这个类中的东西很简单,就在类上添加了一个@SpringBootApplication 注解,而在主方法中就一行代码。我们在启动服务器时就是执行的该类中的主方法。

再看看 pom.xml 配置文件中的内容
在这里插入图片描述
在这里插入图片描述
【SpringBoot与Spring开发对比】:
做完 SpringBoot 的入门案例后,接下来对比一下 Spring 程序和 SpringBoot 程序。如下图

在这里插入图片描述

  • 坐标
    Spring 程序中的坐标需要自己编写,而且坐标非常多
    SpringBoot 程序中的坐标是我们在创建工程时进行勾选自动生成
  • web3.0配置类
    Spring 程序需要自己编写这个配置类。这个配置类大家之前编写过,肯定感觉很复杂
    SpringBoot 程序不需要我们自己书写
  • 配置类
    Spring/SpringMVC 程序的配置类需要自己书写。而 SpringBoot 程序则不需要书写。

SpringBoot工程官网创建方式

由于基于Idea的 Spring Initializr 快速构建 SpringBoot 工程时需要联网(整个框架是Idea从Spring官网下载下来的)。 要是没有集成Idea的环境,我们也可以通过官网进行工程构建,步骤如下:

步骤一:进入SpringBoot官网(官网地址

步骤二:点击官网最下面的Spring Initializr(第一张图)会跳转到第二张图
在这里插入图片描述
在这里插入图片描述
这个页面内容是不是感觉很眼熟的,这和我们使用 Idea 快速构建 SpringBoot 工程的界面基本相同。在上面页面输入对应的信息

步骤三:选择依赖(选择 Spring Web 可以点击上图右上角的 ADD DEPENDENCIES… CTRL + B 按钮,就会出现如下界面)
在这里插入图片描述
步骤四:生成工程
以上步骤完成后就可以生成 SpringBoot 工程了。在页面的最下方点击 GENERATE CTRL + 回车 按钮生成工程并下载到本地,如下图所示
在这里插入图片描述
打开下载好的压缩包可以看到工程结构和使用 Idea 生成的一模一样,如下图
在这里插入图片描述
而打开 pom.xml 文件,里面也包含了父工程和 Spring Web 的依赖。

【总结】:通过上面官网的操作,我们知道 Idea 中快速构建 SpringBoot 工程其实就是使用的官网的快速构建组件,那以后即使没有Idea 也可以使用官网的方式构建 SpringBoot 工程。

SpringBoot程序快速启动

【问题导入】:
在这里插入图片描述
以后我们和前端开发人员协同开发,而前端开发人员需要测试前端程序就需要后端开启服务器,这就受制于后端开发人员。 为了摆脱这个受制,前端开发人员尝试着在自己电脑上安装 Tomcat 和 Idea ,在自己电脑上启动后端程序,这显然不现实。
我们后端可以将 SpringBoot 工程打成 jar 包,该 jar 包运行不依赖于 Tomcat 和 Idea 这些工具也可以正常运行,只是这个 jar 包在运行过程中连接和我们自己程序相同的 Mysql 数据库即可。 这样就可以解决这个问题,如下图
在这里插入图片描述
【打包流程】:
步骤一:打包
由于我们在构建 SpringBoot 工程时已经在 pom.xml 中配置了如下插件

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

所以我们只需要使用 Maven 的 package 指令打包就会在 target 目录下生成对应的 Jar 包。注意:该插件必须配置,不然打好的 jar 包也是有问题的。

步骤二:启动
进入 jar 包所在位置,在 命令提示符 中输入如下命令

jar -jar springboot_01_quickstart-0.0.1-SNAPSHOT.jar

执行上述命令就可以看到 SpringBoot 运行的日志信息
在这里插入图片描述

SpringBoot简介(起步依赖)

SpringBoot 是由Pivotal团队提供的全新框架,其设计目的是用来==简化Spring应用的初始搭建以及开发过程。==大家已经感受了 SpringBoot 程序,回过头看看 SpringBoot 主要作用是什么,就是简化 Spring 的搭建过程和开发过程。

原始 Spring 环境搭建和开发存在以下问题:

  • 配置繁琐
  • 依赖设置繁琐

SpringBoot 程序优点恰巧就是针对 Spring 的缺点

  • 自动配置。这个是用来解决 Spring 程序配置繁琐的问题

  • 起步依赖。这个是用来解决 Spring 程序依赖设置繁琐的问题

  • 辅助功能(内置服务器,…)。我们在启动 SpringBoot 程序时既没有使用本地的 tomcat 也没有使用 tomcat 插件,而是使用 SpringBoot 内置的服务器。

接下来我们来说一下 SpringBoot 的起步依赖

【起步依赖】:
我们使用 Spring Initializr 方式创建的 Maven 工程的的 pom.xml 配置文件中自动生成了很多包含 starter 的依赖,如下图

在这里插入图片描述
这些依赖就是 启动依赖,接下来我们探究一下他是如何实现的。

  • 探索父工程:
    在这里插入图片描述
  • 探索依赖:
    在这里插入图片描述
  • 小结:
    在这里插入图片描述

【程序启动】:
创建的每一个 SpringBoot 程序时都包含一个类似于下面的类,我们将这个类称作引导类
在这里插入图片描述
注意:

  • SpringBoot 在创建项目时,采用jar的打包方式
  • SpringBoot 的引导类是项目的入口,运行 main 方法就可以启动项目
  • 因为我们在 pom.xml 中配置了 spring-boot-starter-web 依赖,而该依赖通过前面的学习知道它依赖 tomcat ,所以运行 main 方法就可以使用 tomcat 启动咱们的工程。

Spring简介(辅助功能之切换web服务器)

【问题】:现在我们启动工程使用的是 tomcat 服务器,那能不能不使用 tomcat 而使用 jetty 服务器, jetty 在我们 maven 高级时讲 maven 私服使用的服务器。而要切换 web 服务器就需要将默认的 tomcat 服务器给排除掉,怎么排除呢?使用
exclusion 标签

在这里插入图片描述
现在我们运行引导类可以吗?运行一下试试,打印的日志信息如下
在这里插入图片描述
程序直接停止了,为什么呢?那是因为排除了 tomcat 服务器,程序中就没有服务器了。所以此时不光要排除 tomcat 服务器,还要引入 jetty 服务器。在 pom.xml 中因为 jetty 的起步依赖
在这里插入图片描述
接下来再次运行引导类,在日志信息中就可以看到使用的是 jetty 服务器
在这里插入图片描述
小结:
通过切换服务器,我们不难发现在使用 SpringBoot 换技术时只需要导入该技术的起步依赖即可。

配置文件格式(3种)

【需求】:
在这里插入图片描述

  • application.properties配置文件
    现在需要进行配置,配置文件必须放在 resources 目录下,而该目录下有一个名为 application.properties 的配置文件,我们就可以在该配置文件中修改端口号,在该配置文件中书写 port , Idea 就会提示,如下
    在这里插入图片描述
    application.properties 配置文件内容如下:
    在这里插入图片描述
    启动服务,会在控制台打印出日志信息,从日志信息中可以看到绑定的端口号已经修改了
    在这里插入图片描述

  • application.yml配置文件
    删除 application.properties 配置文件中的内容。在 resources 下创建一个名为 application.yml 的配置文件,在该文件中书写端口号的配置项,格式如下:
    在这里插入图片描述
    而在 yml 配置文件中也是有提示功能的,我们也可以在该文件中书写 port ,然后 idea 就会提示并书写成上面的格式
    在这里插入图片描述
    启动服务,可以在控制台看到绑定的端口号是 81
    在这里插入图片描述

  • application.yaml配置文件
    删除 application.yml 配置文件和 application.properties 配置文件内容,然后在 resources 下创建名为application.yaml 的配置文件,配置内容和后缀名为 yml 的配置文件中的内容相同,只是使用了不同的后缀名而已 application.yaml配置文件内容如下
    在这里插入图片描述
    启动服务,在控制台可以看到绑定的端口号
    在这里插入图片描述

【三种配置文件要是都配置一样的东西,优先级的高低】:application.properties > application.yml > application.yaml

yaml格式

上面讲了三种不同类型的配置文件,而 properties 类型的配合文件之前我们学习过,接下来我们重点学习 yaml 类型的配置文件。

YAML(YAML Ain’t Markup Language),一种数据序列化格式。 这种格式的配置文件在近些年已经占有主导地位,那么
这种配置文件和前期使用的配置文件是有一些优势的,我们先看之前使用的配置文件。

最开始我们使用的是 xml ,格式如下:
在这里插入图片描述
而 properties 类型的配置文件如下:
在这里插入图片描述
yaml 类型的配置文件内容如下:
在这里插入图片描述
在这里插入图片描述
【语法规则】:
在这里插入图片描述

yaml数据读取方式(3种)

【环境准备】:新创建一个名为 springboot_03_read_data 的 SpringBoot 工程,目录结构如下
在这里插入图片描述
在 com.itheima.controller 包写创建名为 BookController 的控制器,内容如下
在这里插入图片描述
在 com.itheima.domain 包下创建一个名为 Enterprise 的实体类等会用来封装数据,内容如下
在这里插入图片描述
在 resources 下创建一个名为 application.yml 的配置文件,里面配置了不同的数据,内容如下
在这里插入图片描述
【读取方式】:

  • 使用 @Value注解:使用 @Value("表达式") 注解可以从配合文件中读取数据,注解中用于读取属性名引用方式是: ${一级属性名.二级属性名……}我们可以在 BookController 中使用 @Value 注解读取配合文件数据,如下
    在这里插入图片描述

  • Environment对象:上面方式读取到的数据特别零散, SpringBoot 还可以使用 @Autowired 注解注入 Environment 对象的方式读取数据。这种方式 SpringBoot 会将配置文件中所有的数据 封装到 Environment 对象中,如果需要使用哪个数据只需要通过调用Environment 对象的 getProperty(String name) 方法获取。具体代码如下:
    在这里插入图片描述

  • 自定义对象:SpringBoot 还提供了将配置文件中的数据封装到我们自定义的实体类对象中的方式。具体操作如下:
    1)、将实体类 bean 的创建交给 Spring 管理。
    ——在类上添加 @Component 注解
    2)、使用 @ConfigurationProperties 注解表示加载配置文件
    ——在该注解中也可以使用 prefix 属性指定只加载指定前缀的数据
    3)、在 BookController 中进行注入
    具体代码如下:
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    注意:
    在这里插入图片描述

多环境开发配置

以后在工作中,对于开发环境、测试环境、生产环境的配置肯定都不相同,比如我们开发阶段会在自己的电脑上安装 mysql,连接自己电脑上的 mysql 即可,但是项目开发完毕后要上线就需要该配置,将环境的配置改为线上环境的。
在这里插入图片描述
来回的修改配置会很麻烦,而 SpringBoot 给开发者提供了多环境的快捷配置,需要切换环境时只需要改一个配置即可。不同类型的配置文件多环境开发的配置都不相同,接下来对不同类型的配置文件进行说明

  • yaml文件
    在这里插入图片描述在这里插入图片描述

  • properties文件
    在这里插入图片描述在这里插入图片描述

多环境命令行启动参数设置

使用 SpringBoot 开发的程序以后都是打成 jar 包,通过 java -jar xxx.jar 的方式启动服务的。那么就存在一个问题,如何切换环境呢?因为配置文件打到的jar包中了。我们知道 jar 包其实就是一个压缩包,可以解压缩,然后修改配置,最后再打成jar包就可以了。
这种方式显然有点麻烦,而 SpringBoot 提供了在运行 jar 时设置开启指定的环境的方式,如下
在这里插入图片描述
那么这种方式能不能临时修改端口号呢?也是可以的,可以通过如下方式
在这里插入图片描述
当然也可以同时设置多个配置,比如即指定启用哪个环境配置,又临时指定端口,如下
在这里插入图片描述
大家进行测试后就会发现命令行设置的端口号优先级高(也就是使用的是命令行设置的端口号),配置的优先级其实SpringBoot 官网已经进行了说明,参见 :Spring Boot官网

进入上面网站后会看到如下页面
在这里插入图片描述
如果使用了多种方式配合同一个配置项,优先级高的生效。

配置文件分类

在这里插入图片描述
有这样的场景,我们开发完毕后需要测试人员进行测试,由于测试环境和开发环境的很多配置都不相同,所以测试人员在运行我们的工程时需要临时修改很多配置,如下
在这里插入图片描述
针对这种情况, SpringBoot 定义了配置文件不同的放置的位置;而放在不同位置的优先级时不同的。

SpringBoot 中4级配置文件放置位置:(级别越高优先级越高)

  • 1级:classpath:application.yml
  • 2级:classpath:config/application.yml
  • 3级:file :application.yml
  • 4级:file :config/application.yml

【演示】:在这里我们只演示不同级别配置文件放置位置的优先级。
环境准备:
创建一个名为 springboot_06_config_file 的 SpringBoot 工程,目录结构如下
在这里插入图片描述
创建一个名为 springboot_06_config_file 的 SpringBoot 工程,目录结构如下
在这里插入图片描述而在 resources 下创建的 application.yml 配置文件中并将端口号设置为 80 ,内容如在这里插入图片描述
【验证1级和2级的优先级】:
运行启动引导类,可以在控制台看到如下日志信息
在这里插入图片描述
【验证2级和4级的优先级:要验证4级,按照以下步骤完成】:

  • 将工程打成 jar 包:点击工程的 package 来打 jar 包
    在这里插入图片描述
  • 在硬盘上找到 jar 包所在位置
    在这里插入图片描述
  • 在 jar 包所在位置创建 config 文件夹,在该文件夹下创建 application.yml 配置文件,而在该配合文件中将端口号设置为 82
  • 在命令行使用以下命令运行程序
    在这里插入图片描述

SpringBoot整合junit

回顾 Spring 整合 junit,使用 @RunWith 注解指定运行器,使用 @ContextConfiguration 注解来指定配置类或者配置文件。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
    @Autowired
    private BookService bookService;
    @Test
    public void testSave(){
        bookService.save();
   }
}

而 SpringBoot 整合junit 特别简单,分为以下三步完成

  • 在测试类上添加 SpringBootTest 注解
  • 使用 @Autowired 注入要测试的资源
  • 定义测试方法进行测试

【整合流程】:
步骤一:创建一个名为 springboot_07_test 的 SpringBoot 工程,工程目录结构如下
在这里插入图片描述
步骤二:在 com.itheima.service 下创建 BookService 接口,内容如下

public interface BookService {
    public void save();
}

步骤三:在 com.itheima.service.impl 包写创建一个 BookServiceImpl 类,使其实现 BookService 接口,内容如下

@Service
public class BookServiceImpl implements BookService {
    @Override
    public void save() {
        System.out.println("book service is running ...");
   }
}

步骤三:编写测试类(在 test/java 下创建 com.itheima 包,在该包下创建测试类,将 BookService 注入到该测试类中)

@SpringBootTest
class Springboot07TestApplicationTests {
    @Autowired
    private BookService bookService;
    @Test
    public void save() {
        bookService.save();
   }
}

注意:这里的引导类所在包必须是测试类所在包及其子包。
例如:

  • 引导类所在包是 com.itheima
  • 测试类所在包是 com.itheima

如果不满足这个要求的话,就需要在使用 @SpringBootTest 注解时,使用 classes 属性指定引导类的字节码对象。如@SpringBootTest(classes = Springboot07TestApplication.class)

SpringBoot整合mybatis

【步骤一】:创建模块

  • 创建新模块,选择 Spring Initializr ,并配置模块相关基础信息
    在这里插入图片描述
  • 选择当前模块需要使用的技术集(MyBatis、MySQL)
    在这里插入图片描述
    【步骤二】:定义实体类
    在 com.itheima.domain 包下定义实体类 Book ,内容如下
public class Book {
    private Integer id;
    private String name;
    private String type;
    private String description;
    //setter and getter
    //toString
}

【步骤三】:定义dao接口
在 com.itheima.dao 包下定义 BookDao 接口,内容如下(需要加上@Mapper原因是 Mybatis 会扫描接口并创建接口的代码对象交给 Spring 管理,但是现在并没有告诉 Mybatis 哪个是 dao 接口。而我们要解决这个问题需要在 BookDao 接口上使用 @Mapper

@Mapper
public interface BookDao {
    @Select("select * from tbl_book where id = #{id}")
    public Book getById(Integer id);
}

【步骤四】:定义测试类
在 test/java 下定义包 com.itheima ,在该包下测试类,内容如下

@SpringBootTest
class Springboot08MybatisApplicationTests {
 @Autowired
 private BookDao bookDao;
 @Test
 void testGetById() {
 Book book = bookDao.getById(1);
 System.out.println(book);
 }
}

【步骤五】:编写配置
我们代码中并没有指定连接哪儿个数据库,用户名是什么,密码是什么。所以这部分需要在 SpringBoot 的配置文件中进行
配合。在 application.yml 配置文件中配置如下内容:

spring:
 datasource:
   driver-class-name: com.mysql.jdbc.Driver
   url: jdbc:mysql://localhost:3306/ssm_db
   username: root
   password: root

另外:现在我们并没有指定数据源, SpringBoot 有默认的数据源,我们也可以指定使用 Druid 数据源,按照以下步骤实现

  • 导入 Druid 依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
  • 在 application.yml 配置文件配置,可以通过spring.datasource.type 来配置使用什么数据源。配置文件内容可以改进为(注意:SpringBoot 版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要在url连接串中配置时区jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC ,或在MySQL数据库端配置时区解决此问题)
spring:
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
   username: root
   password: root
   type: com.alibaba.druid.pool.DruidDataSource

基于SpringBoot实现ssm整合

SpringBoot 到这就已经学习完毕,接下来我们将学习 SSM 时做的三大框架整合的案例用 SpringBoot 来实现一下。我们完成这个案例基本是将之前做的拷贝过来,修改成 SpringBoot 的即可,主要从以下几部分完成

  1. pom.xml
    配置起步依赖,必要的资源坐标(druid)
  2. application.yml
    设置数据源、端口等
  3. 配置类
    全部删除
  4. dao
    设置@Mapper
  5. 测试类
  6. 页面
    放置在resources目录下的static目录中

【步骤一】:创建工程

  • 创建 SpringBoot 工程,在创建工程时需要勾选 web 、 mysql 、 mybatis ,工程目录结构如下
    在这里插入图片描述
  • 由于我们工程中使用到了 Druid ,所以需要导入 Druid 的坐标
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

【步骤二】:拷贝代码
将 springmvc_11_page 工程中的 java 代码及测试代码连同包拷贝到 springboot_09_ssm 工程,按照下图进行拷贝
在这里插入图片描述
需要修改的内容如下:

  • Springmvc_11_page 中 config 包下的是配置类,而 SpringBoot 工程不需要这些配置类,所以这些可以直接删除
  • dao 包下的接口上在拷贝到 springboot_09-ssm 工程中需要在接口中添加 @Mapper 注解
  • BookServiceTest 测试需要改成 SpringBoot 整合 junit 的
@SpringBootTest
public class BookServiceTest {
    @Autowired
    private BookService bookService;
    @Test
    public void testGetById(){
        Book book = bookService.getById(2);
        System.out.println(book);
   }
    @Test
    public void testGetAll(){
        List<Book> all = bookService.getAll();
        System.out.println(all);
   }
}

【步骤三】:配置文件

在 application.yml 配置文件中需要配置如下内容

  • 服务的端口号
  • 连接数据库的信息
  • 数据源
server:
 port: 80
 
spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/ssm_db #?servierTimezone=UTC
   username: root
   password: root

【步骤四】:静态资源
在 SpringBoot 程序中是没有 webapp 目录的,那么在SpringBoot 程序中静态资源需要放在什么位置呢?静态资源需要放在 resources 下的 static 下,如下图所示
在这里插入图片描述

MyBatisPlus

MyBatisPlus入门案例

MybatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提供效率。
SpringBoot刚刚我们学习完成,它能快速构建Spring开发环境用以整合其他技术,使用起来是非常简单,对于MP的学习,我们也基于SpringBoot来构建学习。

【步骤一】:创建数据库及表

create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (
   id bigint(20) primary key auto_increment,
   name varchar(32) not null,
   password  varchar(32) not null,
   age int(3) not null ,
   tel varchar(32) not null
);
insert into user values(1,'Tom','tom',3,'18866668888');
insert into user values(2,'Jerry','jerry',4,'16688886666');
insert into user values(3,'Jock','123456',41,'18812345678');
insert into user values(4,'传智播客','itcast',15,'4006184000');

【步骤二】:创建SpringBoot工程
在这里插入图片描述
【步骤三】:勾选配置使用技术(说明:由于MP并未被收录到idea的系统内置配置,无法直接选择加入,需要手动在pom.xml中配置添加)
在这里插入图片描述
【步骤四】:pom.xml补全依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

说明:

  • druid数据源可以加也可以不加,SpringBoot有内置的数据源,可以配置成使用Druid数据源
  • 从MP的依赖关系可以看出,通过依赖传递已经将MyBatis与MyBatis整合Spring的jar包导入,我们不需要额外在添加MyBatis的相关jar包
    在这里插入图片描述

【步骤五】:添加MP的相关配置信息
resources默认生成的是properties配置文件,可以将其替换成yml文件,并在文件中配置数据库连接的相关信息: application.yml

spring:
 datasource:
   type: com.alibaba.druid.pool.DruidDataSource
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC 
   username: root
   password: root

【步骤六】:根据数据库表创建实体类

public class User {   
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
    //setter...getter...toString方法略
}

【步骤七】:创建Dao接口

@Mapper
public interface UserDao extends BaseMapper<User>{
}

【步骤8】:编写引导类

@SpringBootApplication
//@MapperScan("com.itheima.dao")
public class Mybatisplus01QuickstartApplication {
    public static void main(String[] args) {
   		 SpringApplication.run(Mybatisplus01QuickstartApplication.class, args);
   }
}

说明:Dao接口要想被容器扫描到,有两种解决方案:
方案一:在Dao接口上添加@Mapper注解,并且确保Dao处在引导类所在包或其子包中
——该方案的缺点是需要在每一Dao接口中添加注解
方案二:在引导类上添加@MapperScan注解,其属性为所要扫描的Dao所在包
——该方案的好处是只需要写一次,则指定包下的所有Dao接口都能被扫描到,@Mapper就可以不
写。

【步骤九】:编写测试类(直接可以使用userDao接口(因为继承了父类BaseMapper)提供的方法实现数据库的操作

@SpringBootTest
class MpDemoApplicationTests {
 @Autowired
 private UserDao userDao;
 @Test
 public void testGetAll() {
 List<User> userList = userDao.selectList(null);
 System.out.println(userList);
 }
}

说明:
userDao注入的时候下面有红线提示的原因是什么?
——UserDao是一个接口,不能实例化对象
——只有在服务器启动IOC容器初始化后,由框架创建DAO接口的代理对象来注入
——现在服务器并未启动,所以代理对象也未创建,IDEA查找不到对应的对象注入,所以提示报红
——一旦服务启动,就能注入其代理对象,所以该错误提示不影响正常运行。

查看运行结果:
在这里插入图片描述
跟之前整合MyBatis相比,你会发现我们不需要在DAO接口中编写方法和SQL语句了,只需要继承BaseMapper接口即可。整体来说简化很多。

MyBatisPlus简介

MyBatisPlus的官网为: https://mp.baomidou.com/

标准CRUD制作

对于标准的CRUD功能都有哪些以及MP都提供了哪些方法可以使用呢?我们先来看张图:
在这里插入图片描述
对于这张图的方法,我们挨个来演示下:
首先说下,案例中的环境就是咱们入门案例的内容,第一个先来完成新增功能

【新增】
在这里插入图片描述
在测试类中进行新增操作:直接使用userDao接口的insert方法即可

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    @Autowired
    private UserDao userDao
    @Test
    void testSave() {
        User user = new User();
        user.setName("黑马程序员");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
   }
}

执行测试后,数据库表中就会添加一条数据。
在这里插入图片描述
但是数据中的主键ID,有点长,那这个主键ID是如何来的?我们更想要的是主键自增,应该是5才对,这个是我们后面要学习的主键ID生成策略,这块的这个问题,我们暂时先放放。

【删除】:
在这里插入图片描述在这里插入图片描述
在测试类中进行删除操作:直接使用userDao接口的deleteById方法即可

 @SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testDelete() {
        userDao.deleteById(1401856123725713409L);
   }
}

【修改】:
在这里插入图片描述
在测试类中进行修改操作:直接使用userDao接口的updataById方法即可

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testUpdate() {
        User user = new User();
        user.setId(1L);
        user.setName("Tom888");
        user.setPassword("tom888");
        userDao.updateById(user);
   }
}

说明:修改的时候,只修改实体对象中有值的字段。

【根据ID查询】:
在这里插入图片描述
在测试类中进行新增操作:直接使用userDao接口的selectById方法即可

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetById() {
        User user = userDao.selectById(2L);
        System.out.println(user);
   }
}

【查询所有】:
在这里插入图片描述
在测试类中进行新增操作:直接使用userDao接口的selectList方法即可

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll() {
        List<User> userList = userDao.selectList(null);
        System.out.println(userList);
   }
}

Lombok

代码写到这,我们会发现DAO接口类的编写现在变成最简单的了,里面什么都不用写。反过来看看模型类的编写都需要哪些内容:

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

虽然这些内容不难,同时也都是通过IDEA工具生成的,但是过程还是必须得走一遍,那么对于模型类的编写有没有什么优化方法?就是我们接下来要学习的Lombok。

【概念】:Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发

【流程】:
步骤一:添加lombok依赖(注意:版本可以不用写,因为SpringBoot中已经管理了lombok的版本。)

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <!--<version>1.18.12</version>-->
</dependency>

步骤二:安装Lombok的插件 ,新版本IDEA已经内置了该插件,如果删除setter和getter方法程序有报红,则需要安装插件
在这里插入图片描述
如果在IDEA中找不到lombok插件,可以访问如下网站:lombok插件,根据自己IDEA的版本下载对应的lombok插件,下载成功后,在IDEA中采用离线安装的方式进行安装。在这里插入图片描述
步骤三:模型类上添加注解

Lombok常见的注解有:

  • @Setter:为模型类的属性提供setter方法
  • @Getter:为模型类的属性提供getter方法
  • @ToString:为模型类的属性提供toString方法
  • @EqualsAndHashCode:为模型类的属性提供equals和hashcode方法
  • @Data:是个组合注解,包含上面的注解的功能
  • @NoArgsConstructor:提供一个无参构造函数
  • @AllArgsConstructor:提供一个包含所有参数的构造函数
    Lombok的注解还有很多,上面标红的三个是比较常用的,其他的大家后期用到了,再去补充学习。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
 
    public User(String name, String password) {
        this.name = name;
        this.password = password;
   }
}

标准分页功能制作

在这里插入图片描述在这里插入图片描述
【分页查询步骤】:
步骤一:调用方法传入参数获取返回值(直接适应userDao接口的selectPage方法,而里面的page参数需要创建相应的对象【Ipage<User> page = new Page<>(1,3)】获得

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    @Autowired
    private UserDao userDao;
    //分页查询
    @Test
    void testSelectPage(){
        //1 创建IPage分页对象,设置分页参数,1为当前页码,3为每页显示的记录数
        IPage<User> page=new Page<>(1,3);
        //2 执行分页查询
        userDao.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());
   }
}

步骤二:设置分页拦截器,要想使用selectPage方法需要设计拦截器配置类

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1 创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加分页拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
   }
}

步骤三:如果想查看MP执行的SQL语句,可以修application.yml配置文件,

mybatis-plus:
 configuration:
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印SQL日志到控制台

打开日志后,就可以在控制台打印出对应的SQL语句,开启日志功能性能就会受到影响,调试完后记得关闭。

条件查询的三种格式

在进行查询的时候,我们的入口是在Wrapper这个类上,因为它是一个接口,所以我们需要去找它对应的实现类,关于实现类也有很多,说明我们有多种构建查询条对象的方式,
在这里插入图片描述

  • 第一种:QueryWrapper
    在这里插入图片描述

  • 第二种:QueryWrapper的基础上使用lambda在这里插入图片描述

  • 第三种:LambdaQueryWrapper
    在这里插入图片描述 - 多条件查询在这里插入图片描述在这里插入图片描述

条件查询null判定

【问题】:先来看一张图
在这里插入图片描述

  • 我们在做条件查询的时候,一般会有很多条件可以供用户进行选择查询。

  • 这些条件用户可以选择使用也可以选择不使用,比如我要查询价格在8000以上的手机

  • 在输入条件的时候,价格有一个区间范围,按照需求只需要在第一个价格输入框中输入8000

  • 后台在做价格查询的时候,一般会让 price>值1 and price <值2

  • 因为前端没有输入值2,所以如果不处理的话,就会出现 price>8000 and price < null问 题

  • 这个时候查询的结果就会出问题,具体该如何解决?

在这里插入图片描述

需求:查询数据库表中,根据输入年龄范围来查询符合条件的记录
用户在输入值的时候,
如果只输入第一个框,说明要查询大于该年龄的用户
如果只输入第二个框,说明要查询小于该年龄的用户
如果两个框都输入了,说明要查询年龄在两个范围之间的用户

思考第一个问题:后台如果想接收前端的两个数据,该如何接收?
我们可以使用两个简单数据类型,也可以使用一个模型类,但是User类中目前只有一个age属性,如:

@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

使用一个age属性,如何去接收页面上的两个值呢?这个时候我们有两个解决方案
方案一:添加属性age2,这种做法可以但是会影响到原模型类的属性内容

@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
    private Integer age2;
}

方案二:新建一个模型类,让其继承User类,并在其中添加age2属性,UserQuery在拥有User属性后同时添加了age2属性。

@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}
@Data
public class UserQuery extends User {
    private Integer age2;
}

环境准备好后,我们来实现下刚才的需求:

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        //模拟页面传递过来的查询数据
        UserQuery uq = new UserQuery();
        uq.setAge(10);
        uq.setAge2(30);
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        if(null != uq.getAge2()){
            lqw.lt(User::getAge, uq.getAge2());
       }
        if( null != uq.getAge()) {
            lqw.gt(User::getAge, uq.getAge());
       }
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
   }
}

上面的写法可以完成条件为非空的判断,但是问题很明显,如果条件多的话,每个条件都需要判断,代码量就比较大,来看MP给我们提供的简化方式:

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        //模拟页面传递过来的查询数据
        UserQuery uq = new UserQuery();
        uq.setAge(10);
        uq.setAge2(30);
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.lt(null!=uq.getAge2(),User::getAge, uq.getAge2());//uq.getAge2()为空,该语句就不会执行
        lqw.gt(null!=uq.getAge(),User::getAge, uq.getAge());
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
   }
}

lt()方法在这里插入图片描述
condition为boolean类型,返回true,则添加条件,返回false则不添加条件

查询投影

目前我们在查询数据的时候,什么都没有做默认就是查询表中所有字段的内容,我们所说的查询投影即不查询所有字段,只查询出指定内容的数据。

【查询实体类中指定的的属性字段】:如实体类中的name、tel、password等【select(…)方法用来设置查询的字段列,可以设置多个,最终的sql语句为:SELECT id,name,age FROM user

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.select(User::getId,User::getName,User::getAge);
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
   }
}

select(…)方法用来设置查询的字段列,可以设置多个,最终的sql语句为:

如果使用的不是lambda,就需要手动指定字段【最终的sql语句为:SELECT id,name,age,tel FROM user

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        QueryWrapper<User> lqw = new QueryWrapper<User>();
        lqw.select("id","name","age","tel");
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
   }
}

【聚合查询】:

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

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        QueryWrapper<User> lqw = new QueryWrapper<User>();
        //lqw.select("count(*) as count");
        //SELECT count(*) as count FROM user
        //lqw.select("max(age) as maxAge");
        //SELECT max(age) as maxAge FROM user
        //lqw.select("min(age) as minAge");
        //SELECT min(age) as minAge FROM user
        //lqw.select("sum(age) as sumAge");
        //SELECT sum(age) as sumAge FROM user
        lqw.select("avg(age) as avgAge");
        //SELECT avg(age) as avgAge FROM user
        List<Map<String, Object>> userList = userDao.selectMaps(lqw);//由于实体类中没有avgAge这个属性,所以只能将这个返回值用Map收到
        System.out.println(userList);
   }
}

【分组查询】:

需求:分组查询,完成 group by的查询使用

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        QueryWrapper<User> lqw = new QueryWrapper<User>();
        lqw.select("count(*) as count,tel");
        lqw.groupBy("tel");
        List<Map<String, Object>> list = userDao.selectMaps(lqw);
        System.out.println(list);
   }
}

groupBy为分组,最终的sql语句为SELECT count(*) as count,tel FROM user GROUP BY tel

注意:

  • 聚合与分组查询,无法使用lambda表达式来完成
  • MP只是对MyBatis的增强,如果MP实现不了,我们可以直接在DAO接口中使用MyBatis的方式实现

查询条件设置

前面我们只使用了lt()和gt(),除了这两个方法外,MP还封装了很多条件对应的方法,这一节我们重点把MP提供的查询条件方法进行学习下。

MP的查询条件有很多:

  • 范围匹配(> 、 = 、between)
  • 模糊匹配(like)
  • 空判定(null)
  • 包含性匹配(in)
  • 分组(group)
  • 排序(order)
  • ……

【等值查询】:

需求:根据用户名和密码查询用户信息

@SpringBootTest
class Mybatisplus02DqlApplicationTests { 
    @Autowired
    private UserDao userDao;    
    @Test
    void testGetAll(){
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
        User loginUser = userDao.selectOne(lqw);
        System.out.println(loginUser);
   }
}

在这里插入图片描述
【范围查询】:

需求:对年龄进行范围查询,使用lt()、le()、gt()、ge()、between()进行范围查询

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.between(User::getAge, 10, 30);
        //SELECT id,name,password,age,tel FROM user WHERE (age BETWEEN ? AND ?)
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
   }
}

在这里插入图片描述
【模糊查询】:

需求:查询表中name属性的值以J开头的用户信息,使用like进行模糊查询

@SpringBootTest
class Mybatisplus02DqlApplicationTests { 
    @Autowired
    private UserDao userDao;   
    @Test
    void testGetAll(){
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.likeLeft(User::getName, "J");
        //SELECT id,name,password,age,tel FROM user WHERE (name LIKE ?)
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
   }
}

在这里插入图片描述
【排序查询】:

需求:查询所有数据,然后按照id降序

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
    private UserDao userDao;
    @Test
    void testGetAll(){
        LambdaQueryWrapper<User> lwq = new LambdaQueryWrapper<>();
        /**
         * condition :条件,返回boolean,
         当condition为true,进行排序,如果为false,则不排序
         * isAsc:是否为升序,true为升序,false为降序
         * columns:需要操作的列
         */
        lwq.orderBy(true,false, User::getId);
        userDao.selectList(lw
   }
}

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

映射匹配兼容性

前面我们已经能从表中查询出数据,并将数据封装到模型类中,这整个过程涉及到一张表和一个模型类:
在这里插入图片描述
之所以数据能够成功的从表中获取并封装到模型对象中,原因是表的字段列名和模型类的属性名一样。那么问题就来了:
【问题一】:
表的列名和模型类的属性名发生不一致,就会导致数据封装不到模型对象,这个时候就需要其中一方做出修改,那如果前提是两边都不能改又该如何解决?
MP给我们提供了一个注解@TableField ,使用该注解可以实现模型类属性名和表的列名之间的映射关系
在这里插入图片描述
【问题二】:编码中添加了数据库中未定义的属性
当模型类中==多了一个数据库表不存在的字段,==就会导致生成的sql语句中在select的时候查询了数据库不存在的字段,程序运行就会报错,错误信息为:

Unknown column ‘多出来的字段名称’ in ‘field list’

具体的解决方案用到的还是@TableField注解,它有一个属性叫exist,设置该字段是否在数据库表中存在,如果设置为false则不存在,生成sql语句查询的时候,就不会再查询该字段了。
在这里插入图片描述
【问题三】:采用默认查询开放了更多的字段查看权限
查询表中所有的列的数据,就可能把一些敏感数据查询到返回给前端,这个时候我们就需要限制哪些字段默认不要进行查询。解决方案是@TableField注解的一个属性叫select,该属性设置默认是否需要查询该字段的值,true(默认值)表示默认查询该字段,false表示默认不查询该字段
在这里插入图片描述在这里插入图片描述
【问题四】:表名与编码开发设计不同步
该问题主要是表的名称和模型类的名称不一致,导致查询失败,这个时候通常会报如下错误信息:

Table ‘databaseName.tableNaem’ doesn’t exist

翻译过来就是数据库中的表不存在。解决方案是使用MP提供的另外一个注解@TableName来设置表与模型类之间的对应关系。
在这里插入图片描述在这里插入图片描述

id生成策略

前面我们在新增的时候留了一个问题,就是新增成功后,主键ID是一个很长串的内容,我们更想要的是按照数据库表字段进行自增长,在解决这个问题之前,我们先来分析下ID该如何选择:

不同的表应用不同的id生成策略

  • 日志:自增(1,2,3,4,……)
  • 购物订单:特殊规则(FQ23948AK3843)
  • 外卖单:关联地区日期等信息(10 04 20200314 34 91)
  • 关系表:可省略id
  • ……

不同的业务采用的ID生成方式应该是不一样的,那么在MP中都提供了哪些主键生成策略,以及我们该如何进行选择?

在这里我们又需要用到MP的一个注解叫@TableId
在这里插入图片描述
【AUTO策略】:
步骤一:设置生成策略为AUTO

@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

步骤二:删除测试数据并修改自增值

  • 删除测试数据在这里插入图片描述
  • 因为之前生成主键ID的值比较长,会把MySQL的自动增长的值变的很大,所以需要将其调整为目前最新的id值。在这里插入图片描述

步骤三:运行新增方法
会发现,新增成功,并且主键id也是从5开始
在这里插入图片描述
经过这三步的演示,会发现AUTO的作用是使用数据库ID自增,在使用该策略的时候一定要确保对应的数据库表设置了ID主键自增,否则无效。

【INPUT策略】:
步骤一:设置生成策略为INPUT

@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

注意:这种ID生成策略,需要将表的自增策略删除掉
在这里插入图片描述
步骤二:添加数据手动设置ID

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testSave(){
    User user = new User();
    //设置主键ID的值
    user.setId(666L);
    user.setName("黑马程序员");
    user.setPassword("itheima");
    user.setAge(12);
    user.setTel("4006184000");
    userDao.insert(user);
   }
}

步骤三:运行新增方法
如果没有设置主键ID的值,则会报错,错误提示就是主键ID没有给值:
在这里插入图片描述
如果设置了主键ID,则数据添加成功,如下:
在这里插入图片描述
【ASSIGN_ID策略】:
步骤一:设置生成策略为ASSIGN_ID

@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

步骤二:添加数据不设置ID

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testSave(){
        User user = new User();
        user.setName("黑马程序员");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
   }
}

注意:这种生成策略,不需要手动设置ID,如果手动设置ID,则会使用自己设置的值。

步骤三:运行新增方法
在这里插入图片描述
生成的ID就是一个Long类型的数据。

【ASSIGN_UUID策略】:
步骤一:设置生成策略为ASSIGN_UUID
使用uuid需要注意的是,主键的类型不能是Long,而应该改成String类型

@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

步骤二:修改表的主键类型
在这里插入图片描述
主键类型设置为varchar,长度要大于32,因为UUID生成的主键为32位,如果长度小的话就会导致插入失败。

步骤三:添加数据不设置ID

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testSave(){
        User user = new User();
        user.setName("黑马程序员");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
   }
}

步骤四:运行新增方法
在这里插入图片描述
【简化配置】:
——模型类主键策略设置:
对于主键ID的策略已经介绍完,但是如果要在项目中的每一个模型类上都需要使用相同的生成策略,如:
在这里插入图片描述
确实是稍微有点繁琐,我们能不能在某一处进行配置,就能让所有的模型类都可以使用该主键ID策略呢?
答案是肯定有,我们只需要在配置文件中添加如下内容:

mybatis-plus:
 global-config:
   db-config:
   id-type: assign_id

配置完成后,每个模型类的主键ID策略都将成为assign_id.

——数据库表与模型类的映射关系:
MP会默认将模型类的类名名首字母小写作为表名使用,假如数据库表的名称都以tbl_开头,那么我们就需要将所有的模型类上添加@TableName,如:
在这里插入图片描述
配置起来还是比较繁琐,简化方式为在配置文件中配置如下内容:

mybatis-plus:
 global-config:
   db-config:
   table-prefix: tbl_

设置表的前缀内容,这样MP就会拿 tbl_加上模型类的首字母小写,就刚好组装成数据库的表名。

多数据操作(删除与查询)

【问题】:
之前添加了很多商品到购物车,过了几天发现这些东西又不想要了,该怎么办呢?

很简单删除掉,但是一个个删除的话还是比较慢和费事的,所以一般会给用户一个批量操作,也就是前面有一个复选框,用户一次可以勾选多个也可以进行全选,然后删一次就可以将购物车清空,这个就需要用到批量删除的操作了。

具体该如何实现多条删除,我们找找对应的API方法int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);(翻译方法的字面意思为:删除(根据ID 批量删除),参数是一个集合,可以存放多个id值。)

需求:根据传入的id集合将数据库表中的数据删除掉。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testDelete(){
        //删除指定多条数据
        List<Long> list = new ArrayList<>();
        list.add(1402551342481838081L);
        list.add(1402553134049501186L);
        list.add(1402553619611430913L);
        userDao.deleteBatchIds(list);
   }
}

执行成功后,数据库表中的数据就会按照指定的id进行删除。
除了按照id集合进行批量删除,也可以按照id集合进行批量查询,还是先来看下APIList<T>selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);(方法名称翻译为:查询(根据ID 批量查询),参数是一个集合,可以存放多个id值。)

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testGetByIds(){
        //查询指定多条数据
        List<Long> list = new ArrayList<>();
        list.add(1L);
        list.add(3L);
        list.add(4L);
        userDao.selectBatchIds(list);
   }
}

查询结果就会按照指定传入的id值进行查询
在这里插入图片描述

逻辑删除

【问题】:
在这里插入图片描述
步骤一:修改数据库表添加deleted列
字段名可以任意,内容也可以自定义,比如0代表正常,1代表删除,可以在添加列的同时设置其默认值为0正常。
在这里插入图片描述
步骤二:实体类添加属性
(1)添加与数据库表的列对应的一个属性名,名称可以任意,如果和数据表列名对不上,可以使用@TableField进行关系映射,如果一致,则会自动对应。
(2)标识新增的字段为逻辑删除字段,使用@TableLogic

@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    @TableLogic(value="0",delval="1")
    //value为正常数据的值,delval为删除数据的值
    private Integer deleted;
}

步骤三:运行删除方法

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testDelete(){
       userDao.deleteById(1L);
   }
}

在这里插入图片描述
从测试结果来看,逻辑删除最后走的是update操作,会将指定的字段修改成删除状态对应的值。

【思考】:
逻辑删除,对查询有没有影响呢?

在这里插入图片描述在这里插入图片描述在这里插入图片描述
【小结】:
在这里插入图片描述在这里插入图片描述

乐观锁

【概念】:
在这里插入图片描述
乐观锁的实现方式:

  • 数据库表中添加version列,比如默认值给1
  • 第一个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
  • 第二个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
  • 第一个线程执行更新时,
    set version = newVersion where version = oldVersion
    newVersion = version+1 [2]
    oldVersion = version [1]
  • 第二个线程执行更新时,
    set version = newVersion where version = oldVersion
    newVersion = version+1 [2]
    oldVersion = version [1]
  • 假如这两个线程都来更新数据,第一个和第二个线程都可能先执行
    假如第一个线程先执行更新,会把version改为2,
    第二个线程再更新的时候,set version = 2 where version = 1,此时数据库表的数据version已经为2,所以第二个线程会修改失败
    假如第二个线程先执行更新,会把version改为2,
    第一个线程再更新的时候,set version = 2 where version = 1,此时数据库表的数 据version已经为2,所以第一个线程会修改失败
    不管谁先执行都会确保只能有一个线程更新数据,这就是MP提供的乐观锁的实现原理分析。

【实现步骤】:
步骤一:数据库表添加列
列名可以任意,比如使用version ,给列设置默认值为1
在这里插入图片描述
步骤二:在模型类中添加对应的属性
根据添加的字段列名,在模型类中添加对应的属性值

@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    private Integer deleted;
    @Version
    private Integer version;
}

步骤三:添加乐观锁的拦截器

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加乐观锁拦截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpInterceptor;
   }
}

步骤四:执行更新操作

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testUpdate(){
       User user = new User();
        user.setId(3L);
        user.setName("Jock666");
        userDao.updateById(user);
   }
}

在这里插入图片描述
你会发现,这次修改并没有更新version字段,原因是没有携带version数据。
添加version数据

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testUpdate(){
        User user = new User();
        user.setId(3L);
        user.setName("Jock666");
        user.setVersion(1);
        userDao.updateById(user);
   }
}

在这里插入图片描述
你会发现,我们传递的是1,MP会将1进行加1,然后,更新回到数据库表中。
所以要想实现乐观锁,首先第一步应该是拿到表中的version,然后拿version当条件在将version加1更新回到数据库表中,所以我们在查询的时候,需要对其进行查询

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testUpdate(){
        //1.先通过要修改的数据id将当前数据查询出来
        User user = userDao.selectById(3L);
        //2.将要修改的属性逐一设置进去
        user.setName("Jock888");
        userDao.updateById(user);
   }
}

在这里插入图片描述
大概分析完乐观锁的实现步骤以后,我们来模拟一种加锁的情况,看看能不能实现多个人修改同一个数据的时候,只能有一个人修改成功。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    @Autowired
    private UserDao userDao;
    @Test
    void testUpdate(){
       //1.先通过要修改的数据id将当前数据查询出来
        User user = userDao.selectById(3L);     //version=3
        User user2 = userDao.selectById(3L);    //version=3
        user2.setName("Jock aaa");
        userDao.updateById(user2);              //version=>4
        user.setName("Jock bbb");
        userDao.updateById(user);               //verion=3?条件还成立吗?
   }
}

运行程序,分析结果:
在这里插入图片描述
乐观锁就已经实现完成了,如果对于上面的这些步骤记不住咋办呢?参考官方文档来实现:乐观锁

代码生成器【了解即可】

【步骤一】:创建一个Maven项目
在这里插入图片描述在这里插入图片描述在这里插入图片描述

【步骤三】:编写引导类
在这里插入图片描述
【步骤四】:创建代码生成类
在这里插入图片描述在这里插入图片描述
对于代码生成器中的代码内容,我们可以直接从官方文档中获取代码进行修改,https://mp.baomidou.com/guide/generator.html

【步骤五】:运行程序
在这里插入图片描述
MP中Service的CRUD
在这里插入图片描述在这里插入图片描述

注意:mybatisplus_04_generator项目中对于MyBatis的环境是没有进行配置,如果想要运行,需要提取将配置文件中的内容进行完善后在运行。
思考:在MP封装的Service层都有哪些方法可以用?
查看官方文档: https://mp.baomidou.com/guide/crud-interface.html ,这些提供的方法大家可以参考官方文档进行学习使用,方法的名称可能有些变化,但是方法对应的参数和返回值基本类似。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值