Java:77-Mybatis-Plus详解

Mybatis-Plus详解

Mybatis-Plus概念:
Mybatis-Plus介绍:
官⽹:https://mp.baomidou.com/
MyBatis-Plus(简称 MP)是⼀个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变(即自然内置了MyBatis的依赖,也就可以单独的使用MyBatis框架的内容),为简化开发、提高效率而生

在这里插入图片描述

愿景:
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1 P、 2P,基友搭配,效率翻倍

在这里插入图片描述

特性:
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接⾯向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作
更有强大的条件构造器,满⾜各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯⼀ ID 生成器 - Sequence),可自由配置,完美 解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进⾏强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代 码
支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、 MariaDB、 Oracle、 DB2、 H2、 HSQL、 SQLite、 Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 Sql 语句以及其执⾏时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
架构:

在这里插入图片描述

作者:
Mybatis-Plus是由 baomidou(苞米豆)组织开发并且开源的,以前该组织大概有30人左右,现在可能少了些,如下就是17人
码云地址:https://gitee.com/organizations/baomidou

在这里插入图片描述

Mybatis-Plus快速入门:
安装:
全新的 MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调用,所以安装集成 MP3.0 要求如下:
JDK 8+
Maven
如果是Spring Boot,那么对应的依赖就是:
<dependency>    
    <!--Spring Boot整合Mybatis-Plus的依赖-->
    <groupId>com.baomidou</groupId>    
    <artifactId>mybatis-plus-boot-starter</artifactId>   
    <version>3.4.0</version>
</dependency>
如果是Spring MVC,那么对应的依赖就是:
<dependency>    
    <!--Mybatis-Plus的依赖-->
    <groupId>com.baomidou</groupId>    
    <artifactId>mybatis-plus</artifactId>    
    <version>3.4.0</version>
</dependency>
实际上Spring Boot也可以自己操作扫描,因为他也是一种方式而已,所以,我们也可以单独的操作其他框架
只是Spring boot帮我们操作了而已,使得方便,而单独时
如操作Spring,需要我们些配置文件或者注解(有其他方式,则自然可以使用其他方式),并操作才可
所以上面的依赖,并不是绝对的必须是对应的依赖,虽然使用Spring Boot操作时,需要
当然,若你创建Spring boot而不引入对应的依赖,那么自然,可以自己引入依赖来单独操作,这是可以的

在这里插入图片描述

因为可能会覆盖mybatis的版本等等
对于Mybatis整合MP有常常有三种用法,分别是Mybatis+MP、Spring+Mybatis+MP、Spring Boot+Mybatis+MP
MyBatis-Plus简称 MP
创建数据库以及表:
CREATE DATABASE mp CHARACTER SET utf8;

USE mp;

DROP TABLE IF EXISTS tb_user;
CREATE TABLE USER(
id BIGINT(20) NOT NULL COMMENT '主键ID', 
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',   
    -- null代表可以为null,默认是可以为null的,所以可以不写,即与not null(不可以为null)相反的
age INT(11) NULL DEFAULT NULL COMMENT '年龄',  
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);

INSERT INTO USER (id, NAME, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
创建工程(普通的创建,不是Spring boot工程):
实际上无论是普通的创建还是特殊的创建,都只是在空的创建下
加上对应的目录或者文件夹而已,或者设置为特殊文件夹,如web工程(创建时可以加上模板,或自己配置,具体可以百度)
导入依赖:
<dependencies>
    <!-- mybatis-plus插件依赖 -->
     <dependency>         
         <groupId>com.baomidou</groupId>   
         <artifactId>mybatis-plus</artifactId>       
         <version>3.1.1</version>      
    </dependency>
    <!--Mysql-->      
    <dependency>          
        <groupId>mysql</groupId>   
        <artifactId>mysql-connector-java</artifactId>     
        <version>5.1.47</version>   
    </dependency>
    <!--连接池-->
    <dependency>         
        <groupId>com.alibaba</groupId> 
        <artifactId>druid</artifactId>      
        <version>1.0.11</version>      
    </dependency>
    <!--简化bean代码的⼯具包-->
     <dependency>         
         <groupId>org.projectlombok</groupId>   
         <artifactId>lombok</artifactId>      
         <version>1.18.4</version>      
    </dependency>
    <dependency>       
        <!--@Test注解的操作-->
        <groupId>junit</groupId>     
        <artifactId>junit</artifactId>         
        <version>4.12</version>       
    </dependency>
     <dependency>     
         <!--操作日志的-->
         <groupId>org.slf4j</groupId>   
         <artifactId>slf4j-log4j12</artifactId>    
         <version>1.6.4</version>      
    </dependency>
 </dependencies>
<build>      
    <plugins>    
        <plugin>        
              <!--maven插件,一般用来指定项目的JDK编译版本
maven好像默认使用1.5版本进行编译,这里指定1.8-->
            <groupId>org.apache.maven.plugins</groupId>     
            <artifactId>maven-compiler-plugin</artifactId>  
            <configuration>                   
                <source>1.8</source>          
                <target>1.8</target>          
            </configuration>         
        </plugin>      
    </plugins>   
</build>
Mybatis + MP:
下面演示,通过纯Mybatis与Mybatis-Plus整合
创建该项目的子Module(并没有对应依赖的导入),对应目录如下:

在这里插入图片描述

对应的log4j.properties文件:
log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
Mybatis实现查询User:
第⼀步,编写mybatis-config.xml文件(资源文件里面):
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration     
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"       
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
     <properties resource="jdbc.properties"></properties>
    <!--environments: 运⾏环境-->
    <environments default="development">    
        <environment id="development">
            <!--当前的事务事务管理器是JDBC-->
             <transactionManager type="JDBC"></transactionManager>
            <!--数据源信息 POOLED:使⽤mybatis的连接池-->
             <dataSource type="POOLED">  
                 <!--虽然,会操作默认驱动,但配置不能少,可能以后可以不写,或者版本的操作-->
                 <property name="driver" value="${jdbc.driver}"/>    
                 <property name="url" value="${jdbc.url}"/>      
                 <property name="username" value="${jdbc.username}"/>   
                 <property name="password" value="${jdbc.password}"/>  
            </dataSource>     
        </environment>  
    </environments>
    <!--引⼊映射配置⽂件-->
     <mappers>      
        <package name="com.lagou.mapper"></package>
    </mappers>
    </configuration>
再创建jdbc.properties文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mp
jdbc.username=root
jdbc.password=123456

#自己的数据库信息
第二步,编写User实体对象:(这里使用lombok进行了进化bean操作)
package com.lagou.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 *
 */
@Data // 生成的方法中包括了getter setter @toString
@NoArgsConstructor //生成无参构造
@AllArgsConstructor //生成全参构造
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

我们除了可以运行程序看看是否添加了方法,也可以点击这里来进行查看:

在这里插入图片描述

第三步,编写UserMapper接⼝:
package com.lagou.mapper;

import com.lagou.pojo.User;

import java.util.List;

/**
 *
 */
public interface UserMapper {
    //查询所有
    List<User> findAll();
}

第四步,编写UserMapper.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper       
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"     
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lagou.mapper.UserMapper">
    <!-- 查询所有 通过接口找到这个进行操作-->
     <select id="findAll" resultType="com.lagou.pojo.User">    
         select * from user 
    </select>
    </mapper>
第五步,编写MPTest类测试用例:
package com.lagou.test;

import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 *
 */
public class MPTest {
    @Test
    public void test1() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");      
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); 
        SqlSession sqlSession = sqlSessionFactory.openSession();      
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);    
        List<User> all = mapper.findAll();      
        for (User user : all) {         
            System.out.println(user);   
        }          
    }    
}
查看日志,会出现测试结果:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
注:在后面操作mybatis-plus时,如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName(“指定数据库表名”)
当然这是后话了,因为普通的mybatis是操作字段的,与类名并没有直接的关系,而mybatis-plus却与类名有关联
Mybatis+MP实现查询User:
第⼀步,将UserMapper接口继承BaseMapper接口,将可以拥有BaseMapper接口中的所有⽅法:
package com.lagou.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;

import java.util.List;

/**
 *
 */
public interface UserMapper extends BaseMapper<User> {
    List<User> findAll();
}

第二步,使用MP中的MybatisSqlSessionFactoryBuilder进程构建:
回到测试类MPTest,添加如下方法:
 @Test
    public void test2() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        //这⾥使⽤的是MP中的MybatisSqlSessionFactoryBuilder,才会使得操作泛型时,进行初始化操作
        //否则对应的方法是找不到的,即找不到模板
        //这里的模板也可以说是对应的MappedStatement类信息(可以理解为,生成一个xml操作),后面会说明
 SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 可以调⽤BaseMapper中定义的⽅法
        List<User> all = mapper.selectList(null);
        for (User user : all) {
            System.out.println(user);
        }
    }
测试:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
我们发现,结果一样,唯一的区别就是工厂那里变了,那么可以操作原来的mapper.findAll()吗,答:可以
正如前面说的无侵入,即只增强,我们也可以直观的看到,我们在对应的接口中,实际上拥有的很多的方法
其中自然包括我们自己写的,那么其他的方法是如何操作的呢:
虽然我们并没有写对应的配置文件,实际上他们是有固定的模板的
但模板通常是创建的,模板可以称为xml文件或者是操作的sql语句
一般是创建MappedStatement类信息,后面会说明,在后面的源码中会说明
包含了对应接口对应的语句和内容信息
当我们传递泛型时
通过反射得到泛型信息,好像只能是当成父类可以得到泛型信息
正好BaseMapper是UserMapper的父类(或者他子类的子类的父类,一路查找,后面的Sql 注入器会说明的),接口也是类的一种
所以可以这样说得到泛型信息,然后就可以给模板进行创建改变,将对应的泛型类名称或者地址等等放入对应位置
也就是使得操作的对应值基本都是对应的泛型类型,这里就是User,所以对应的模板
操作的表就是user(至于为什么不是User,后面会解释),对应的接口方法或者相关内容也都是User
这时,我们试着将User修改成Userr,那么可能在其他的模板内容修改时,没有报错
但是,在对应的数据库的sql语句中,却没有对应的表,那么自然报错,经过测试的确如此,那么我们在前面说过
操作mybatis-plus时,如果实体类名称和表名称不⼀致,可以在实体类上添加注解@TableName(“指定数据库表名”)
package com.lagou.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 *
 */
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
@TableName("user") //使得操作数据库表名的地方使用这个名称,是什么就是什么,即USer,就是USer
public class Userr {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
//发现,没有报错了

//那么有个疑问,在原来的时候,类是User,但表名在日志里面却是user
//而我们操作普通的mybatis时,是对应的表名(不会操作大小写,是什么就是什么)
//很明显mybatis-plus,将对应的在数据库的位置上的表名都进行小写了,那么真的一定都会小写吗
//那么在这之前,首先要知道,在数据库里面,是不区分大小写的,也就是说,当我们操作User表时
//对应的数据库位置是user,但在执行时,是不区分大小写的
//这些只要使得你是对应的名称即可,所以这就是为什么普通的mybatis可以区分大小写的原因
//但是虽然数据库不会区分大小写,可mybatis-plus在对应的位置上,却不一定是对应的小写
//实际上mybatis-plus会对数据库位置的名称进行改变,如果是User,那么就是user,这的确是小写了
//那么若是如下的情况呢
/*
若是USer,UserAge,UserageAAge,uSer,User
针对这五种情况,经过测试(看日志)
对应的表名分别是:u_ser,user_age,userage_a_age,u_ser,user

也就是说,除了首字母前面没有_外,其他的大写都会在前面加上_并变成小写,这样的操作简称为小写操作或者特殊小写(驼峰的操作)
所以通常mybatis-plus是默认来操作驼峰的,后面会说明

至此,我们操作UserageAAge,先执行一次,然后将数据库的表名修改成userage_a_age再次执行
那么会发现,第一次报错,第二次成功,至此结论正确,那么对应的@TableName("user")也是这样的吗
答:不是,他是直接的替换,而不会出现上面的操作,比如说@TableName("USer")中的USer
在数据库对应的位置中,也就是USer,而不是u_ser

那么还有一个疑问,如果不操作泛型呢,一般情况下,我们执行操作工厂时,因为MybatisSqlSessionFactoryBuilder工厂
会找当前项目所有的继承BaseMapper类型的接口
基本必须是接口,因为并没有类的操作,模板基本只是操作了语句和对应的信息在MappedStatement类里面
所以会找泛型,从而进行操作,而不操作泛型时,默认是Object,也就是说,表名是object
我测试了一下,若没有泛型,在初始化时就会报错
当然,有泛型,却不是对应的MybatisSqlSessionFactoryBuilder工厂,也会初始化报错,是同一个报错
因为都使得没有操作或者说创建模板
也就是说,必须要有泛型和对应的MybatisSqlSessionFactoryBuilder工厂
否则方法不让执行(方法那里报错了,使得没有找到,因为报错,使得后续不执行,自然也找不到对应的方法模板)

最后一个疑问,如果初始化报错了,那么自己写的findAll()方法可以执行吗,答:可以
因为初始化报错,只是使得对应的方法不能操作而已,但我们自己写的与初始化无关,还是在的,即findAll()方法可以执行

驼峰一般操作所有,包括查询字段,以及条件等等,即查询,增删改都会处理,具体可以在测试时发现

*/
简单说明:
由于使用了 MybatisSqlSessionFactoryBuilder进行了构建,继承的BaseMapper中的方法就载入到了SqlSession中
并在操作泛型时,会使得初始化,所以就可以直接使用相关的方法
如图:

在这里插入图片描述

在61章博客说明过,简单来说是对应存放sql语句的地方,只是当时是通过xml来得到的,而这里是模板
所以模板可以理解为创建xml然后操作,而实际上他也只是设置信息而已,就与xml一样
Spring + Mybatis + MP:
引⼊了Spring框架,数据源、构建等工作就交给了Spring管理
创建子Module,并导入对应依赖
<properties>        
    <spring.version>5.1.6.RELEASE</spring.version>  
</properties>
<dependencies>   
    
<dependency>      
    <!--springMVC坐标,含有控制器,如DispatcherServlet-->
<groupId>org.springframework</groupId> 
<artifactId>spring-webmvc</artifactId>  
<version>${spring.version}</version>   
</dependency>     
    
<dependency>       
    <!--Spring自带的使用连接池的-->
<groupId>org.springframework</groupId>   
<artifactId>spring-jdbc</artifactId>     
<version>${spring.version}</version>
</dependency>      
    
<dependency>        
    <!--Spring整合@Test,使得可以使用注解指定配置类或者配置文件-->
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>    
<version>${spring.version}</version>    
</dependency>   
    
</dependencies>
对应目录如下:

在这里插入图片描述

实现查询User:
第一步,编写jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=GMT%2B8&useSSL=false
jdbc.username=root
jdbc.password=123456
第二步,编写applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
    <!--引⼊properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--dataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--这⾥使⽤MP提供的sqlSessionFactory,完成spring与mp的整合-->
    <bean id="sqlSessionFactory"
          class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--扫描mapper接⼝,使用的依然是mybatis原⽣的扫描器
因为对应的接口还是需要一样的扫描的,且我们只改变了工厂-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lagou.mapper"/>
    </bean>
</beans>
第三步,编写User对象以及UserMapper接口:
package com.lagou.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 *
 */
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

package com.lagou.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;

import java.util.List;

/**
 *
 */
public interface UserMapper extends BaseMapper<User> {
    List<User> findAll();
}

第四步,编写TestSpringMp类测试用例:
package com.lagou.test;

import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestSpringMp {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        List<User> all = userMapper.selectList(null);
        System.out.println(all);
        //这里需要提一下,如果说,我们到表是操作了上面的特殊小写,那么表到类,就是会反过来的,所以这里要注意
          //当然这是mybatis-plus的原因,而mybatis本身并不会这样
          //但并不是完全一样,只是只有去除了下划线而已,因为字段不区分大小写
          //所以对应的方法或者变量需要操作去掉下划线的字段
               //并且有对应get和set(忽略大小写),那么使用,否则就操作变量赋值,并且由于他们之间的执行是互相的,所以必然可以对应

    }
    
    //findAll()现在还不能操作,因为我们没有他的配置文件,所以我们只能使用BaseMapper接口的方法

}



执行测试,发现,有对应的结果了
SpringBoot + Mybatis + MP:
使用SpringBoot将进⼀步的简化MP的整合(自动配置,就不需要编写对应的配置文件了),需要注意的是
由于使用SpringBoot需要继承parent,所以需要重新创建工程,并不是创建子Module
创建工程(不知道Spring boot可以到88章博客里学习),并导入依赖:
 <dependencies>       
 <dependency>     
     <!--spring boot的基本依赖,虽然其他的依赖基本有他,所以可以不写-->
 <groupId>org.springframework.boot</groupId>     
 <artifactId>spring-boot-starter</artifactId>        
 <exclusions>              
 <exclusion>               
       <!--该标签一般是排除的意思(即不使用他的依赖,那么在maven中看不到了)
使得我们导入的日志文件可以起作用,而不是使用他的,即他会覆盖我们的日志依赖(只是部分主要功能,比如后面的slf4j-log4j12依赖),但不是没有使用他(优先原则)
即还是导入的
因为虽然我们导入了,但是部分主要功能被覆盖了,使得不会出现对应的打印信息
而他的,通常不会打印出对应的信息,比如后面的sql语句的信息,所以这里就进行排除了,他通常用来打印启动项目信息
我们导入的下面的日志依赖(slf4j-log4j12)基本不会有操作该信息,所以各有优点吧

注意如果有log4j.properties文件(名称必须一致),他们两个都会使用,否则没有的话,我们的slf4j-log4j12不操作,而spring-boot-starter-logging操作他自带的,所以实际上spring-boot-starter-logging也是操作日志的,否则什么日志都没有,可以删除slf4j-log4j12依赖,然后排除pring-boot-starter-logging就知道了,而slf4j-log4j12不操作也是如此
-->
 <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-logging</artifactId>   
  </exclusion>          
  </exclusions>       
  </dependency>      
  <dependency>       
        <!--使得可以进行测试spring boot,这个@SpringBootTest需要这个依赖-->
  <groupId>org.springframework.boot</groupId>       
  <artifactId>spring-boot-starter-test</artifactId>  
  <scope>test</scope>    
  </dependency>
      <!--简化代码的⼯具包-->
     <dependency>            
         <groupId>org.projectlombok</groupId>    
         <artifactId>lombok</artifactId>       
         <optional>true</optional>  
     </dependency>
       <!--mybatis-plus的springboot⽀持,类似于mybatis一样的
好像也是对应的去迎合spring boot,而不是spring boot自带的我们-->
      <dependency>           
          <groupId>com.baomidou</groupId>      
          <artifactId>mybatis-plus-boot-starter</artifactId>   
          <version>3.1.1</version>    
              <!--通常说,不同的版本可能代码的显示不同,或者方法不同
比如对应的结果中where后面用()括起来,比如是3.3.2版本就会括起来
且对应的方法可能参数不一样,比如map的分页,3.3.2是纯IPage类(不加泛型的,即参数不能加泛型)
而3.1.1是IPage(子类是Page),有泛型,即参数可以加泛型,也可以不加,这里考虑了泛型的赋值,百度了解即可
3.1.1版本不会使用()括起来
但是实际上可能相同的版本显示也是不同
因为他并不是不变的(就相当于你写博客时,难道不会修改吗,所以对应的mybatis-plus的官方可能也是会修改的)
简称为维护,注意即可-->
     </dependency>
      <!--mysql驱动-->
       <dependency>        
           <groupId>mysql</groupId>    
           <artifactId>mysql-connector-java</artifactId>  
           <version>5.1.47</version>    
     </dependency>
     <!--操作日志-->
      <dependency>      
          <groupId>org.slf4j</groupId>        
          <artifactId>slf4j-log4j12</artifactId> 
     </dependency>
       <dependency>
           
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
      </dependencies>
 <!--maven插件-->
 <build>       
     <plugins>         
         <plugin>          
             <groupId>org.springframework.boot</groupId>      
             <artifactId>spring-boot-maven-plugin</artifactId>   
         </plugin>    
     </plugins>  
</build>
对应的目录(有部分修改的,如启动类移动到lagou包下):

在这里插入图片描述

对应的log4j.properties文件:
log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
编写application.properties文件:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#密码记得要正确,不要在数后面加上#,否则#当成数,而不是注释,通常说明是不要在=后面写
编写User类:
package com.lagou.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 *
 */
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

编写UserMapper接口:
package com.lagou.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;

import java.util.List;

/**
 *
 */
public interface UserMapper extends BaseMapper<User> {
    List<User> findAll();
}

编写启动类LagouMpSpringbootApplication:
package com.lagou;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.lagou")
public class LagouMpSpringbootApplication {

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

}

编写测试用例LagouMpSpringbootApplicationTests:
package com.lagou;

import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.junit.jupiter.api.Test; 
//该导入,会使得没有@Test也可以执行方法
//即有对应的执行按钮,只是什么都不操作,其他的作用具体到88章博客里面查看
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;


@RunWith(SpringRunner.class)
@SpringBootTest
class LagouMpSpringbootApplicationTests {

    @Autowired
    private UserMapper userMapper; 
    //若出现爆红,不需要管,这是idea认为没有注入,而出现的爆红
    //但实际上运行时,是注入的

    @Test
    void testSelect() {

        List<User> all = userMapper.selectList(null);
        System.out.println(all);
        //查询表,指定变量字段
        
    }

    //对应的findAll()方法,在这里也执行不了,虽然看不到报错(spring boot使得的)
}

启动后,可能找不到结果,将日志文件删除
因为操作spring boot时,再操作日志,可能会将对应打印信息覆盖了,可能都不会显示
或者修改log4j.properties文件(甚至可以看到sql语句,但不一定,可能受版本影响,当然,如果实在不会显示,可以试着操作百度,因为大多数使用框架时,某些实现操作,或者框架本身若没有对应sql语句日志信息,是因为需要某些配置才可,具体就只能百度了,因为框架的操作是看他怎么进行的):
log4j.rootLogger=info,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
log4j.logger.com.lagou.mapper=TRACE
百度的类似的结果(一般可以打印出sql语句):
mybatis-plus:
  global-config:
    banner: false
  db-config:
    id-type: ASSIGN_ID
    table-underline: true
    logic-delete-value: true
    logic-not-delete-value: false
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    call-setters-on-nulls: true
删除或者修改后,继续启动,那么就找到结果了
具体的sql显示,在不同组合下,一般都会有配置,通常如果不知道的话,建议百度看看
通⽤CRUD:
通过前⾯的学习,我们了解到通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们将详细讲解这些操作

在这里插入图片描述

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> wrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);

    IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
}

插入操作(插入一条记录):
方法定义:
//插⼊⼀条记录
//entity:实体对象,T一般我们都会指定,否则操作不了,因为即初始化不了
int insert(T entity);
在上面的spring boot项目中的LagouMpSpringbootApplicationTests测试类里面加上如下方法:
 /*
    测试添加
     */
    @Test
    public void testInsert(){
        User user = new User();
        //user.setId(9l); 因为long类型,需要加上l
        user.setName("哈哈哈");
        user.setAge(18);
        user.setEmail("zimu@jajaja");
        //返回值自然也是影响行数
        int insert = userMapper.insert(user); //整合spring自动提交(springboot一般包括spring的,即整合springboot基本也是一样的)
        System.out.println(insert); //1

        System.out.println("id值为:"+user.getId()); 
          //1562065619489136641(其中一个,一般与时间戳有关,所以你的和我的基本是不一样的,除非能改变时间戳,所以通常这里与UUID有关)
        //该值是整型的(会根据自身的值来决定类型,这是特殊的,所以这里会默认加上l,因为超过了int类型,使得认为是long类型)
        //而不是字符串,也就是说,如果对应的id是String类型,那么就会报错
        //且也要满足长度的原因,所以Integer也不行
     //所以这里就使用Long类型了(该值认为自己是long类型,且Long默认是null,所以可以会操作生成的值进行赋值)
        //否则会变成0的,所以通常在不考虑自增的前提下,这里也基本只能是Long类型

        //注意:数据库的字段需要可以存放他这样的,否则不行,即会报错
        //且如果不设置的字段是基本类型,那么一般默认是0,那么返回的也是0,因为不是null,即一般也会加上该字段,所以是返回0,即通常是null导致不加的
        //而不加,那么就操作生成了,或者后面的不加上字段(比如查询)

        //注意:对应的数据库的主键并没有操作自增,且因为大小写忽略,所以字段是不能出现同一个的
        //即Id和id不可以同时存在,在创建表时就会报错,而我之所以会说明,是为了后面的解释
        
        //我们可以看到,我们这里并没有编写主键,为什么可以运行呢:
        /*
        实际上,当我们没有设置id值,那么他会操作类的变量为id(忽略大小写)
        然后自动的根据时间戳的值来加上(给)对应的id(赋值),在生成之前
        甚至可能会检查是否有对应的id存在,但一般不会,所以可能也有冲突,但基本不会出现
        在生成后,那么会放入对应的sql语句中,并且对应sql语句字段
        都是类操作的变量(初始化时就决定了),然后对应的数据放在对应的位置
        且变量放入对应位置时都是对应的小写操作,即Name就是name,且NAme,也是n_ame,会加上"_",即也是特别处理
        但是这里的id是特例,他是什么就是什么,即ID也就是ID,前提是,没有设置策略给其他(没有覆盖)
        那么id就会操作特殊小写了,如单独的给其他变量设置自增策略,那么ID也就是i_d,即也操作了特殊小写
        那么如何看到sql语句呢
        实际上只需要修改变量名,使得报错即可,那么就可以看到了,或者修改了log4j.properties文件,前面的修改
        
        那么有个疑问,如果设置了id呢,那么就是使用id的
        
        那么如果,没有id呢,且不设置对应的变量值,那么会说明,没有设置的不能操作自动的添加id值
        
        会提示没有id,因为主键不能是null,且必须添加
        
        如果没有id,且都设置了值,那么就是操作对应的设置值的sql语句,这时就要看数据库了
        
        这就是对应的id细节
        
        所以说,对应的id生成是针对于id这个数来操作的,即该策略针对于id这个数
        或者说,只要你没有设置id的值,那么我帮你生成
        
        
        那么为什么会设置id值呢,我们也可以说明在执行之前生成,这是一个解释,实际上也是如此
        那么是如何得到返回的id值的,可以理解为执行两个sql语句,先添加,然后获取
        具体的方式,我们可以参照62章博客的内容即可,更具体一点,可以理解为第二个sql操作的是
        select last_insert_id()语句,该函数可以获得最后一次添加的内容的主键
        由于自增一般有范围限制,可以手动添加过大的数,然后再使得自增就知道了
        可能会回来,也有可能会增加上限(刷新即可,大概是显示的问题,还没有转换)
        所以在一定的数量下,我们一般不会使用这个
        而是使用语句进行查看或者使用自己生成的
        当然通常并不会出现这种情况,因为是显示,所以也是会使用的自增的
        所以那么是操作这个吗,答:不是
        因为last_insert_id()函数虽然可以获得最后一次添加的内容的主键(这个时候,数据库是否是自增就没有什么关系了,只要是主键即可)
        但是自己添加的不算,也就是直接添加的不算
        只能是因为自动添加的最后一个,所以也通常操作自增
        自增也使得该字段必须是主键,所以整体来说是获得最后一次添加内容的主键
        
        而这里虽然是主键,但不是自增,所以操作不了(一般说的是当前类没有自增策略的),那么既然也是操作sql语句
        那么是否也是通过直接查询自动生成的id并得到(除非你已经设置过了,若设置过了自然是返回自己设置的)
        还是直接将生成的赋值呢
        大概率是直接将生成的赋值,因为这样是最方便的,虽然可能对应的数据库,但通过有风险
        万一数据库在添加后的一瞬间被修改了,那么结果是不一致的,当然,这是正常情况,因为我们只需要返回添加的id
        而不是最终稳定的结果,所以是没有影响的,所以实际上基本也就是操作一个sql语句
        */
        
        //最后注意:后面的操作的默认的id(如顺序)中,基本都是忽略大小写的,注意即可
        //或者说只要是操作id的,那么基本都是忽略大小写,这里也称为id数(id数:表示该id忽略大小写)
    }
注意:如果编写代码时,没有提示,那么一般是该插件,在编译期不起作用,而在运行期起作用
而我们需要对应注解在编译期起作用,就需要点击如下:

在这里插入图片描述

点击右上角的Enable开头的即可,但通常情况下,插件基本都是在编译期可以起作用的
这个只是针对于部分不在编译期起作用的插件,注意即可
至此插入数据完毕
我们可以查看数据库,可以看到,数据已经写⼊到了数据库,但是,id的值却并不好
因为我们期望的是数据库自增长,而这里是MP生成了id的值写入到了数据库
如何设置id的生成策略呢?
MP支持的id策略:
通过ctrl+n查找IdType类:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.annotation;

public enum IdType {
    //数据库ID⾃增
    AUTO(0),
    //该类型为未设置主键类型
    NONE(1),
    //⽤户输⼊ID,该类型可以通过⾃⼰注册⾃动填充插件进⾏填充
    INPUT(2),
    
    //以下3种类型、只有当插⼊对象ID 为空,才⾃动填充
    
    //全局唯⼀ID (idWorker),这个是默认的策略,一般默认给id数(前提是没有策略)
    ID_WORKER(3),
    //全局唯⼀ID (UUID)
    UUID(4),
    //字符串全局唯⼀ID (idWorker 的字符串表示)
    ID_WORKER_STR(5);

    private final int key;

    private IdType(int key) {
        this.key = key;
    }

    public int getKey() {
        return this.key;
    }
}

那么如何操作自增策略呢,自然需要让我们的泛型那个类进行操作,使得初始化时进行设置,即:
package com.lagou.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 *
 */
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    
    //使得初始化时,操作自增的策略,即AUTO(0),而不是默认的全局唯一ID策略,即ID_WORKER(3)
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    
    //只要我们设置了策略,那么默认的策略就变成了该策略,无论是哪个变量设置,即对方也变成了该设置的对应
    //当然默认的是默认操作id数
    //这里需要我们指定给谁设置
//注意:自增可能会达到数据库的最大,那么数据库通常会从小到大的或者从大到小的过去,比如0,1,2,再0,1,2

}

由于该策略使得不会添加字段,那么设置后,无论我们是否设置了对应的id值
都不会当成字段来添加,所以这也使得对应的数据库字段也通常要自增,这里可能操作了last_insert_id()函数
但也可能是sql语句得到,通常是前者,总体来说就是,使用该策略,就不参与添加字段
但要注意,该注解操作谁,那么该last_insert_id()函数返回的值,就是给谁的,即会覆盖原来有的值
所以也使得,对应表id的值,返回给了不是对应的变量,注意即可
/*
比如
INSERT INTO USER (id, NAME, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com')
变成了
INSERT INTO USER (NAME, age, email) VALUES ('Jone', 18, 'test1@baomidou.com')

id不参与了,当然,他并不是只操作id(不与前面的生成策略一样),所以其他字段也可以加上该注解


只是该注解只会使得第一个操作的注解起作用,其他的注解并不会起作用,所以其他字段就算加上了
也会参与字段,所以相当于没有该注解

最后注意:当没有设置值时,默认null的值,不会加上字段
因为这里默认是null,若默认不是null,那么会加上,如将age修改成int类型,那么就会加上,因为默认是0,而不是null
除了策略的作用,如前面的全局唯一ID策略,操作的id,我们实际上可以认为
默认给类的id数加上全局唯一ID策略的,除非没有id数,或者除非有其他策略,那么就不会加上
因为可以通过判断是否是null,而操作是否加上,这里可以参照62章博客即可
*/
至此,我们添加后,可以看到,数据库有数据了
@TableField:
在MP中通过@TableField注解可以指定字段的⼀些属性,常常解决的问题有2个:
1:对象中的属性名和字段名不⼀致的问题
一般情况下,类和表通常是驼峰关系(因为驼峰一般会操作加上"_",前面说过的特殊小写就是驼峰),那么默认是不需要操作的
如果类和表不是驼峰关系,即操作非驼峰或者设置了不驼峰的,就需要进行解决了,即操作@TableField注解
2:对象中的属性字段在表中不存在的问题
使用:
package com.lagou.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 *
 */
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    @TableField(select = false) //表示查询时,不返回该字段的值,即不在对应位置加上该字段变量,true则加上
    //所以导致不会返回
    //注意是查询,即操作查询时,不加上对应的该字段,但是其他的操作如添加会加上
    //相当于部分的@TableField(exist = false) ,即这里只操作查,增删改不操作,即增删改会加上该字段
    //而查询不会加上该字段
    private String name;
    private Integer age;

    @TableField(value = "eMail") //解决字段名不一致问题
    private String mail;
    //在sql语句中是这样的:eMail AS mail
    //也就是说,eMail是什么就是什么(没有特使的操作,如小写操作)
    //且变量变成了别名
    
    @TableField(exist = false) 
    //表示该字段,在数据库表中不存在,实际上是不加上对应的字段,所以这样说明,默认为true,而设置为false时
    //自然不会在初始化时,在对应的位置加上
    //而设置为true时,由于数据库没有该字段,那么当然是报错的(数据库没有该字段)
    //他作用与所有的操作,即增删改查
    private String address;
    
}

至此,我们测试即可
/*
现在我们将关键信息放在这里,以后基本需要参照这些信息

类名对应表名,按照特殊小写(或者说小写操作)触发
变量对应字段,按照特殊小写(或者说小写操作)触发
其他的设置名称,或者操作对应策略的字段(比如全局唯一ID策略操作的变量)
那么是什么就是什么(如USer,就是USer,而不是u_ser)

没有设置值或者操作了对应策略(比如自增策略),那么默认不加上字段
除了操作的字段(比如全局唯一ID策略操作的变量)

特殊小写:NAme,就是n_ame,User,就是user,UsEr,就是us_er

在mybatis-plus里面,上面的解释,基本贯彻所有
*/
更新操作:
在MP中,更新操作有2种,⼀种是根据id更新,另⼀种是根据条件更新
根据id进行更新:
方法定义:
//根据 ID 修改
//entity:实体对象
int updateById(@Param("et") T entity);
测试:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
    测试根据id进行修改
     */

    @Test
    public void testUpdateById(){
        
    
        User user = new User();
        user.setId(6l); //主键,这里是where后面的条件
        user.setAge(30); //更新的字段,这里是set后面的条件
        //上面针对于更新(update)

        //将id为6的age修改成30
        int i = userMapper.updateById(user);
        System.out.println(i);
        //符合不设置,不加字段

    }

//注意:这里有个细节,我们是怎么确定,对应的where后面的字段的,答:根据顺序来
//顺序是:策略设置者-id数-null
//后面大多数都是这个顺序,除了特别的会使得报错外(如AR的添加和更新并存的insertOrUpdate方法)
//即我们给那个字段操作了策略,那么后面的就是那个字段,比如这里就是id
//那么同样的,如果给name设置该策略(该策略是自增策略,所以只会第一个生效),那么后面的就是name
//如果都没有设置,那么默认为id变量,没有id变量,默认是null字段,而默认是null时,那么自然的,是必定失败的
//因为我们虽然设置了值,但是获得值时,一般需要对应的get方法或者赋值,具体看对应的61章博客介绍的#{}
//而没有null这个变量,因为不能创建,自然报错
进行测试,发现,修改了对应的值
根据条件进行更新:
//根据条件,更新记录
//entity:实体对象 (set 条件值,可以为 null,前提是另外一个参数操作了set,否则会报错,语法错误)
//因为sql语句中"update 表名"后面基本需要指定set才可
//即UpdateWrapper类可以为null,但QueryWrapper类不可以为null
//updateWrapper:实体对象封装操作类,可以为 null,那么就相当于代表所有的进行修改
//⾥⾯的 entity(T,也是实体对象,只是统称为entity)
//通常⽤于⽣成 where 语句或者set语句,主要的区别就是set语句的操作(set方法,这是主要的区别,因为我主要用来更新的,而其主要用来查询)
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
测试:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
    测试根据条件进行修改
     */

    @Test
    public void testUpdate(){
        User user = new User();
        //更新的字段
        user.setAge(35);

        //更新的条件
        //QueryWrapper是Wrapper的子类,基本只能操作where后面的条件
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper.eq("name","解决");
         //eq代表操作"=",即这里相当于name="解决"的条件

        int i = userMapper.update(user,objectQueryWrapper);
        System.out.println(i);
        
        //将name为"解决"的age修改成35

    }

//即where后面的是name,set后面的是age,其中name是什么就是什么,当然age是操作特殊小写的
//最后注意:set后面不会操作id,即自动的不加上id这个数,所以这里设置id的值没有用
//或者说,因为我们指定了条件,所以覆盖了id条件

  /*
    测试根据条件进行修改
     */

    @Test
    public void testUpdate2(){

        //换一个对象,UpdateWrapper是Wrapper的子类
        //该对象不止可以操作where后面的条件,也可以操作set后面的条件
        UpdateWrapper<User> objectUpdateWrapper = new UpdateWrapper<>();
        objectUpdateWrapper.eq("id","6").set("age",40);

        int i = userMapper.update(null,objectUpdateWrapper); //设置为null,代表只会以条件来,而不会参照类,如果都存在,那么以条件为主
        System.out.println(i);

        //将id为6的age修改成40,即这里的eq变成了where后面的条件,后面的set方法设置set后面的条件
        //注意:这里的id可以写在set后面,不会不加上
    }
至此,进行测试,发现修改了对应的值
上面两种操作均可达到更新的效果,其中eq方法可以看成是操作where后面的条件的,而set方法是操作set后面的条件
关于wrapper更多的用法,以后可能会继续介绍
删除操作(根据id进行删除):
//根据 ID 删除
//id:主键ID
int deleteById(Serializable id);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
    根据id进行删除
     */
    @Test
    public void testDeleteById(){

        int i = userMapper.deleteById(6l);
        System.out.println(i);
    }

//注意:在有设置策略时,默认设置的那个策略为where后面的条件操作,否则默认为类里面的id变量为对应的条件
//其中6l就是对应id的值,只要符合方法参数和数据库类型基本就可以

至此,进行测试,发现删除了对应的值
删除操作(根据map集合进行删除):
//根据 columnMap 条件,删除记录
//columnMap:表字段的 map 对象
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
  根据columnMap进行删除
   */
    @Test
    public void testDeleteByMap(){

        HashMap<String, Object> map = new HashMap<>();

        map.put("name","哈哈哈");
        map.put("age",35);
        //name在前,age在后,按照顺序(ASCII大小,大的在前),后面或者前面默认也是如此,注意即可
        //一般操作map的基本都是如此,因为map自动的排序了,所以根据map从头到尾获取,自然是先操作name
        //将map集合当成条件(where后面的)来进行删除,而不是使用类的变量,当然,多个条件自然也是and连接的关系
        int i = userMapper.deleteByMap(map);
        System.out.println(i);
    }

//那么有个疑问,如果值是null呢,那么加上IS NULL,如name IS NULL
//前面或者后面也是如此(下面或者上面的介绍也是)
//这是操作map集合的作用
//其他的如eq,gt,set,等等,结果就是null(字符串),而没有操作IS NULL,即以name为例,就是
//name = null,name >null,set name = null这三种情况
//若是类的设置为null,那么不会加上字段
//实际上无论你设置什么,结果是以字符串的形式出发的,只是类和map会进行检查而已
至此,进行测试,发现删除了对应的值
删除操作(根据wrapper实体对象进行删除):
//根据 Wrapper条件,删除记录
//entity:(实体类,或者T泛型的解释)
//wrapper:实体对象封装操作类(可以为 null,那么相当于没有条件,即也就是相当于删除所有)
int delete(@Param("ew") Wrapper<T> wrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
   /*
  根据wrapper进行删除
   */
    @Test
    public void testDelete(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        //操作where后面的条件
        objectQueryWrapper.eq("name","33").eq("age",18);

//        User user = new User();
//        user.setName("33");
//        user.setAge(18);
//        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>(user);
//       //这样也可,相当于objectQueryWrapper.eq("name","33").eq("age",18); 
        //执行类似的eq方法或者gt方法,通常会返回调用者objectQueryWrapper,使得可以继续操作
//       //只是对应的字段会操作特殊小写,因为是类

        int i = userMapper.delete(objectQueryWrapper);
        System.out.println(i);
    }

//我们可以发现,为什么明明是调用方法,为什么会使得对应的MappedStatement类信息发生了改变(字段变化)
//可能是方法也会在执行之前,修改MappedStatement类信息吧
至此,进行测试,发现删除了对应的值
删除操作(根据id进行批量删除):
//删除(根据ID 批量删除)
//idList:主键ID列表(不能为 null 以及 empty)
//而之所以不能是empty或者null,那是因为in后面括号里基本识别不了,或者在执行前就操作了异常
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
   /*
    根据deleteBatchIds进行删除
     */
    @Test
    public void testdeleteBatchIds(){
        
        int i = userMapper.deleteBatchIds(Arrays.asList(17,18));
        System.out.println(i);
        
        //删除17和18这两个数据,该deleteBatchIds一般操作的是in来进行删除,如in(17,18)
        //对应操作的where后面的字段,也是根据前面说的:策略设置者-id数-null
    }

至此,进行测试,发现删除了对应的值
这里说明一下,前面说的不能是null或者实体类的,那么就不能是null或者实体类
大概是语法问题,比如in后面的括号是空的或者不识别,或者框架操作问题,操作了异常
而没有说明或者说明可以是null或者实体类的,则基本都可以是null或者实体类,因为具体是看条件的
即null基本代表没有条件(比如在查询时,就相当于查询所有了,但是更新的话,有些需要set,那么可能会报错,语法错误)
因为sql语句中"update 表名"后面基本需要指定set才可
实体类代表有他的条件
但要注意空实体类操作默认的值是否符合sql语法(会操作get方法),如根据id进行修改)
或者字段的是否添加(不操作get方法),如根据条件进行修改,等等
注意即可,后面基本也是如此
查询操作:
MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作
查询操作(根据id进行查询):
//id:主键ID
T selectById(Serializable id);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
  /*
    根据id进行查询
     */
    @Test
    public void testSelectById(){

        User user = userMapper.selectById(15);
        System.out.println(user);

        //对应操作的where后面的字段,也是根据前面说的:策略设置者-id-null


    }
至此,进行测试,发现查询了对应的值
查询操作(根据id进行批量查询):
//idList:主键ID列表 (不能为 null 以及 empty)
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
  /*
   根据id进行批量查询
    */
    @Test
    public void testSelectBatchIds(){

        List<User> users = userMapper.selectBatchIds(Arrays.asList(4, 5));

        System.out.println(users);

        //也是操作in


    }
至此,进行测试,发现查询了对应的值
查询操作(根据map集合进行查询):
//根据 columnMap 条件,查询记录
//columnMap:表字段的 map 对象
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
  /*
   根据map集合进行查询
    */
    @Test
    public void testSelectByMap(){

        HashMap<String, Object> map = new HashMap<>();
        map.put("name","Jack");
        List<User> users = userMapper.selectByMap(map);

        System.out.println(users);


//也是将map集合看成条件,是什么就是什么

    }
至此,进行测试,发现查询了对应的值
到这里,大概有个对Mybatis-plus的一定了解了,可以知道,我们并不需要编写对应的xml配置文件
他自动的帮我们进行了相关的操作,也就相当于帮我们写了xml文件了,这使得方便许多
查询操作(根据entity条件,查询⼀条记录):
//根据 条件,查询⼀条记录
//entity(实体类,或者T泛型的解释)
//queryWrapper:实体对象封装操作类(可以为 null)
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
    /*
测试SelectOne
 */
    @Test
    public void testSelectOne(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("name","Jack");

        //根据条件查询一条记录并返回,如果超过一条记录,会报错,大概是判断查询的结果,进行操作异常造成的
        User user = userMapper.selectOne(objectQueryWrapper);
        System.out.println(user);


    }
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询总记录数,即数量):
//根据 Wrapper条件,查询总记录数
//queryWrapper:实体对象封装操作类(可以为 null)
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
  /*
根据 Wrapper条件,查询总记录数
*/
    @Test
    public void testSelectCount(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.gt("age","18"); //操作大于18的数据
        //gt代表操作>,即这里相当于age>18的条件
      
        //对应的方法只要不是set,那么基本都是操作where后面的操作

        //返回总条数
        int user = userMapper.selectCount(objectQueryWrapper);
        System.out.println(user);

        //相当于操作了count(1),所以可以查询所有记录,具体的可以到36章博客进行查看


    }
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询满足条件的所有记录,这里并不是记录数量):
//根据 queryWrapper 条件,查询全部记录
//queryWrapper:实体对象封装操作类(可以为 null)
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
   /*
根据 Wrapper条件,查询满足条件的所有记录
*/
    @Test
    public void testSelectList(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.gt("age","18"); //操作大于18的数据



        //返回满足条件的所有记录
        List<User> user = userMapper.selectList(objectQueryWrapper);
        System.out.println(user);


    }
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询满足条件的所有记录,并将根据条件的查询的结果放入map集合中):
//根据 queryWrapper 条件,查询全部记录,并放入map集合中
//queryWrapper:实体对象封装操作类(可以为 null)
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
/*
    根据Wrapper条件,查询满足条件的所有记录,并将根据条件的查询的结果放入map集合中
     */
    @Test
    public void testSelectMaps(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.gt("age","18"); //操作大于18的数据



        //将所有的满足条件的所有记录,并放入map集合中
        //而不是类中(前面操作的都是类,所以就没有进行数码,这里有点特别,就说明一下)
        List<Map<String, Object>> user = userMapper.selectMaps(objectQueryWrapper);
        for(Map m : user) {
            System.out.println(m);
        }
        
        //注意:查询的结果根据对应的ASCII进行排序,一般越大,越在前,如
        /*
            {mail=test3@baomidou.com, id=3, age=28}
            {mail=test4@baomidou.com, id=4, age=21}
            {mail=test5@baomidou.com, id=5, age=24}
		
		m的最大的,然后是i,最后是a,可以试着将m修改成b,那么就是如下:
		
            {id=3, bail=test3@baomidou.com, age=28}
            {id=4, bail=test4@baomidou.com, age=21}
            {id=5, bail=test5@baomidou.com, age=24}
            
            这时就是i>b>a了,发现的确如此
        */

    }

至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,将满足条件的所有记录中,根据顺序:操作策略者-id数-查询条件的第一个,来进行返回数据):
//根据 queryWrapper 条件,查询全部记录,并根据顺序:操作策略者-id数-查询条件的第一个,来进行返回数据
//queryWrapper:实体对象封装操作类(可以为 null)
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
  /*
    根据Wrapper条件,将满足条件的所有记录中,根据顺序:操作策略者-id数-查询条件的第一个,来进行返回数据
     */
    @Test
    public void testSelectObjs(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.gt("age","18"); //操作大于18的数据



        //将满足条件的所有记录中,根据顺序:
        //"操作策略者(也就是设置策略者)-id数-查询条件的第一个",来进行返回数据
        List<Object> list = userMapper.selectObjs(objectQueryWrapper);
        for(Object m : list) {
            System.out.println(m);
        }

        //即上面的m就是查询的所有id的值,当成一个集合,根据顺序,这里也就是id

    }
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询全部记录,并可以操作分页):
//page:分⻚查询条件
//queryWrapper:实体对象封装操作类(可以为 null)

//实际上queryWrapper条件一般是操作where后面的,当然,有些他的子类,是可以操作set
//如QueryWrapper是Wrapper的子类,基本只能操作where后面的条件
//但UpdateWrapper虽然也是Wrapper的子类,该对象不止可以操作where后面的条件,也可以操作set后面的条件
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
在测试之前,我们需要配置分页插件:
在lagou包下,创建config包,并在里面创建MybatisPlusConfig类:
package com.lagou.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 *
 */

@Configuration
public class MybatisPlusConfig {

    /*
    分页插件
     */

    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }


}
//该分页插件,使得数据合理化,即对应的总条数或者总页数使得计算
//并计算得出当前页数据,否则一般会使得当前页数的那个方法,是总数据,即不会计算
//在测试时,可以试着将@Configuration注释掉,再次测试即可知道了

//在mybatis-plus里面一般需要这个,他的底层需要这个操作分页的计算
//若不操作注入的方式,那么需要给mybatis添加插件来使得计算
//即使用<plugins>标签,后面会说明,或者在62章博客里查看
//而之所以可以操作注入,是因为对应的PaginationInterceptor类是mybatis-plus里面的
//整合了spring boot,并且有对应的分页操作,他可以操作类和配置
//而mybatis的PageHelper分页,一般只能是配置才可计算,就算整合了spring也差不多也要配置,而不是类
//当然我们也可以自己操作插件,使得,可以操作配置或者类,后面插件部分会进行说明
//一般来说,这里并不是操作limit,而是分析数据进行操作
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
    分页查询
      */
    @Test
    public void testSelectPage(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.gt("age","18"); //操作大于18的数据


        //参数1:当前页,参数2,每页显示条数
        Page<User> page = new Page(1,2); //Page是对应的mybatis-plus里面的

        //这里Page<User>可以不指定泛型,直接的Page也可,反正是赋值,只看对象
        //但指定了泛型,需要是对应的类型,否则报错
        //记住泛型只在编译期起作用,运行期不区分什么类型了,相当于Object了
        //单纯的不指定,可以赋值指定的,因为默认结合,而不能是已经指定不同的了,所以可以这样写

        IPage<User> iPage = userMapper.selectPage(page, objectQueryWrapper);
        System.out.println("总条数:" + iPage.getTotal());
        System.out.println("总页数:" + iPage.getPages());

        System.out.println("当前页的分页数据:" + iPage.getRecords());

    }
至此,进行测试,发现查询了对应的值
查询操作(根据Wrapper条件,查询全部记录,并可以翻页,只是将map进行接收数据了,而不是类):
//page:分⻚查询条件
//queryWrapper:实体对象封装操作类(可以为 null)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
   分页查询,只是数据是用map集合保存,而不是类
     */
    @Test
    public void testSelectMapsPage(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.gt("age","18"); //操作大于18的数据


        //参数1:当前页,参数2,每页显示条数
        Page<User> page = new Page(1,2);


        IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, objectQueryWrapper);
        System.out.println("总条数:" + mapIPage.getTotal());
        System.out.println("总页数:" + mapIPage.getPages());

        System.out.println("当前页的分页数据:" + mapIPage.getRecords()); //显示的是map集合的数据
        //也按照"操作策略者(也就是设置策略者)-id数-查询条件的第一个",来进行返回数据

 //注意:不同的版本,对应方法的参数可能不同,特别的,在3.4.0中上面是: <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
//也就是说,我们只能给出Page page = new Page(1,2);,否则编译期会报错的,当然,再怎么说,只考虑泛型的,即对应的类型已经是指定好的,并不需要加上一些泛型(他们并不指定操作的类)


    }
至此,进行测试,发现查询了对应的值
SQL注入的原理(不是对应的进入数据库的手段,而是mybatis-plus为什么有对应的sql注入到MappedStatement里面的解析):
前⾯我们已经知道,MP在启动后会将BaseMapper中的⼀系列的方法注册到meppedStatements(也就是MappedStatement)中
只是这里加上s,是可能对应的变量是保存所有的MappedStatement的,所以也可以这样说
那么究竟是如何注入的呢,流程又是怎么样的,下面我们将⼀起来分析下:
在MP中,ISqlInjector负责SQL的注入工作,它是⼀个接口,AbstractSqlInjector是它的实现类,实现关系如下:

在这里插入图片描述

回到如下:
/*

在测试类LagouMpSpringbootApplicationTests中,找到如下:

    根据id进行查询

    @Test
    public void testSelectById(){
断点到这里,并不是他执行了,而是准备执行(需要你点击下一步来执行)
不要以为对应的初始化是该方法的执行,注意即可
   在这里打上断点     User user = userMapper.selectById(15);
        System.out.println(user);

        //对应where后面的也是:指定策略者-id-null


    }
    
    使用ctrl+n,找到ISqlInjector,并找到他的实现类AbstractSqlInjector,如下:
    
     public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
在这里打上断点        Class<?> modelClass = this.extractModelClass(mapperClass);
        if (modelClass != null) {
            String className = mapperClass.toString();
            Set<String> mapperRegistryCache = 
            GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            if (!mapperRegistryCache.contains(className)) {
                List<AbstractMethod> methodList = this.getMethodList();
                if (CollectionUtils.isNotEmpty(methodList)) {
                    TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, 
                    modelClass);
                    methodList.forEach((m) -> {
                        m.inject(builderAssistant, mapperClass, modelClass, tableInfo);
                    });
                } else {
                    logger.debug(mapperClass.toString() + ", No effective injection method was 
                    found.");
                }

                mapperRegistryCache.add(className);
            }
        }

    }


断点打完后,执行testSelectById(debug运行),会发现,首先跳转到
Class<?> modelClass = this.extractModelClass(mapperClass);

我们下一步,看看modelClass,发现,就是我们的实体类对象class信息
继续下一步  
String className = mapperClass.toString();
返回我们接口的地址信息和其他信息,如他是一个接口

继续下一步:
    Set<String> mapperRegistryCache = 
            GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            
            一般是看看是否有对应的mapperClass.toString();结果值,通常情况下,是操作缓存的
            
              List<AbstractMethod> methodList = this.getMethodList();
              拿到方法集合,是对应的BaseMapper接口的所有方法(是不包含他的子类的)
              一般有17个,所以methodList的size也是17,具体方法调用的类是DefaultSqlInjector
              但是该顺序,却不是对应的BaseMapper接口的方法顺序,这里要注意,因为接口方法的顺序,并不完整
              
              找到这里
               methodList.forEach((m) -> {
                        m.inject(builderAssistant, mapperClass, modelClass, tableInfo);
                    });
                    该m代表循环上面的17个操作
                    
                    进入inject(先手动在里面打断点进入),否则一般进入不了:
                    public void inject(MapperBuilderAssistant builderAssistant, Class<?> 
                    mapperClass, 
                    Class<?> modelClass, TableInfo tableInfo) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = this.configuration.getDefaultScriptingLanguageInstance();
        this.injectMappedStatement(mapperClass, modelClass, tableInfo);
    }
    
    先手动点击 this.injectMappedStatement(mapperClass, modelClass, tableInfo);
    参数分别是:接口地址信息和其他信息,实体类对象class信息,关于表的信息
    
    进入public abstract MappedStatement injectMappedStatement(
    Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);
    
    我们看他谁操作了他,发现,是对应的方法名称的类操作了他
    如图:
  
*/

在这里插入图片描述

一般会按照前面17个的顺序,依次进行执行对应的方法,但这里我们直接的点击selectById:
/*
 public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo 
 tableInfo) {
在这里打上断点        SqlMethod sqlMethod = SqlMethod.LOGIC_SELECT_BY_ID;
        SqlSource sqlSource = new RawSqlSource(this.configuration, String.format(sqlMethod.getSql(), 
        this.sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), 
        tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, false)), Object.class);
        return this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, 
        modelClass, tableInfo);
    }
    
    打上断点后,进行点击下一个断点跳转即可(而不是下一步)
    
    我们可以先看看LOGIC_SELECT_BY_ID的值,发现,有个模板,然后后面的代码
    就是通过表信息进行补全模板,表信息可以看成是对应的类和接口地址操作成的
    然后放入对应的MappedStatement类信息中(后面的return后面的代码的操作),使得可以操作数据库
    所以说,若根据结果,模板也可以说是MappedStatement类信息,虽然是对应的语句
    
    为了验证,我们直接看看sqlSource的值,继续下一步,可以发现对应的值(sql部分)
    就是一个完整的sql了(且对应的字段值,在等待我们输入)
    即User user = userMapper.selectById(15);中的15就是我们输入的值
    且包含了其他的信息
    使得普通的mybatis操作的xml放入MappedStatement类信息中得到的类似的信息
    也就是说sqlSource他相当于sql语句的信息和其他信息,就如jdbc里面的连接一样
    而返回的就是运行后的结果,类似于代码平台,准备调用
    
    至此,大致解析完毕,的确是得到了对应的信息放入MappedStatement类信息中
    使得我们不需要编写对应的xml了
    
    
    至此,当这些初始化完毕后,到下一个断点,直到在
 
    根据id进行查询

    @Test
    public void testSelectById(){

  到这里了,之前打的断点      User user = userMapper.selectById(15);
        System.out.println(user);

        //对应where后面的也是:指定策略者-id-null


    }
    
    那么他的执行,也就是操作了数据库语句的执行,至此,得到了数据
    
    
    那么最后有个疑问,初始化是否在在对应的工程那里进行了,答:是的,工厂那么就是上面的执行流程
    所以前面也说是初始化的操作
    
*/
配置:
在MP中有大量的配置,其中有⼀部分是Mybatis原生的配置,另⼀部分是MP的配置
MP配置详情:https://mybatis.plus/config/
下面我们对常⽤的配置做讲解:
基本配置:
configLocation:
MyBatis 配置文件位置,如果有单独的 MyBatis 配置,请将其路径配置到 configLocation 中
MyBatis Configuration 的具体内容请参考MyBatis 官方文档
为了进行测试,我们继续操作前面的分页,我们在资源文件夹下,创建mybatis-config.xml文件(全局配置文件):
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <plugins>
       <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
       </plugin>
   </plugins>
</configuration>
一般来说,我们需要操作该配置,所以我们需要回到对应的项目的配置:
若操作Spring Boot项目,那么配置如下:
#加载全局配置文件
mybatis-plus.config-location = classpath:mybatis-config.xml
#那么运行Spring Boot时,会帮我们进行加载,并读取配置,从而操作,相当于下面的配置
#因为这里Spring Boot与Mybatis-plus是整合的,所以可以操作得到,相当于下面的配置
若操作Spring项目,那么对应的Spring的配置如下:
<bean id="sqlSessionFactory" 
      class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">  
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--继续操作对应的配置,进行读取操作-->
至此分页操作计算成功,可以进行测试,我们将原来的配置分页插件的类的关键注释给注释掉,发现,的确也是可以计算的
mapperLocations:
MyBatis Mapper 所对应的 XML ⽂件位置,如果您在 Mapper 中有⾃定义方法(XML 中有⾃定义实现)
需要进行该配置,告诉 Mapper 所对应的 XML ⽂件位置
在操作之前,我们回到UserMapper接口,加上如下方法:
  /*
    自定义findById方法

     */

    public User findById(Integer id);
再在资源文件夹下创建mapper目录,然后创建UserMapper.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lagou.mapper.UserMapper">

    <select id="findById" resultType="com.lagou.pojo.User">
      select * from user where id = #{id}
</select>
</mapper>
一般情况下,我们在操作时,需要将xml的对应的目录与类相对应,但是由于这里是我们来指定,所以目录可以不对应
若操作Spring Boot项目,那么配置如下(springboot的配置,本质是因为他已经写好的固定的模板,如果找到这个模板就可以配置,否则不行,但是他已经可以完成大多数的配置了,所以使用就没有问题,因为他也会进行更新的):
#加载映射配置文件
mybatis-plus.mapper-locations = classpath*:mapper/*.xml
#classpath*也会扫描其他的classes目录(比如其他的jar包),而classpath只扫描当前的classes目录
若操作Spring项目,那么对应的Spring的配置如下:
<bean id="sqlSessionFactory" 
      class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> 
    <property name="mapperLocations" value="classpath*:mapper/*.xml"/>
</bean>
他们这样的指定,就使得我们调用接口时,从原来的,我们根据接口的地址,来使得扫描配置
变成了手动的配置,即我们手动的扫描配置,即就不需要对应的接口地址了
且手动的配置会覆盖默认的根据接口的地址,即优先操作手动的配置
那么Spring Boot有没有操作操作接口的地址的,答:有
我们将对应的配置修改成如下即可:
#使得扫描接口,进而扫描配置
mybatis-plus.mapper-locations = classpath*:com.lagou.mapper
#对应的单独的mybatis,也可以是这样,测试过了,首先优先看对应的mapper是否是类,是类,则操作该类,否则操作该包
但是在操作Spring时,对应的基本只能是具体的xml,且扫描也不能操作全局配置里面的扫描了
只能是Spring配置里进行扫描(且只能是操作接口地址,而不能是具体的xml)
即工厂那里只能操作具体xml,而扫描那里只能操作具体接口
(即所在的包里面包含即可,但不能是最上层和指定的文件,其他没有说明的路径,基本也是如此,如@MapperScan注解
有说明的路径,因为说明了,那么只能是扫描当前包里面的,不会扫描子包里面的,虽然可能并没有印象,但也是有的,比如* 和* * 的对比,其中单个 * 就是没有子包的说明,而两个 * 就有子包的说明
当然可能有些框架的地方也操作了类似于单个 * 的这样的说明,比如:
< property name=“basePackage” value=“com.lagou.dao”>< /property>,这个指定的com.lagou.dao包也是没有操作子包的
只操作当前com.lagou.dao包下的接口,一般不能是类,否则可能会报错,这是mybatis映射底层规定的,具体看61章博客
其他的不操作子包的地方,具体可以百度查看)
使得分开了,而Spring Boot操作时,自然也是这样
只是只能使得只操作一个,根据结果来的,没有覆盖操作
注意:无论什么情况,对应的mybatis的全局扫描必须有
如全局配置文件的< mappers >标签或者@MapperScan的注解以及Spring操作的扫描等等
分别操作mybatis,spring boot,spring整合的,因为他们使得可以操作注入
其中spring整合的,操作对应的单独注解时,却不可以,因为我们注入只能是一个,因为spring整合的只是增加信息而已
这些Spring Boot只是增强而已,所以要注意,因为是mybatis-plus的操作
实际上只要不是全局的配置,那么基本都是增强的操作,无论是mybatis和spring boot整合还是mybatis-plus和spring boot整合等等
而正是如此,所以对应的全局扫描,需要有相同名称和路径的配置,否则会报错
但无论是spring boot操作的还是spring操作的,都是对应的同样的设置,只是加载方式不同而已
Maven 多模块项目的扫描路径通常需以classpath*: 开头 (即加载多个 jar 包下的 xml文件)
通常用在Spring Boot里面,因为Spring Boot基本是使用依赖的,所以通常使用这个
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
/*
    测试自定义的findById方法
     */
    @Test
    public void findById(){
        User byId = userMapper.findById(1);
        System.out.println(byId);
        
        //注意:他操作的是自己写好的语句,不是我们放入对应的位置的类变量操作
        //所以基本不会操作类的信息(如特殊小写)
    }
至此,返回数据则操作成功
typeAliasesPackage:
MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML ⽂件中可以直接使用类名
而不用使用全限定的类名(即 XML 中调⽤的时候不用包含包名)
若操作Spring Boot项目,那么配置如下:
# 起别名
mybatis-plus.type-aliases-package = com.lagou.pojo
若操作Spring项目,那么对应的Spring的配置如下:
<bean id="sqlSessionFactory" 
      class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">   
    <property name="typeAliasesPackage" value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
</bean>
当起别名后,我们写的配置,中对应的名称可以直接写类了,具体看61章博客
至此可以这样了:
   <select id="findById" resultType="User"> 
       <!--直接写User,即可,而不用写对应的地址,忽略大小写,即uSer也可-->
      select * from user where id = #{id}
</select>
进行测试,发现,的确可以,可以删除对应的配置进行再次测试
发现启动时报错(会检查的,或者说初始化报错,即允许时报错,而不是编译期)
至此我们发现,实际上也就是操作对应mybatis原来的全局配置
只是在spring boot中,是操作他的配置文件,而spring整合的,则是操作整合的配置文件
进阶配置:
本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形式进行配置
mapUnderscoreToCamelCase:
是否开启⾃动驼峰命名规则(camel case)映射
即从经典数据库列名 a_column(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
注意:
此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body(查询身体)
且默认值是true,即默认开启的,其中他不只是操作了表(数据库表)到类(前面说明过了,如果是false,那么自然他们就是与mybatis类似的样子,满足sql,满足类设置替换等等,61章博客有说明)
而也操作了我们生成时的类到表(在mybatis-plus中操作的,比如特殊小写,为false自然也不会操作,与mybatis类似)
而不是只有mybatis的表到类,就如前面说的只是去除下划线而已,当然也可以说是特殊小写
只是字段不区分大小写而已,所以只是去除下划线,并且在程序中由于基本都是先类到表,所以表必然是会到类的,只是若有对应set的话(后面忽略大小写),就操作set,否则就是对应变量,且必定对应,因为你已经对应好我了,所以一般的,我们只会报错类到表,而不会考虑表到类,实际上就算使用@TableField来使得不对呀,但是最后也会因为他来确定,所以还是对应的,基本没有什么空可以钻的,自己测试就知道了
所以如果您的数据库命名符合规则,则无需使用@TableField 注解指定数据库字段名
若操作Spring Boot项目,那么配置如下:
#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
#也操作了类到表,不只是表到类
#所以设置为false时,那么对应的就不会特殊小写了,即aGe,就是aGe,是什么就是什么了,而不是a_ge了

#那么既然不能和mybatis-plus.config-location同时存在,也就使得不能操作全局配置文件了
#那么为什么不能共存呢,因为该配置是操作mybatis-plus.config-location里面的
#即在mybatis-plus.config-location里面,我们操作spring时,是写在里面的,所以看起来是共存了
#但这里的共存,很明显是可以写两个的,所以如果对应的mybatis-plus.config-location配置里也有该配置
#即mybatis-plus.configuration.map-underscore-to-camel-case配置
#那么就可能会覆盖或者冲突,所以为了不出现这样的操作,则直接报异常即可
#如果我们需要对应的mybatis-plus.config-location配置
#我们在里面写mybatis-plus.configuration.map-underscore-to-camel-case配置即可,而不是直接的配置
操作Spring项目,那么对应的Spring的配置如下:
 <settings>

        <setting name="mapUnderscoreToCamelCase" value="false"/>

    </settings>

<!--这里操作全局配置的,且根据顺序,要在<plugins>标签之前
若在mybatis-plus中,则操作表到类的同时,也操作了类到表-->
进行测试后,发现,的确没有操作特殊小写了
cacheEnabled:
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true(原mybatis也默认是true)
操作Spring项目,那么对应的Spring的配置如下:
mybatis-plus.configuration.cache-enabled=false
具体可以看63章博客,因为并没有什么好说明的
DB 策略配置:
idType:
一般默认是ID_WORKER(3),全局唯⼀ID (idWorker),这个是默认的策略
我们可以设置全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置
若操作Spring Boot项目,那么配置如下:
#注意:配置后,默认使得类(基本是所有的类,但要注意是操作的泛型类)里面的id数(id数:表示该id忽略大小写)
#是设置了@TableId(type = IdType.AUTO)配置
mybatis-plus.global-config.db-config.id-type=auto
操作Spring项目,那么对应的Spring的配置如下:
 <bean id="sqlSessionFactory" 
       class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">   
     <property name="dataSource" ref="dataSource"/>  
     <!--下面的是主要部分,一般只能操作mybatis-plus,因为对应的包就是他里面的
且只能操作mybatis-plus工厂,因为对应的mybatis没有下面的操作,甚至没有globalConfig变量-->
     <property name="globalConfig">          
         <bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">    
             <property name="dbConfig">               
                 <bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">           
                     <property name="idType" value="AUTO"/>          
                 </bean>         
             </property>       
         </bean>      
     </property> 
</bean>
至此可以进行测试,发现,操作之前的添加时,还是操作自增的,而不是使用时间戳生成的
那么@TableId(type = IdType.AUTO)和上面的设置,谁优先呢,答:@TableId(type = IdType.AUTO)优先
即若其他字段有@TableId(type = IdType.AUTO),那么以其他字段为主,所以返回的信息在其他字段那里了
tablePrefix:
表名前缀,全局配置后可省略根据表名操作的前缀,使得不需要@TableName()配置了
若操作Spring Boot项目,那么配置如下:
#给表名加上前缀"tb_"
mybatis-plus.global-config.db-config.table-prefix=tb_
操作Spring项目,那么对应的Spring的配置如下:
<bean id="sqlSessionFactory" 
      class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> 
    <property name="dataSource" ref="dataSource"/>     
     <!--下面的是主要部分,一般只能操作mybatis-plus,因为对应的包就是他里面的
且只能操作mybatis-plus工厂,因为对应的mybatis没有下面的操作,甚至没有globalConfig变量-->
    <property name="globalConfig">         
        <bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">  
            <property name="dbConfig">                  
                <bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">           
                    <property name="idType" value="AUTO"/>     <!--也加上了对应的自增操作-->         
                    <property name="tablePrefix" value="tb_"/>    
                </bean>             
            </property>        
        </bean>   
    </property>  
</bean>
注意:对应的名称是有顺序的,即以表USer为例子,且操作了驼峰
那么有如下,没有@TableName()注解和没有设置前缀,那么表名是u_ser,有前缀tB_,没有注解,那么表名是tB_u_ser
即前缀不参与驼峰,若有注解(名称是user),那么无论是否有前缀,都操作注解,即表名是user
所以注解>前缀+类名>类名
至此,可以进行测试,我们经过测试,发现的确如此
条件构造器:
在MP中,Wrapper接⼝的实现类关系如下:

在这里插入图片描述

可以看到,AbstractWrapper和AbstractChainWrapper是重点实现,接下来我们重点学习AbstractWrapper以及其子类
说明:
QueryWrapper(是AbstractWrapper类的子类),LambdaQueryWrapper(是AbstractLambdaWrapper类的子类)
UpdateWrapper(是AbstractWrapper类的子类),LambdaUpdateWrapper(是AbstractLambdaWrapper类的子类)
他们的父类(有多个父类)作用:
用于生成 sql 的 where 条件或者set条件(部分),entity 属性也用于生成 sql 的 where 条件
其中AbstractWrapper类和AbstractLambdaWrapper类是Wrapper类的子类
我们主要操作AbstractWrapper类:
注意:entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
官网文档地址:https://mybatis.plus/guide/wrapper.html
可能是访问不了的,但并不需要在意,我们访问https://mp.baomidou.com/即可
allEq(基本是AbstractWrapper父类里面的操作):
说明:
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
    /*
    参数说明:
    params:key为数据库字段名,value为字段值
    null2IsNull:为true则在map的value为null时调⽤isNull的显示⽅法,为false时则忽略value为null的
    不写默认为true
    例1:allEq({id:1,name:"⽼王",age:null})--->id = 1 and name = '⽼王' and age is null
    例2:allEq({id:1,name:"⽼王",age:null}, false)--->id = 1 and name = '⽼王'
    condition:代表是否加入最后生成的sql中
    */
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法(多个方法):
 /*
    测试条件构建器,allEq
    map当条件
     */

    @Test
    public void testAllEq(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        //构建map
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","Jack");
        map.put("age",null);

        //sql语句的where部分是:WHERE name = ? AND age IS NULL
        objectQueryWrapper.allEq(map);

        //加入后,相当于之前操作的eq,变成了操作map集合,这也就使得可以操作IS NULL
        //使得之前我们基本只能可以使用selectByMap方法操作IS NULL,变成了都可以操作IS NULL了
        //即都可以操作map了

        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

//注意:一个不进行任何操作的QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        //那么相当于null作为参数,其他可以操作null的基本也是如此(可能也不一定是该对象),注意即可

    }

   /*
    测试条件构建器,allEq
    多次添加
     */

    @Test
    public void testAllEq2(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        //构建map
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","Jack");
        map.put("age",null);

   
        //多次执行
        objectQueryWrapper.allEq(map);
        map.put("agee",null);
        objectQueryWrapper.allEq(map);

        //sql语句的where部分是:
        //WHERE name = ? AND age IS NULL AND name = ? AND agee IS NULL AND age IS NULL
        //即全部往后添加

        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);
    }

    /*
    测试条件构建器,allEq
    是否忽略null
     */

    @Test
    public void testAllEq3(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        //构建map
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","Jack");
        map.put("age",null);


        //sql语句的where部分是:WHERE name = ?,IS NULL被忽略,true则不会
        objectQueryWrapper.allEq(map,false);



        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);
    }

 /*
    测试条件构建器,allEq
    是否添加
     */

    @Test
    public void testAllEq4(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        //构建map
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","Jack");
        map.put("age",null);


        objectQueryWrapper.allEq(map);



        map.put("agee",null);
        objectQueryWrapper.allEq(false,map,true);

        //sql语句的where部分是:WHERE name = ? AND age IS NULL 
        //后面的没有再加入了,注意:必须是三个参数的,两个的该参数好像没有,即allEq(false,map)没有
        //所以后面我指定true,当然,false也可以

        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);
    }

  /*
    测试条件构建器,allEq
    是否添加(操作都不添加)
     */

    @Test
    public void testAllEq5(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        //构建map
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","Jack");
        map.put("age",null);


        objectQueryWrapper.allEq(false,map,true);
        
        //由于没有条件了,那么
        //sql语句是:SELECT id,age,eMail AS mail FROM user
        //没有where了(这里就给出全部,当然,对应的字段是我的设置),也就是没有条件,这是自然的

        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);
    }


至此,三个参数测试完毕
继续说明:
allEq(BiPredicate<R, V> filter, Map<R, V> params)
allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) 
    /*
    参数说明:
    filter:过滤函数,是否允许字段传⼊⽐对条件中
    例1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"⽼王",age:null})--->
    name = '⽼王' and age is null
    例2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"⽼王",age:null}, false)--->
    name = '⽼王'
    使得key有a这个字母,那么就添加对应条件
    
    params 与 null2IsNull 与 condition:同上,即与前面一样
    */
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
    /*
    测试条件构建器,allEq
    过滤操作
     */

    @Test
    public void testAllEq6(){

        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        //构建map
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","Jack");
        map.put("age",null);


     objectQueryWrapper.allEq((k,v)->!k.equals("name"),map,true);
        //我们将k看成map集合的key值,过滤所有的map数据
        //只要对应的返回是false,那么不会加上对应的key-value条件
        //即这里就是,如果对应的key是name,那么返回false,即不加上条件
        //所以这里sql语句的where部分是::WHERE age IS NULL 
        //没有name的对应条件了

        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);
    }
至此,过滤参数测试完毕
基本比较操作(基本是AbstractWrapper父类里面的操作):
eq:等于 =
ne:不等于 <>
gt:⼤于 >
ge:大于等于 >=
lt:小于 <
le:小于等于 <=
between:BETWEEN 值1 AND 值2
notBetween:NOT BETWEEN 值1 AND 值2
in:字段 IN (value.get(0), value.get(1 ), …)
notIn:字段 NOT IN (v0, v1 , …)
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
  /*
    基本比较操作
     */
    @Test
    public void testWrapper(){
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("name","哈哈")
                .ge("age","20") //这个就算是20,不是"20",那么最终都会变成字符串,即是一样的
                .in("name","解决","拒绝");


        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

        //sql语句的where部分是:
        //WHERE name = ? AND age >= ? AND name IN (?,?) 
        
        //很明显是根据调用的方法顺序来进行的操作,所以是先name,然后age,再然后就是in方法的name操作
    }
至此大致操作完毕,剩余的就不进行测试了,因为并没有意义,但为了看结果,那么看如下:
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
   基本比较操作
    总的合并
    */
    @Test
    public void testWrapper2(){
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper
                .eq("name","1")
                .ne("name","2")
                .gt("name","3")
                .ge("name","4")
                .lt("name","5")
                .le("name","6")
                .between("name","7","8")
                .notBetween("name","9","10")
                .in("name","11","12")
                .notIn("name","13","14");

 //返回自身是可以分开写的,如
        objectQueryWrapper
                .eq("name","1");
         objectQueryWrapper
                .eq("name2","2")
             //因为你是返回自身的

//注意:对应的范围若使用String类型(最好使用Integer类型),那么最好不要是特殊的,比如"100"
//特殊有很多,这里只给出一个,具体看35章博客的说明
        //因为在sql里面会导致失败,所以mybatis-plus会使得改变sql语句(有前提,看后面的前提)
        //将select 和from中间的字段,全部删除,加上COUNT(1),与COUNT(*)一样,查询有多少记录,一般是查询所有
        //因为会过滤不与他相关的(列名的操作)
//其中改变sql语句的前提是,操作分页(一般是对应的方法操作的)
        //且存在分页插件(其他的插件可能也可以),否则不会改变sql语句
        //即该前提没有满足,那么就算你操作了特殊的,也不会改变sql语句,所以要改变sql语句
        //首先,先操作到特殊,然后满足前提,否则只要有一个没有满足,那么就不会改变

        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

        //sql语句的where部分是:
        /*
        WHERE name = ? AND name <> ? AND name > ? AND name >= ? AND name < ?
        AND name <= ? AND name BETWEEN ? AND ? AND name NOT BETWEEN ? AND ?
        AND name IN (?,?) AND name NOT IN (?,?)
         */

        //从这里,应该很清楚,正是因为对应的是字段,所以不能随便写
        //如a,b,等等,需要是表里面有的字段,即name
        //所以也并不是什么条件都可以操作,前提是字段正确,当然,map也是一样的
    }
至此,总的合并操作完毕
模糊查询:
like:LIKE ‘%值%’
例:like(“name”, “王”)—>name like ‘%王%’
notLike:NOT LIKE ‘%值%’
例:notLike(“name”, “王”)—>name not like ‘%王%’
likeLef:LIKE ‘%值’
例:likeLeft(“name”, “王”)—>name like ‘%王’
likeRight:LIKE ‘值%’
例:likeRight(“name”, “王”)—>name like ‘王%’
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
  /*
模糊查询

*/
    @Test
    public void testWrapper3() {
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper
                .like("name", "jaja")
                .notLike("name", "jj")
                .likeLeft("name", "aa")
                .likeRight("name", "bb");


        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

        //sql语句的where部分是:
        //WHERE name LIKE ? AND name NOT LIKE ? AND name LIKE ? AND name LIKE ? 

        //值:Parameters: %jaja%(String), %jj%(String), %aa(String), bb%(String)
        //那么为什么没有操作单独的值呢,的确没有,因为,eq不就可以了吗
        //为什么要加一个相同操作呢,所以就没有了
        //最后注意:当没有加上任何方法,时,那么自然,也就是没有where后面的操作
        //也就是相当于没有条件,到表名就结束了,即 SELECT id,age,eMail AS mail FROM user (我这里是)
       
    }
至此操作完毕
排序:
orderBy:ORDER BY 字段, …
例:orderBy(true, true, “id”, “name”)—>order by id ASC,name ASC
orderByAsc:ORDER BY 字段, … ASC
例:orderByAsc(“id”, “name”)—>order by id ASC,name ASC
orderByDesc:ORDER BY 字段, … DESC
例:orderByDesc(“id”, “name”)—>order by id DESC,name DESC
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
    /*
排序查询

*/
    @Test
    public void testWrapper4() {
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper.orderBy(true,false,"name","name");
        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

        //sql语句的order by部分是:
        //ORDER BY name DESC , name DESC
        //orderBy的参数:参数1,是否添加,参数2,是否排序,若是true,则是ASC,升序,false,则是DESC,降序
        //升序(对于表来看,就是从上到下,依次提升),降序(对于表来看,就是从上到下,依次降低)
        //参数3到参数n是可变长的参数,所以可以多写,注意:若这里是相同的参数,那么自然是优先考虑靠前的
        //若有其他的方法,那么不会加上order by了,而是直接的相连接,若有不是排序的方法
        //那么自然是再order by前面的,因为默认防止后面,也使得符合sql语法
         }

总体操作:
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 /*
排序查询
总的操作
*/
    @Test
    public void testWrapper5() {
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper
                .orderBy(true,false,"name","name")
                .orderByAsc("name")
                .orderByDesc("name");
        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

         //sql语句的order by部分是:
        //ORDER BY name DESC , name DESC , name ASC , name DESC
        
        //当然,对应的条件(不是条件属性,他包括基本所有的属性设置,比如eq,orderByDesc等等属性设置)执行不分先后,也就是说,如果中间出现了eq,那么实际上eq也不会写在对应后面
        //只是在对应的地方,所以,执行先后并没有很重要(相同的才会在对应地方后面,但是不会占用其他地方)
        //可以试着将eq防止中间
        /*
        比如:
          .orderBy(true,false,"name","name")
                .eq("a",1)
                .orderByAsc("name")
                .eq("a",1)
                .orderByDesc("name")
                .eq("b",1);
                来测试就知道了
                那么一般是这样
                WHERE a = ? AND a = ? AND b = ? ORDER BY name DESC , name DESC , name ASC , name DESC
                即的确没有占位置,相同的会在后面
                
                所以要注意:对应的先后并不是很重要,他们只会在对应的位置上进行添加,具体为什么不在后面添加
                是由判断的,所以会加在对应位置,比如判断到了ORDER BY
                那么eq就认为在他的前面,而不是后面,没有
                那么就在where(eq会添加的,条件属性的设置通常都会添加,ORDER BY不会,他并不需要操作where,所以他不是条件属性的设置,所以不会添加)后面
        */
    }

至此操作完毕
逻辑查询:
or:拼接 OR
主动调用or表示紧接着下⼀个方法不是用and连接(不调用or则默认为使用and连接)
and:AND 嵌套
例:and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—>and (name = ‘李白’ and status <> ‘活着’)
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法(多个):
/*
    逻辑查询
     */

    @Test
    public void testWrapper6() {
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper
                .eq("name","jack").or().eq("name","jj");
        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

        //sql语句的where部分是:
        //WHERE name = ? OR name = ?
        //一般情况下,我们之前的操作都默认是and连接的,我们再条件之间
        //加上or()那么就会使得默认的and,变成or

    }
/*
    逻辑查询
     */

    @Test
    public void testWrapper7() {
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper
                .eq("name","jack").and(i->i.eq("name","j"));
        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

        //sql语句的where部分是:
        //WHERE name = ? AND ( name = ? )
        //手动的加上括号

    }

至此操作完毕
select:
在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
/*
    select
     */

    @Test
    public void testWrapper8() {
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper
                .eq("name","jack").select("namee");
        List<User> users = userMapper.selectList(objectQueryWrapper);
        System.out.println(users);

        //sql语句的select部分是:
        //SELECT namee FROM user WHERE name = ?
        //加上select使得,不会默认使用操作类的所有字段来查询,而是使用select里面的字段来查询(select方法里面可以写多个,具体可以看里面的参数),要注意:对应的select只是在对应位置加上信息,所以写上*可行,并且*err也可行,因为他只是添加上,而没有什么判断(如eq的字段判断)
        //自然这里调用方法,会修改MappedStatement类信息的,使得改变,当然大多数的改变
        //基本都是修改MappedStatement类信息,方法也是一样,实际上mybatis也是一样,如输入数据等等
        //因为我们需要生成一个真正的sql,然后执行他,获得数据
        //所以说方法的操作,通常都会改变MappedStatement类信息,或者说改变sql语句
        //所以说,前面说的可能,一般是操作的,注意即可


    }
至此操作完毕
ActiveRecord:
ActiveRecord(简称AR)⼀直广受动态语⾔( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言
对于 ActiveRecord 往往只能感叹其优雅,但是MP(Mybatis-Plus)却可以操作,所以我们也在 AR 道路上进行了⼀定的探索
什么是ActiveRecord?
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出
遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性
配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,⽽且简洁易懂
ActiveRecord的主要思想是:
每⼀个数据库表对应创建⼀个类,类的每⼀个对象实例对应于数据库中表的⼀行记录,通常表的每个字段在类中都有相应的Field
ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD
ActiveRecord是⼀种领域模型(Domain Model),封装了部分业务逻辑
开启AR之旅:
在MP中,开启AR⾮常简单,只需要将实体对象继承Model即可
public class User extends Model implements Serializable {
    //语法,继承要在实现前面
测试用例:
到测试文件里的lagou包下,创建TestAR类(包括多个测试方法):
package com.lagou;

import com.lagou.pojo.User;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 *
 */
@RunWith(SpringRunner.class)
@SpringBootTest
class TestAR {


    /*
    在AR模式下,完成主键查询
     */

    @Test
    public void testARSelectById(){

        User user = new User();
        user.setId(12l);

        
        User user1 = (User) user.selectById();

        System.out.println(user1);
        
        //可以发现,Model不写泛型,代表操作返回值
        //所以可以不写泛型,但是对应的接口BaseMapper需要,因为需要操作泛型的字段,来进行放入,即初始化
    }
    
    //那么为什么可以操作呢,我们在前面的源码分析中,可以知道,在执行@Test方法时,会进行初始化,生成语句
    //那么这里也是如此,所以我们需要有对应的接口BaseMapper,并有对应的初始化
    //然后就是值的放入了,我们操作的Model类,就通过我们的调用者user,来进行值的放入,所以是封装好的
    
    //可以理解为:
    //AR和MP:是先放入字段,然后值放入(值放入的不同操作而已)

    //而正是只有值的放入的区别,所以也只是操作了值,所以也会操作特殊小写,字段的操作基本是没有影响的
    
    
      /*
    在AR模式下,完成新增数据
    注意:增删改一般只要返回的影响条数不为0,那么就返回true,否则返回false
    因为对应的方法中,有如下代码
    return null != result && result >= 1;,这个关键代码,在增删改里面
     */

    @Test
    public void testARInsert(){

        User user = new User();
        user.setName("哈哈");
        user.setAge(18);
        user.setMail("建设局");

        boolean user1 = user.insert();

        System.out.println(user1);
       
    }

      /*
    在AR模式下,完成更新操作
     */
    @Test
    public void testARUpdate(){

        User user = new User();
        user.setName("哈哈2");
        user.setAge(18);
        user.setMail("建设局2");

        boolean user1 = user.insertOrUpdate(); 
        //代表操作同一个时,就不在添加,而是更新,否则会添加
        //一般根据id的值来进行判断,会根据如下的解释:
        //首先,查询谁存在的问题的where后面的字段,根据这样的顺序:
        /*
        策略者(不能有多个数据,否则也报错)-id数(不能有多个数据,否则也报错)
        -报错(直接判断是否为null了,而不会加上null到字段位置,这里是特殊的)
        */
        //即首先查询,对应的字段是否有一个数据,若有,则更新,否则添加
        
        //我们又可以发现,总是有id这个数存在的默认放入对应位置的字段,大概是mybatis-plus操作的吧
        

        System.out.println(user1);
        System.out.println(user.getId());
        //那么既然是只操作值的放入,那么原来的id返回,自然也会在user对象里
        //经过测试,发现,的确如此
    }

     /*
    在AR模式下,完成更新操作
     */
    @Test
    public void testARUpdateById(){

        User user = new User();
        user.setId(11l);
        user.setName("哈哈233");
        
        user.setAge(18);
        user.setMail("建设局23");

        boolean user1 = user.updateById();
        //直接根据id来进行更新,自然也需要遵循默认的顺序,该默认一般操作where后面的

        System.out.println(user1);
        System.out.println(user.getId());
    }
    /*
 在AR模式下,完成删除操作
 */

    @Test
    public void testARDelete(){

        User user = new User();
        //user.setId(11l);


        boolean user1 = user.deleteById(12);  //这里操作手动的指定
      
 

        System.out.println(user1);
        System.out.println(user.getId());
    }
    
     /*
 在AR模式下,完成根据条件查询
 */

    @Test
    public void testARFindById(){

        User user = new User();
        QueryWrapper<Object> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.ge("age",20);


        List user1 = user.selectList(objectQueryWrapper);

        System.out.println(user1);
        
        //也能操作查询,只是从接口变成了来而已,但都是传入值
    }

}

上面只是给出部分的操作,因为大多数与MP的操作类似,所以就给出主要的方法了
插件:
mybatis的插件机制:
MyBatis 允许你在已映射语句执行过程中的某⼀点进行拦截调用,默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
/*
1:Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2:ParameterHandler (getParameterObject, setParameters)
3:ResultSetHandler (handleResultSets, handleOutputParameters)
4:StatementHandler (prepare, parameterize, batch, update, query)
*/
我们看到了可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的⼀些方法等
总体概括为:
1:拦截执行器的方法
2:拦截参数的处理
3:拦截结果集的处理
4:拦截Sql语法构建的处理
拦截器示例:
在lagou包下(没有说明测试文件的,一般都是java资源文件里面,所以注意即可),创建MyInterceptor类并实现Interceptor接口:
package com.lagou;

//可以发现是mybatis自带的自定义插件操作方式
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.util.Properties;

/**
 *
 */
@Intercepts({@Signature(
        type= Executor.class,
        method = "update",
        args = {MappedStatement.class,Object.class})})
//拦截Executor类的update方法,并指定参数的类型MappedStatement和Object,防止重载
public class MyInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println(1);
        //拦截⽅法,具体业务逻辑编写的位置
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object o) {
        //该o一般是前面的总体(四个),会执行四次plugin方法
        /*
            org.apache.ibatis.executor.CachingExecutor@230a73f2
            如果是增删改,那么这里会先执行intercept方法,然后执行plugin方法
            com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler@3e26482
            org.apache.ibatis.executor.resultset.DefaultResultSetHandler@7c5df615
            org.apache.ibatis.executor.statement.RoutingStatementHandler@2f995afc
            当然,注意即可
        */
        System.out.println(22);
        //创建target对象的代理对象,⽬的是将当前拦截器加⼊到该对象中
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {
        System.out.println(3);
        //属性设置
    }
}

该插件写好后,若是操作注入的方式,即类的方式,那么如下:
注入到Spring容器(简称类操作):
//⾃定义拦截器  
@Bean    
public MyInterceptor myInterceptor(){   
    return new MyInterceptor();   
}
通过xml配置(前提是可以被读取到,操作的全局配置):
 <plugins>       
     <plugin interceptor="com.lagou.MyInterceptor"></plugin> 
</plugins>
至此,我们在执行时,可以看到,每次查询得到一条数据,都会打印一个22,所以的确是拦截,但这里并不深入了解
若要深入了解,可以百度,查看对应的参数信息
执行分析插件:
在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作
注意:该插件仅适用于开发环境,不适用于生产环境,因为使用他会损耗多数性能,但开发中,可以使用,进行测试等等
性能分析插件:
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执⾏时间,超过时间会抛出异常
该插件只用于开发环境,不建议生产环境使用,与执行分析插件一样,损耗多数性能
配置:
注入到Spring容器(简称类操作):
//执行分析插件
/*
@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){    
    SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
    List<ISqlParser> sqlParserList = new ArrayList<>();
    // 攻击 SQL 阻断解析器、加⼊解析链
    sqlParserList.add(new BlockAttackSqlParser());
    sqlExplainInterceptor.setSqlParserList(sqlParserList);
    return sqlExplainInterceptor;
}
*/
 /*
    性能分析插件
     */
    @Bean
    public PerformanceInterceptor performanceInterceptor(){
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(100);
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
        
        //这个对应于下面的配置,一般xml配置与类配置一起时,至于谁优先,主要看谁后覆盖,这里好像是xml优先
        //即后执行(因为他并不是得到,而是已经注入了,但也不绝对)
        //当然,有些情况下,可能不会允许覆盖,所以在这种情况下
        //通常来说xml会优先,因为一般xml会覆盖注解,而注解不会覆盖xml,使得还是使用xml,注解的被移除
        //但本质上还是覆盖问题,只是xml占优而已
    }
通过xml配置(前提是可以被读取到,操作的全局配置):
<!--  性能分析插件-->   
<plugins>
       <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
          <!--sql性能分析,开发环境使用,线上不推荐,maxTime指的是sql最大执行时长-->
           <property name="maxTime"  value="100"/>
           <!--sql是否格式化,默认false-->
           <property name="format" value="true"/>
       </plugin>
   </plugins>
对应的该插件是MP所拥有的,注意即可
先操作 性能分析插件:
回到LagouMpSpringbootApplicationTests类,运行testSelect方法,可以看看打印的结果
我们可以明显的看到,有如下:
/*
 
 我这里这样
 
    根据id进行查询
    
   @Test
    void testSelect() {

        System.out.println(44);
        List<User> all = userMapper.selectList(null);
        System.out.println(all);
    }



Time:20 ms - ID:com.lagou.mapper.UserMapper.selectList
Execute SQL:
    SELECT
        id,
        age,
        eMail AS mail 
    FROM
        user
        
        这是我们的显示,对应的执行时间是20ms,当然,多次的运行一般是不同的
        
        也发现sql语句进行了格式化,我们将最大的执行时间
        修改成10ms(虽然可能也会成功,如运行好执行时间更短了)
        再次看看运行结果
        我们可以很明显的发现,报错了,使得对应的结果没有打印
        
        我们可以知道,对于sql的执行时间,在开发中,这个插件也就能够看看优化的sql是否是被优化还是被劣化了
        
        没有格式化的就是如下
Execute SQL:SELECT id,age,eMail AS mail FROM user

*/
那我们的执行分析插件的作用是什么呢:
我们回到LagouMpSpringbootApplicationTests类,找到testUpdate方法,我这里如下:
   /*
    测试根据条件进行修改
     */

    @Test
    public void testUpdate() {
        User user = new User();
        //更新的字段
        user.setAge(35);
        //更新的条件
        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper.eq("name", "哈哈哈");

        int i = userMapper.update(user, objectQueryWrapper);
        System.out.println(i);

    }
我们运行一下,发现可以运行,那么接下来修改一下该方法,修改如下:
    /*
    测试根据条件进行修改
     */

    @Test
    public void testUpdate() {
        User user = new User();
        //更新的字段
        user.setAge(35);
        //更新的条件
//        QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//
//        objectQueryWrapper.eq("name", "哈哈哈");

        int i = userMapper.update(user, null);
        System.out.println(i);

    }
我们直到,在没有条件的情况下,就是操作所有了,但这时,我们运行,会报错,也就是说对应的执行分析插件是拦截操作全部
即使得报错,防止我们失误的操作,造成的后果
这时,我们注释掉对应的类配置,发现,可以执行了,通过我的测试,他主要是用来防止,我们操作"删改"操作的全部类型的操作
这样可以有效的提高安全性,而正是因为对应的类配置复杂,所以,一般是没有xml配置的,可能有(可以百度看看)
乐观锁插件:
对于乐观锁,与之相对应的就是悲观锁,实际上乐观锁并不能称为锁,因为基本是没有锁的实现
只是通过程序使得降低操作相同数据的方式,如操作版本号
但在多访问的情况下,如果对应数据库没有唯一性,那么可能在判断版本号之后,会出现共同操作,并也会有多次失败的情况
但这种情况基本不会出现,因为数据库对字段来说,基本是同步的(因为我们通常会设置主键自增,所以是基本),即最后的变化基本只能是一个人
如添加(因为主键,一般主键无论多大的并发他都不会相同,特别是自增,可以认为他内部有锁,数据库在不同的事务下会自动的加锁,如增删改是写锁,而查询是读锁,在96章博客会说明,所以导致我们操作相同数据库时,会出现唯一性)和删除(因为没有,同样操作锁,所以有唯一性),即也基本有唯一性
但是虽然有唯一性,但可能也会因为高并发下,会出现多次执行失败的情况(比如删除),而多次的失败,自然会浪费更多资源
这时就需要加上锁了,这样也就称为悲观锁,使得一个数据只能被一人操作
这里我们就只说明乐观锁
主要适用场景:
意图:
当要更新⼀条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
假设,数据库有个字段,为version
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion,oldVersion原来的version
newVersion 新的version,通常是操作加1,也有可能是随机的(除了自己)
这时,在唯一性的情况下,其他人来更新数据后,我们的where version = oldVersion判断,基本不会操作,因为被改变了
所以一般会出现更新失败,即如果version不对,就更新失败(并不是报错,只是,没有更新)
图解:

在这里插入图片描述

插件配置:
注入到Spring容器:
/*
乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {   
    return new OptimisticLockerInterceptor();
}
实际上我们也可以使用xml完成注入的操作,如下
通过xml配置(前提是可以被读取到,这里并不是全局配置了,实际上这两个是一样的,都是操作容器)
虽然全局是被访问得到:
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
全局的配置文件:
 <plugins>
    <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor">
     </plugin>
   </plugins>
注解实体字段:
需要为实体字段添加@Version注解
在这之前:
第⼀步,为表添加version字段,并且设置初始值为1,可以手动的去表里进行添加,也可以使用语句添加,具体sql语句如下:
-- 添加version字段
ALTER TABLE USER
ADD COLUMN VERSION INT(10) NULL AFTER email;
-- 一般默认添加在最后面的位置,所以不需要加上AFTER email
-- 那么有没有before,添加在前,好像并没有,可能有其他方式,具体可以百度

-- 所有数据的version都设置为1,因为我们需要最开始就是1的数据
UPDATE USER SET VERSION = 1
第二步,为User实体对象添加version字段,并且添加@Version注解:
@Version
private Integer version;
我们回到LagouMpSpringbootApplicationTests类,找到testUpdateById方法,我这里如下:
  /*
    测试根据id进行修改
     */

    @Test
    public void testUpdateById() {
        User user = new User();
        user.setId(6l);
        user.setAge(30);
        user.setName(null);
        int i = userMapper.updateById(user);
        System.out.println(i);

    }
将该方法进行修改,修改成如下:
 /*
    测试根据id进行修改
     */

    @Test
    public void testUpdateById() {
        User user = new User();
        User user1 = userMapper.selectById(3);

        user.setId(3l);
        user.setName("哈哈哈");
        user.setVersion(user1.getVersion());

        int i = userMapper.updateById(user);
        System.out.println(i);
        System.out.println(user); //对应的version是新的值(加上1的)

    }
我们执行一下,看看对应的sql语句,我们发现执行成功,sql语句如下:
/*
 UPDATE
        user 
    SET
        name='哈哈哈',
        age=0, age类型是int,所以不会省略
        version=2 
    WHERE
        id=3 
        AND version=1
        
        其中  version=2 ,即表示自动的加上了1,实际上是因为由于注解@Version的存在
        我们在操作值时,会默认加上AND version=对应的原值,且原来的set值变成了原值加1
        
        所以我们没有条件,也会出现AND version=1,再次执行时,也是如此,即变成了如下:
        
        UPDATE
        user 
    SET
        name='哈哈哈',
        age=0,
        version=3 
    WHERE
        id=3 
        AND version=2
        
        
        我们可以将@Version注解删除,看看结果
        
          UPDATE
        user 
    SET
        name='哈哈哈',
        age=0,
        version=3 
    WHERE
        id=3
        
        继续将@Version注解加回来
        UPDATE
        user 
    SET
        name='哈哈哈',
        age=0,
        version=4 
    WHERE
        id=3 
        AND version=3
        
        所以说,将原值进行操作的就是@Version注解的作用,以及也会加上条件
        当然,需要放入对应的位置,即需要赋值才可,因为null也会导致不会加上字段
        
        而正是因为会加上条件,使得基本不会出现操作同一个数据,即避免了大多数的拿到同一个数据问题
        虽然可能存在不唯一性(基本不会),或者更新失败的问题
        
        为了测试更新失败,我们可以在如下
         @Test
    public void testUpdateById() {
        User user = new User();
        User user1 = userMapper.selectById(3);

        user.setId(3l);
        user.setName("哈哈哈");
        user.setVersion(user1.getVersion());

  这里打上断点      int i = userMapper.updateById(user);
        System.out.println(i);

    }
    
    debug执行后(只要我们不放行,那么基本就算一直在运行中,而不执行sql语句)
    这时我们去数据库修改对应的version,然后放行,会发现,返回0,即没有进行操作
    所以的确没有进行更新,即更新失败
*/
至此乐观锁操作完毕
特别说明:
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime,因为需要操作下面的加1
即整数类型下 newVersion = oldVersion + 1,当是其他类型,如String类型,那么newVersion = oldVersion,而不是加1
其中,无论是什么类型,newVersion 会回写到 entity 中,即由原来的设置原version值,变成了设置新的version值
但仅支持 updateById(entity) 与 update(entity, wrapper) 方法,实际上也只有这两个方法
那么为什么只操作更新呢,因为添加和删除,他们就算是有多次操作,那么也基本是只能一个成,如主键,或者没有
而更新,才是异步中最主要的问题
在 update(entity,wrapper)方法下,wrapper 不能复用,若使用wrapper
那么会多出一个version的判断,使得新的version总体加上了2,测试就知道了,所以说真正的方法,只有 updateById(entity)
因为update(entity, wrapper)多判断,基本使得不会更新成功,即虽然支持,但成不成功,就不知道了
Sql 注入器:
我们已经知道,在MP中,通过AbstractSqlInjector将BaseMapper中的方法注入到了Mybatis容器,且不用我们写对应的配置文件
这样这些方法才可以正常执行
那么,如果我们需要扩充BaseMapper中的方法,又该如何实现呢?
下面我们以扩展findAll方法为例进行学习
首先在lagou包下的mapper包下创建MyBaseMapper接口并继承BaseMapper接口:
package com.lagou.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;

import java.util.List;

/**
 *
 */

//通用mapper接口,以后创建其他mapper接口时,就不需要再继承BaseMapper接口了
//而是继承MyBaseMapper接口(也就是当前接口)
    //只要指定了T,那么父类的T也会指定一样的,注意即可
public interface MyBaseMapper<T> extends BaseMapper<T> {
    List<User> findAll();
}

//那么可以不使用MyBaseMapper吗,而是在自己的接口里写List<User> findAll();
//答:可以,只是既然是要扩展,那么自然的是我们实现的接口来扩展,而不是我们自己

然后再lagou包下,创建injector包,并再该包下创建MySqlInjector类:
package com.lagou.injector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;

import java.util.List;

/**
 * 自定义sql注入器
 */
public class MySqlInjector extends AbstractSqlInjector {
    
    @Override
    public List<AbstractMethod> getMethodList() {
        return null;
    }
}

在这之前,我们首先进入AbstractSqlInjector类里面:
/*
找到 public abstract List<AbstractMethod> getMethodList();方法

看看有谁实现了他,可以发现是DefaultSqlInjector

如下:
public class DefaultSqlInjector extends AbstractSqlInjector {
    public DefaultSqlInjector() {
    }

    public List<AbstractMethod> getMethodList() {
        return (List)Stream.of(new Insert(), new Delete(), new DeleteByMap(), new DeleteById(), new 
        DeleteBatchByIds(), new Update(), new UpdateById(), new SelectById(), new SelectBatchByIds(), 
        new SelectByMap(), new SelectOne(), new SelectCount(), new SelectMaps(), new 
        SelectMapsPage(), new SelectObjs(), new SelectList(), new 
        SelectPage()).collect(Collectors.toList());
    }
}

我们随便点击一个,如点击new SelectById()进去,发现包含了之前的初始化的操作
也就是说,实际上之前的初始化的操作(前面源码分析中,得到了17个数据的那个方法)
就是创建了这些对象(一般都继承AbstractMethod类,因为我们通常需要他里面的内容)
然后执行injectMappedStatement方法
所以我们也知道,要想操作自己的,我们需要也创建对象才可,且创建的对象也要有injectMappedStatement方法

*/
即再在injector包下,创建FindAll类并继承AbstractMethod类:
package com.lagou.injector;

import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

/**
 *
 */
public class FindAll extends AbstractMethod {


    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo 
                                                 tableInfo) {

        String sql = "select * from " + tableInfo.getTableName(); //tableInfo.getTableName()表名
        SqlSource sqlSource = this.languageDriver.createSqlSource(
            this.configuration, sql, modelClass); //有sql以外的信息
        return this.addSelectMappedStatement(
            mapperClass, "findAll", sqlSource, modelClass, tableInfo); 
        //返回可执行的sql,相当于代码平台,直接操作数据库

    }
}

创建好后,我们需要进行添加,回到MySqlInjector类,对应的代码如下:
注意:如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法将失效
若要有效,我们可以选择继承DefaultSqlInjector进行扩展,或者加上对应的返回,后面会说明,这里就不继承了
package com.lagou.injector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义sql注入器
 */
public class MySqlInjector extends AbstractSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList() {
        //扩充自定义方法
        List<AbstractMethod> methodList = new ArrayList<>();
        methodList.add(new FindAll());
        return methodList;
         //return (List) Stream.of(new FindAll()).collect(Collectors.toList());

    }
}

若要继承,则是:
package com.lagou.injector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.methods.*;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 自定义sql注入器
 */
public class MySqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList() {
        //扩充自定义方法
        List<AbstractMethod> methodList = super.getMethodList();
        methodList.add(new FindAll());
        return methodList;
    
    }
}

编写好后,要想生效,我们需要进行注入操作
回到MybatisPlusConfig类。添加如下代码:

    /*
    自定义sql注入器
     */
    @Bean
    public MySqlInjector mySqlInjector(){
        return new MySqlInjector();

    }
//只要我们操作了这一步,那么原来的方法都会失效(即原有的BaseMapper中的方法将失效)
//因为我们这里只有findAll方法操作了
//我们可以看源码,会发现,对应的17个变成了1个
接下来,我们将UserMapper接口由原来的继承BaseMapper接口,变成继承MyBaseMapper接口:
public interface UserMapper extends BaseMapper<User> {
//修改成如下
public interface UserMapper extends MyBaseMapper<User> {
    
    //虽然public interface UserMapper extends BaseMapper<User> {
    //这个也可,只需要自己写方法即可,只是,扩展,自然不是自己扩展
测试用例:
继续到LagouMpSpringbootApplicationTests类里面加上对应的方法:
 @Test
    public void testFindAll(){
        List<User> all = userMapper.findAll();

        System.out.println(all);
    }

//运行,发现,执行了

//那么有个问题,为什么可以执行findAll方法,在前面我们知道
//有个 
/*
    return this.addSelectMappedStatement(
            mapperClass, "findAll", sqlSource, modelClass, tableInfo); 
            
            其中findAll就代码方法名称(必须一样,即没有大小写忽略,否则报错,即找不到方法)
            只要是BaseMapper的子类(这样可以操作泛型)中
            有他这个方法,那么执行该方法,就会执行,该sql语句
            
            所以之前的17个中,才会出现语句的执行,只是这里进行了注入,所以只有该一个方法了
            有注入的则使用注入的,否则使用默认的17个,当然,我们可以将那17个写上,那么也能执行
            如:
             return (List)Stream.of(new FindAll(),new Insert(), new Delete(), new DeleteByMap(), new 
             DeleteById(), new DeleteBatchByIds(), new Update(), new UpdateById(), new SelectById(), 
             new SelectBatchByIds(), new SelectByMap(), new SelectOne(), new SelectCount(), new 
             SelectMaps(), new SelectMapsPage(), new SelectObjs(), new SelectList(), new 
             SelectPage()).collect(Collectors.toList());
             
             多出了new FindAll(),这样才是真正的扩展
             
             至此,实际上FindAll类和MySqlInjector类是进行sql语句的操作,然后通过注入,使得方法与sql语句连接
             
             至此当我们有继承了BaseMapper接口的接口时
             那么该接口的方法可以与注入sql语句对应的方法进行对应,然后可以执行sql
             因为我们需要BaseMapper接口来操作放入位置(特别是泛型的操作),即初始化
             当然,因为继承的关系,我们只需要将MyBaseMapper的泛型写上即可,BaseMapper的泛型可以不写
             那么自然,MyBaseMapper里面的都是该泛型
             因为子类关系,那么我们的初始化,自然会操作对应的父类泛型,虽然并不是BaseMapper接口
             只要是BaseMapper的子类即可,无论多少层级,即一路查找
             
             所以虽然单纯的返回类可能需要强转,但集合不需要(自动的会变,因为对方并没有泛型)
             因为他只是在操作时,会提示

            */
至此我们扩展完毕
自动填充功能:
有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据
比如密码、 version等,在MP中提供了这样的功能,可以实现自动填充
添加@TableField注解:
@TableField(fill = FieldFill.INSERT) //表示在插入时,进行填充
private Integer version;
FieldFill提供了多种模式选择:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.annotation;

public enum FieldFill {
    //默认不处理
    DEFAULT,
    //插⼊时填充字段
    INSERT,
    //更新时填充字段
    UPDATE,
    //插⼊或更新时填充字段
    INSERT_UPDATE;

    private FieldFill() {
    }
}

//注意:现在好像,只要你有对应的注入,那么无论你是否加上对应的注解,都会执行填充,但一般不会的,必须存在对应的注解(如@TableField(fill = FieldFill.INSERT)),后面会说明,注意即可
但我们这里也只是进行了指定,具体操作需要我们自己编写,即需要填充什么:
在lagou包下创建handler包,并在该包下创建MyMetaObjectHandler类并实现MetaObjectHandler接口:
package com.lagou.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

/**
 *
 */

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    //插入时的填充
    @Override
    public void insertFill(MetaObject metaObject) {

        //得到传递的对象的version值
        Object version = getFieldValByName("version", metaObject);
        if(null == version){
            //该属性为空,我们可以进行填充,改变对应的值(这里只是设置和填充,你没有我添加,你有我修改),前提是存在version这个字段,否则什么都不操作
                        //对应的第二个参数可是Object的,即可以是字符串哦,当然了,他是操作set和get方法的(那么对应的值需要对应参数,否则自然会报错,就如1怎么给String呢,这样的话,报错自然是会出现的),且首字母必须大小,且其他必须相同,没有,那么这些get和set自然不会设置(get返回null)
            setFieldValByName("version",1,metaObject); //注意:对应get和set的version与类的变量完全相同才可,这样才会进行确定对应的地方,要知道,mp自然是可以保存很多信息的,当然也包括原来的变量名称,只是我们大多数的认为sql变化不是而已(如特殊小写)
        }
        
            System.out.println(metaObject.getOriginalObject()); //这个就是我们传递的类的信息
        
        //且基本只能是类的信息,因为添加和修改的操作中,字段值基本是与类相关的,而不是条件

    }

    //更新时的填充
    @Override
    public void updateFill(MetaObject metaObject) {

    }
        //一般只有插入和更新(等语句的)的触发操作,当然,就算是更新,实际上若代码一样,那么其实还是相同的类似结果,设置和填充(一起的)
}

这里需要注入,使得可以被使用才可操作
测试用例:
我们回到LagouMpSpringbootApplicationTests类,找到testInsert方法:
 /*
    测试添加
     */
    @Test
    public void testInsert() {
        User user = new User();

        //user.setId(9l);
        user.setName("哈哈哈");
        user.setAge(18);
        user.setMail("zimu@jajaja");
        //返回值自然也是影响行数
        int insert = userMapper.insert(user);
        System.out.println(insert);

        System.out.println("id值为:" + user.getId());

    }
修改成如下:
/*
    测试添加
     */
    @Test
    public void testInsert() {
        User user = new User();


        user.setName("哈哈哈w");
        user.setAge(18);
        user.setMail("zimu@jajaja");

        int insert = userMapper.insert(user);
        System.out.println(insert);

        System.out.println("id值为:" + user.getId());

    }

//进行运行,我们发现,他并没有因为没有设置version的值(默认值),而不加上字段,而是已经加上了
//因为我们进行了填充,我们将对应的注解,删除再次测试,发现,并没有操作了方法,操作更新,也没有操作了方法
//所以对应的注解是决定作用,且以注入为主了
//我们将@Component去掉,不被注入(注入map集合,并不是依赖注入的意思)
//发现没添加字段了,这时,我们加回来,在对应的方法里,不进行操作,发现也是没有添加字段
//所以说,那个填充的在放入字段之前操作了
//即填充(操作),放入之前(判断是否为null,而决定是否添加字段),放入
//根据这样的说明,那么如果你手动的设置为null,那么就在放入之前这个阶段,使得不会添加字段
至此操作填充完毕
逻辑删除:
开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除
而并非真正的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到
这样做的目的就是避免数据被真正的删除,因为对应的删除的数据可能也是有作用的,所以通常并不会真正的删除数据
MP就提供了这样的功能,方便我们使用,接下来我们⼀起学习下:
修改表结构:
为user表增加deleted字段,用于表示数据是否被删除, 1代表删除, 0代表未删除
对应的sql语句(当然,也可以手动的添加):
ALTER TABLE USER
ADD COLUMN deleted INT(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER VERSION
同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:
@TableLogic //对该字段操作逻辑删除,至于怎么操作,看如下配置
private Integer deleted; 
配置:
Spring Boot的配置:
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
测试用例:
我们回到LagouMpSpringbootApplicationTests类,找到testDeleteById方法:
/*
    根据id进行删除
     */
    @Test
    public void testDeleteById() {



        int i = userMapper.deleteById(7);
        System.out.println(i);
    }
//运行后,看看打印情况,可以发现,对应的语句如下:
/*
UPDATE
        user 
    SET
        deleted=1 
    WHERE
        id=7 
        AND deleted=0
        
        明明是删除操作,却变成了更新操作
        实际上更新操作和查询操作后面的where都会有deleted=0,在条件之前,类之后(如操作id)
        所以也使得,只能查询或者修改deleted=0的数据
        但因为插入,没有where,所以没有操作
        
        其中
        deleted=0:代表mybatis-plus.global-config.db-config.logic-not-delete-value=0
        deleted=1:代表mybatis-plus.global-config.db-config.logic-delete-value=1
        修改这两个值,那么对应的deleted后面的值也就会修改,而正是因为操作的是deleted,所以表字段也要deleted
        自己可以进行测试
        
        即从逻辑未删除值(默认为 0)变成逻辑已删除值(默认为 1)
        通常需要指定数据库表的0为未删除,1代表已删除,就是与这里对应,使得操作的sql语句也正好对应
    当然,他只能操作0到1,因为是删除的,所以并没有操作1到0哦,这里要注意,所以执行一次后,再次的执行,基本不会有什么数据的改变了

*/
至此操作逻辑删除完毕
代码生成器:
AutoGenerator 是 MyBatis-Plus 的代码生成器
通过 AutoGenerator 可以快速⽣成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率
创建工程:
对应的依赖:
 <dependencies>      
     <dependency>        
     <groupId>org.springframework.boot</groupId>    
     <artifactId>spring-boot-starter-test</artifactId>  
     <scope>test</scope>      
     </dependency>
      <!--mybatis-plus的springboot⽀持-->
     
      <dependency>     
          <groupId>com.baomidou</groupId>   
          <artifactId>mybatis-plus-boot-starter</artifactId>   
          <version>3.1.1</version>    
     </dependency>  
     
     <!--下面两个是新的依赖-->
     <dependency>        
         <!--有对应的StringUtils类操作,且也可以使得创建对应的AutoGenerator对象的操作-->
         <groupId>com.baomidou</groupId>    
         <artifactId>mybatis-plus-generator</artifactId>     
         <version>3.1.1</version>     
     </dependency>     
     <dependency>     
         <!--虽然明面上的代码并没有操作该依赖,但生成时,需要该依赖
没有该依赖,那么mpg.execute();这一步执行报错-->
         <groupId>org.springframework.boot</groupId>   
         <artifactId>spring-boot-starter-freemarker</artifactId>  
     </dependency>
      <!--mysql驱动-->
     <dependency>     
         <groupId>mysql</groupId>      
         <artifactId>mysql-connector-java</artifactId>  
         <version>5.1.47</version>    
     </dependency>     
     <dependency>        
         <groupId>org.slf4j</groupId>     
         <artifactId>slf4j-log4j12</artifactId> 
     </dependency>    
     <dependency>       
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-web</artifactId> 
     </dependency>
     <!--简化代码的⼯具包-->
      <dependency>   
          <groupId>org.projectlombok</groupId>  
          <artifactId>lombok</artifactId>       
          <optional>true</optional>      
     </dependency>
      </dependencies>
 <build>     
     <plugins>      
         <plugin>           
             <groupId>org.springframework.boot</groupId> 
             <artifactId>spring-boot-maven-plugin</artifactId>    
         </plugin>    
     </plugins> 
</build>
对应的目录:

在这里插入图片描述

对应的MysqlGenerator类:
package com.lagou.generator;


import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 *
 */
public class MysqlGenerator {

    /*
    读取控制台内容
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if(scanner.hasNext()){
            String ipt = scanner.next();
            if(StringUtils.isNotEmpty(ipt)){
                return ipt;
            }
        }
        throw new MybatisPlusException("请输⼊正确的" + tip + "!");
    }

    /*
    RUN THIS(运行这个)
     */
    public static void main(String[] args) {
        // 代码⽣成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java"); //代码生成位置
        gc.setAuthor("lagou"); //作者是谁
        gc.setOpen(false);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(
            "jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&useSSL=false&characterEncoding=utf8");
        //dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456"); //修改成自己的
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模块名"));
        pc.setParent("com.lagou.mp.generator"); //模块名的包的位置,没有会自动创建
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        List<FileOutConfig> focList = new ArrayList<>();
        focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输⼊⽂件名称
                return projectPath + "/lagou-mp-generator/src/main/resources/mapper/" + 
                    pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + 
                    StringPool.DOT_XML;
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        mpg.setTemplate(new TemplateConfig().setXml(null));

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);

         /*
         
         strategy.setSuperEntityClass("
         com.baomidou.mybatisplus.samples.generator.common.BaseEntity");

*/
        strategy.setEntityLombokModel(true);
        /*
        strategy.setSuperControllerClass("
        com.baomidou.mybatisplus.samples.generator.common.BaseController");
        */

        strategy.setInclude(scanner("表名"));
        strategy.setSuperEntityColumns("id");
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);

        // 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();  //这个要加上,否则什么都没有执行(操作)


    }
}

测试时,对应的模块名,就是controller的首路径,表名必须要与数据库对应,否则只有包,而没有类,因为没有数据表
但有对应的数据表时,对应的类与输入的表名有关
运行后,会发现,多出几个包了,里面包含了你输入的表信息,至此操作完毕,对应的代码了解即可,并不需要手动编写
对应输入的表名变成类时会根据驼峰反向操作,注意即可,输入的表名都要小写,否则只有包
多次运行,如果没有会添加,有则不会覆盖,而使用插件时,会提示是否覆盖
大概是因为代码基本手动(不在物理中断的情况下)中断不了,所以不操作覆盖,只操作添加吧
具体的目录如下:

在这里插入图片描述

即mapper,entity,service,controller都编写完毕,注意:这里只是给出创建方式,具体为什么可以实现以及细节问题,可以百度学习,当然,不同版本的实现方式也是不同的,所以了解即可,在以后需要使用时,可以选择你认为好的实现方式来进行使用
MybatisX 快速开发插件:
MybatisX 是⼀款基于 IDEA 的快速开发插件,为效率而生
安装方法:打开 IDEA,进⼊ File -> Settings -> Plugins,点击Marketplace(市场),输⼊MybatisX 搜索并安装
功能:
Java接口 与 XML 调回跳转
Mapper 方法自动生成 XML
虽然88章博客安装的mybatis插件也可以这样,只是没有图标,且该插件要优先

在这里插入图片描述

生成标签后,对应的sql还是需要自己编写
比如:
/*
若是public void insertt(User user);方法

那么生成时,会根据xml的namespace地址查看是否是当前接口的地址,而进行生成
若没有对应的namespace值,则没有选项,或者没有对应的生成提示了
若有,那么选择生成后,是<insert id="insertt"></insert>
所以对应的sql需要自己编写,因为他只是提供标签

具体自己测试就知道了
*/
至此操作完毕
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值