WELCOME

任何一个伟大的目标,都有一个微不足道的开始。

深入学习Mybatis-plus

 

了解Mybatis-plus

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

1.1MP特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,

  • 更有强大的条件构造器,满足各类使用需求

  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  • 支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、

  • SQLServer2005、SQLServer 等多种数据库

  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解

  • 决主键问题

  • 支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动

  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操

  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

  • 支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词

  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,

  • 支持模板引擎,更有超多自定义配置等您来使用

  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List

  • 查询

  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

  • 内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击

1.2架构

image-1205

❤️ Mybatis-Plus 快速开始

对于Mybatis整合MP有常常有三种用法,分别是Mybatis+MP、Spring+Mybatis+MP、Spring Boot+Mybatis+MP。

2.1创建数据库以及表

-- 创建数据库
CREATE DATABASE mp CHARSET 'utf8';

-- 创建表
CREATE TABLE tb_user(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
username VARCHAR(20) NOT NULL COMMENT'用户名',
PASSWORD VARCHAR(20) NOT NULL COMMENT'密码',
NAME VARCHAR(30) DEFAULT NULL COMMENT'姓名',
age INT DEFAULT NULL COMMENT'年龄',
email VARCHAR(50) DEFAULT NULL COMMENT'邮箱'
)CHARSET = utf8;

2.2、创建工程

<groupId>priv.chenJie</groupId>
<artifactId>myBatis-plus-1</artifactId>
<packaging>pom</packaging>

2.3、Mybatis 实现查询User

第一步 编写mybatis-confifig.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>

   <typeAliases>
       <typeAlias type="priv.cj.pojo.User" alias="user"/>
   </typeAliases>

   <environments default="development">
       <environment id="development">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
               <property name="driver" value="com.mysql.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://127.0.0.1:3306/mp? useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;allowMultiQuerie s=true&amp;useSSL=false"/>
               <property name="username" value="root"/>
               <property name="password" value="root"/>
           </dataSource>
       </environment>
   </environments>

   <mappers>
       <mapper resource="UserMapper.xml"/>
   </mappers>
</configuration>

第二步 编写User实体对象:(这里使用lombok进行了进化bean操作)

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user") //Mybatis+MP中需要注明数据库表名
public class User {
   private Long id;
   private String userName;
   private String password;
   private String name;
   private Integer age;
   private String email;
}

第三步 编写UserMapper接口:

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="priv.cj.mapper.UserMapper">
   <select id="findAll" resultType="user">
       select * from tb_user
   </select>
</mapper>

第五步 编写TestMybatis测试用例:

    @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);
      }
  }

2.4、Mybatis +MP实现查询User

第一步 将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有方法:

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

第二步 使用MP中的MybatisSqlSessionFactoryBuilder进程构建:

    @Test
   public void test1() throws IOException {
       InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
       //由于使用了MybatisSqlSessionFactoryBuilder进行了构建,继承的BaseMapper中的方法就载入到了SqlSession中,所以就可以直接使用相关的方法;
       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);
      }
  }
[tips]  在User对象中添加@TableName,指定数据库表名

2.5、Spring + Mybatis + MP

引入了Spring框架,数据源、构建等工作就交给了Spring管理。

第一步 创建model

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <parent>
       <groupId>priv.chenJie</groupId>
       <artifactId>myBatis-plus-1</artifactId>
       <version>1.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>

   <artifactId>myBatis-plus-spring</artifactId>

   <properties>
       <spring.version>5.2.9.RELEASE</spring.version>
   </properties>

   <dependencies>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-webmvc</artifactId>
           <version>${spring.version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-jdbc</artifactId>
           <version>${spring.version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-test</artifactId>
           <version>${spring.version}</version>
       </dependency>
   </dependencies>
</project>

第二步 编写jdbc.properties及log4j.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mp
jdbc.username=root
jdbc.password=root

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

第三步 编写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"
      xmlns:cintext="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
      ">

   <context:property-placeholder location="classpath:*.properties"/>

   <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}"/>
       <property name="maxActive" value="10"/>
       <property name="minIdle" value="5"/>
   </bean>

   <!--Spring与mp整合-->
   <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
       <property name="dataSource" ref="dataSource"/>
   </bean>

   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="priv.cj.mapper"/>
   </bean>
</beans>

第四步 编写User对象以及UserMapper接口:

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
   private Long id;
   private String userName;
   private String password;
   private String name;
   private Integer age;
   private String email;
}
public interface UserMapper extends BaseMapper<User> {
}

第五步 编写测试用例

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

   @Autowired
   private UserMapper userMapper;

   @Test
   public void test1(){
       List<User> users = userMapper.selectList(null);
       for (User user : users) {
           System.out.println(user);
      }
  }
}

2.6、SpringBoot + Mybatis + MP

使用SpringBoot将进一步的简化MP的整合,需要注意的是,由于使用SpringBoot需要继承parent,所以需要重新创建工程,并不是创建子Module。

第一步 创建工程继承parent并导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.6.2</version>
   </parent>

   <groupId>priv.chenJie</groupId>
   <artifactId>myBatis-plus-springboot</artifactId>
   <version>1.0-SNAPSHOT</version>

   <dependencies>
       <!--spring-boot-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
           <exclusions>
               <exclusion>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-logging</artifactId>
               </exclusion>
           </exclusions>
       </dependency>
       <!--spring-test-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <version>2.6.2</version>
       </dependency>
       <!--简化代码工具包-->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.18.12</version>
       </dependency>
       <!--mybatis-plus的springboot支持-->
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>3.1.1</version>
       </dependency>
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>5.1.32</version>
       </dependency>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
           <version>1.7.32</version>
       </dependency>
   </dependencies>
</project>

第二步 编写application.properties及log4j.properties

spring.application.name=myBatis-plus-springboot //当前项目名
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp
spring.datasource.username=root
spring.datasource.password=root

第四步 编写pojo及mapper

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
   @TableId(type = IdType.AUTO)
   private Long id;
   private String userName;
   private String password;
   private String name;
   private Integer age;
   private String email;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

第五步 编写启动类

@MapperScan("priv.cj.mapper") //设置mapper接口的扫描包
@SpringBootApplication
public class MyApplication {
   public static void main(String[] args) {
       SpringApplication.run(MyApplication.class, args);
  }
}

第六步 编写测试类

@SpringBootTest
public class TestMybatisSpringBoot {

   @Autowired
   private UserMapper userMapper;

   @Test
   public void test1(){
       List<User> users = this.userMapper.selectList(null);
       for (User user : users) {
           System.out.println(user);
      }
  }
}

💛 Mybatis-Plus实现CURD

通过前面的学习,我们了解到通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们将详细讲解这些 操作。

3.1插入操作

    @Test
   public void testInsert() {
       User user = new User();
       user.setAge(20);
       user.setMail("test@itcast.cn");
       user.setName("曹操");
       user.setUserName("caocao");
       user.setPassword("123456");
       user.setFavorite("陈洁");
       int result = this.userMapper.insert(user); //返回的result是受影响的行数,并不是自增后的id
       System.out.println("result = " + result);
       System.out.println(user.getId()); //自增后的id会回填到对象中
  }

还需要设置id的生成策略,期望的是数据库自增长

    //设置id自增长
   @TableId(type = IdType.AUTO)
   private Long id;

@TableField

在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:

1、对象中的属性名和字段名不一致的问题(非驼峰) 2、对象中的属性字段在表中不存在的问题

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
   
   @TableId(type = IdType.AUTO)//设置id自增长
   private Long id;
   private String userName;

   @TableField(select = false)//查询时不返回当前字段中的值
   private String password;
   private String name;
   private Integer age;

   @TableField(value = "email")//解决字段名不一致
   private String mail;

   @TableField(exist = false)//解决对象字段在表中不存在的问题
   private String favorite;
}

3.2 更新操作

在MP中,更新操作有2种,一种是根据id更新,另一种是根据条件更新。

3.2.1 根据id更新

    @Test
   public void testUpdateById() {
       User user = new User();
       user.setId(4L);
       user.setUserName("chenJie");
       user.setPassword("loveJT1314");
       user.setMail("loveCJ.com");
       int result = userMapper.updateById(user);
       System.out.println("result =>" + result);
  }

3.2.2 根据条件更新

QueryWrapper实体对象封装操作类,或可以为null

    @Test
   public void testUpdate1() {
       User user = new User();
       user.setUserName("jiangTao");//更新的字段
       user.setPassword("loveCJ1314");
       user.setMail("loveJT.com");

       QueryWrapper<User> wrapper = new QueryWrapper<>();///更新的条件
       wrapper.eq("user_name", "jt");//匹配user_name=jt的数据
       int result = userMapper.update(user, wrapper);
       System.out.println("result=>" + result);
  }

使用UpdateWrapper

    @Test
   public void testUpdate2() {
       UpdateWrapper<User> wrapper = new UpdateWrapper<>();
       wrapper.set("age", 21).set("password", "loveChenJie1314")//设置更新字段
              .eq("user_name", "jiangTao");//使用的都是字段的名字,不是实体中属性的名
       int result = userMapper.update(null, wrapper);
       System.out.println("result=>" + result);
  }

3.3 删除操作

3.3.1 deleteById

    @Test
   public void testDeleteById() {
       int result = userMapper.deleteById(9);
       System.out.println("result=>" + result);
  }

3.3.2 deleteByMap

    @Test
   public void testDeleteByMap() {
       Map<String, Object> map = new HashMap<>();
       map.put("user_name", "caocao");
       map.put("id", 8);
       //根据map查询数据,多条件之间为and关系
       int result = this.userMapper.deleteByMap(map);
       System.out.println("result=>" + result);
  }

3.3.3 delete 根据包装条件删除

    @Test
   public void testDelete() {
       //方法一:手动填写字段
//       QueryWrapper<User> wrapper = new QueryWrapper<>();
//       wrapper.eq("user_name","caocao").eq("id",7);
       //方法二:面向对象 ==推荐
       User user = new User();
       user.setUserName("caocao");
       user.setId(6L);
       QueryWrapper<User> wrapper = new QueryWrapper<>(user);
       
       int result = this.userMapper.delete(wrapper);
       System.out.println("result=>" + result);
  }

3.3.4 deleteBatchIds 根据id批量删除

    @Test
   public void testDeleteBatchIds(){
       //List<Long> list = new ArrayList<>();
       //list.add(15L);
       //list.add(16L);
       int result = this.userMapper.deleteBatchIds(Arrays.asList(10L, 11L));
       System.out.println("result=>" + result);
  }

3.4 查询操作

MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作。

3.4.1 selectById

@Test
   public void testSelectById() {
       User user = userMapper.selectById(4L);
       System.out.println(user);
  }

3.4.2 selectBatchIds

    @Test
   public void testSelectBatchIds(){
       //根据id批量查询数据
       List<User> users = this.userMapper.selectBatchIds(Arrays.asList(4L,5L,1314L));
       for (User user : users) {
           System.out.println(user);
      }
  }

3.4.3 selectOne

@Test
public void testSelectOne(){
   //根据条件查询一条语句,结果超过一条会报错
   QueryWrapper<User> wrapper = new QueryWrapper<>();
   wrapper.eq("user_name","zhangsan");
   User user = this.userMapper.selectOne(wrapper);
   System.out.println(user);
}

3.4.4 selectCount

    @Test
   public void testSelectCount(){
       //根据条件查询数据条数
       QueryWrapper<User> wrapper = new QueryWrapper<>();
       wrapper.gt("age",20);//年龄大于20的用户
       Integer count = this.userMapper.selectCount(wrapper);
       System.out.println(count);
  }

3.4.5 selectList

@Test
   public void testSelectList(){
       //根据条件查询
       QueryWrapper<User> wrapper = new QueryWrapper<>();
       wrapper.like("email","test");//like模糊查询
       List<User> users = this.userMapper.selectList(wrapper);
       for (User user : users) {
           System.out.println(user);
      }
  }

3.4.6、selectPage

配置分页插件:

@Configuration
@MapperScan("priv.cj.mapper") //设置mapper接口的扫描包
public class MybatisPlusConfig {

   //配置分页拦截器,分页插件
   @Bean
   public PaginationInterceptor paginationInterceptor(){
       return new PaginationInterceptor();
  }
}
    @Test
   public void testSelectPage(){
       Page<User> page = new Page<>(1,2);//查询第1页,查询2条数据

       QueryWrapper<User> wrapper = new QueryWrapper<>();
       wrapper.notLike("email","test");

       IPage<User> iPage = this.userMapper.selectPage(page, wrapper);

       System.out.println("数据总条数:"+iPage.getTotal());
       System.out.println("数据总页数:"+iPage.getPages());
       System.out.println("当前页数:"+iPage.getCurrent());
       List<User> records = iPage.getRecords();//获取分页信息
       for (User record : records) {
           System.out.println(record);
      }
  }

3.5 SQL注入原理

MP在启动后会将BaseMapper中的一系列的方法注册到meppedStatements中,那么究竟是如何注入的呢?流程又是怎么样的?

  1. 在MP中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类

  2. 在AbstractSqlInjector中,主要是由inspectInject()方法进行注入的

  3. 在实现方法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo)); 是关键,循环遍历方法,进行注入。

  4. 最终调用抽象方法injectMappedStatement进行真正的注入,该方法的实现:

    image-20220109200731172

以selectById查看:

可以看到,生成了SqlSource对象,再将SQL通过addSelectMappedStatement方法添加到meppedStatements中。

image-20220109104631144

💙配置

在MP中有大量的配置,其中有一部分是Mybatis原生的配置,另一部分是MP的配置,详情:https://mybatis.plus/config/ 下面我们对常用的配置做讲解。

4.1、基本配置

4.1.1、configLocation

MyBatis 配置文件位置,如果您有单独的 MyBatis 配置,请将其路径配置到 configLocation 中。

Spring MVC:

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

Spring Boot:

mybatis-plus.config-location = classpath:mybatis-config.xml

4.1.2、mapperLocations

MyBatis Mapper 所对应的 XML 文件位置,如果您在 Mapper 中有自定义方法(XML 中有自定义实现),需要进行该配置,告诉 Mapper 所对应的 XML 文件位置。

Spring MVC:

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
</bean>
[tips]  Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)

Spring Boot:

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

4.1.3、typeAliasesPackage

MyBaits 别名包扫描路径.

Spring MVC:

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
</bean>

Spring Boot:

mybatis-plus.type-aliases-package=priv.cj.pojo

4.2、进阶配置

本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形 式进行配置。

4.2.1、mapUnderscoreToCamelCase

是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属 性名 aColumn(驼峰命名) 的类似映射。mor

SprinfBoot

# 关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
[tips]  此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body。如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名

4.2.2、cacheEnabled

全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。

 mybatis-plus.configuration.cache-enabled=false

4.3、DB 策略配置

4.3.1、idType

全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。

SpringBoot:

mybatis-plus.global-config.db-config.id-type=auto

SpringMVC:

<!--这里使用MP提供的sqlSessionFactory,完成了Spring与MP的整合-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/>
<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>

4.3.2、tablePrefix

表名前缀,全局配置后可省略@TableName()配置。

SpringBoot:

mybatis-plus.global-config.db-config.table-prefix=tb_

SpringMVC:

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<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>

🌛条件构造器

在MP中,Wrapper接口的实现类关系如下:

image-20220110101948625

可以看到,AbstractWrapper和AbstractChainWrapper是重点实现,接下来我们重点学习AbstractWrapper以及其 子类

5.1、allEq

5.1.1 Map作为条件

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 的字段

测试用例:

@Test
   public void testWrapper(){
       Map<String, Object> map = new HashMap<>();
       map.put("name","曹操");
       map.put("age",21);
       map.put("password",null);

       QueryWrapper<User> wrapper = new QueryWrapper<>();
   
       //SELECT * FROM tb_user WHERE password IS NULL AND name = 曹操 AND age = 21
       wrapper.allEq(map);
       //SELECT * FROM tb_user WHERE name = 曹操 AND age = 21
       wrapper.allEq(map,false);
       
       List<User> users = this.userMapper.selectList(wrapper);
       for (User user : users) {
           System.out.println(user);
      }
  }

5.1.2 filter过滤函数

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 : 过滤函数,是否允许字段传入比对

测试用例:

//SELECT * FROM tb_user WHERE name = 曹操 AND age = 21
wrapper.allEq((k, v) -> (k.equals("name") || k.equals("age")), map);

5.2、基本比较操作

方法名称含义
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), ...)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("password", "123456")
      .ge("age", 20)
      .in("name", "李四", "王五", "赵六");

//select * FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?,?)
List<User> users = this.userMapper.selectList(wrapper);

5.3、模糊查询

方法名称含义
like LIKE '%值%'
notLike NOT LIKE '%值%'
likeLeft LIKE '%值'
likeRight LIKE '值%'

5.4、排序

方法名称含义
orderBy ORDER BY 字段, ..
orderByAsc ORDER BY 字段, ... ASC
orderByDesc ORDER BY 字段, ... DESC
//select * FROM tb_user ORDER BY age ASC , id ASC
wrapper.orderBy(true,true,"age","id");

5.5、逻辑查询

方法名称含义
or 用于拼接条件 OR
and AND 嵌套
//SELECT * FROM tb_user WHERE name = ? OR age = ? 
wrapper.eq("name","曹操").or().eq("age",12);

5.6、select

在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段。

//SELECT name FROM tb_user WHERE name = ? OR age = ? 
wrapper.eq("name","曹操")
      .or()
      .eq("age",12)
      .select("name");
List<User> users = this.userMapper.selectList(wrapper);

💰 ActiveRecord

ActiveRecord属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记 录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。

ActiveRecord的主要思想是:

  • 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;

  • 通常表的每个字段 在类中都有相应的Field;

  • ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;

  • ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;

6.1 开启AR之旅

在MP中,开启AR非常简单,只需要将实体对象继承Model即可。

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User extends Model<User> {

   @TableId(type = IdType.AUTO)//设置id自增长
   private Long id;
   private String userName;

   @TableField(select = false)//查询时不返回当前字段中的值
   private String password;
   private String name;
   private Integer age;

   @TableField(value = "email")//解决字段名不一致
   private String mail;

   @TableField(exist = false)//解决对象字段在表中不存在的问题
   private String favorite;
}
[tips]  AR不需要注入UserMapper但UserMapper必须存在

6.2 查询操作

@Test
public void testSelectById(){
   User user = new User();
   user.setId(2L);
   //SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE id=?
   User user1 = user.selectById();
   System.out.println(user1);
}
@Test
public void testSelect(){
   User user = new User();
   QueryWrapper<User> wrapper = new QueryWrapper<>();
   wrapper.gt("age",18);
   List<User> userList = user.selectList(wrapper);
   for (User user1 : userList) {
       System.out.println(user1);
  }
}

6.3 新增数据

    @Test
   public void testInsert(){
       User user = new User();
       user.setUserName("AR");
       user.setPassword("123");
       user.setAge(21);
       user.setMail("AR.com");

       boolean res = user.insert();
       System.out.println(res);
  }

6.4 更新操作

    @Test
   public void testUpdateById(){
       User user = new User();
       user.setId(12L);
       user.setUserName("caocao");
       user.setPassword("123123");
       user.setAge(1);
       user.setName("曹操");
       boolean flag = user.updateById();
       System.out.println("flag=>"+flag);
  }
   @Test
   public void testUpdateByCondition(){
       User user = new User();
       user.setPassword("999");
       user.setAge(99);
       user.setName("枭雄");
       QueryWrapper<User> wrapper = new QueryWrapper<>();
       wrapper.eq("user_name","caocao");
       boolean flag = user.update(wrapper);
       System.out.println("flag=>"+flag);
  }

6.5 删除操作

 @Test
   public void testDelete(){
       User user = new User();
       user.setId(12L);
       boolean flag = user.deleteById();
       System.out.println("flag=>"+flag);
  }

⛔️Oracle 主键Sequence

  • Oracle

👦:插件

8.1、mybatis的插件机制

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法 调用包括:

  • Executor 拦截执行器的方法

  • ParameterHandler 拦截参数的处理

  • ResultSetHandler 拦截结果集的处理

  • StatementHandler 拦截Sql语法构建的处理

拦截器实例:

@Intercepts({@Signature(
       type= Executor.class,
       method = "update",
       args = {MappedStatement.class,Object.class})})
public class MyInterceptor implements Interceptor {

   public Object intercept(Invocation invocation) throws Throwable {
       //拦截方法,具体业务逻辑编写的位置
       return invocation.proceed();
  }

   public Object plugin(Object target) {
       //创建target对象的代理对象,目的是将当前拦截器加入到该对象中
       return Plugin.wrap(target, (org.apache.ibatis.plugin.Interceptor) this);
  }

   public void setProperties(Properties properties) {
       //属性设置
  }
}

注入到Spring容器:

/**
* 自定义拦截器
*/
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}

或者通过xml配置,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="cn.itcast.mp.plugins.MyInterceptor"/>
</plugins>
</configuration>

8.2、执行分析插件

在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅适用于开发环境,不 适用于生产环境。

SprinigBoot配置

@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
// 攻击 SQL 阻断解析器、加入解析链
sqlParserList.add(new BlockAttackSqlParser());
sqlExplainInterceptor.setSqlParserList(sqlParserList);
return sqlExplainInterceptor;
}
[ttips]  当执行全表更新时,会抛出异常,这样有效防止了一些误操作。

8.3、性能分析插件

性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。

配置:

<?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>
<!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 -->
<plugin
interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<property name="maxTime" value="100" />
<!--SQL是否格式化 默认false-->
<property name="format" value="true" />
</plugin>
</plugins>
</configuration>

8.4、乐观锁插件

当要更新一条记录的时候,希望这条记录没有被别人更新,可避免多人同时更新导致的数据不一致。

乐观锁实现方式:

  • 取出记录时,获取当前version

  • 更新时,带上这个version

  • 执行更新时, set version = newVersion where version = oldVersion

  • 如果version不对,就更新失败

8.4.1、配置:

Spring xml:

 <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>

SpringBoot:

@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}

8.4.2、注解实体字段

第一步,为表添加version字段,并且设置初始值为1:

ALTER TABLE `tb_user`
ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';

第二步,为User实体对象添加version字段,并且添加@Version注解:

@Version
private Integer version;

8.4.3、测试

@Test
public void testUpdate(){
User user = new User();
user.setAge(30);
user.setId(2L);
user.setVersion(1); //获取到version为1
int result = this.userMapper.updateById(user);
System.out.println("result = " + result);
}

👪Sql注入器

在MP中,通过AbstractSqlInjector将BaseMapper中的方法注入到了Mybatis容器,这样这些方法才 可以正常执行。 那么,如果我们需要扩充BaseMapper中的方法,又该如何实现呢?

9.1、编写MyBaseMapper

public interface MyBaseMapper<T> extends BaseMapper<T> {
List<T> findAll();
}

其他的Mapper都可以继承该Mapper,实现方法的扩展

public interface UserMapper extends MyBaseMapper<User> {
User findById(Long id);
}

9.2、编写MySqlInjictor

如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法将失效,所以我们选择继承DefaultSqlInjector 进行扩展。

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

9.3、编写FindAll

public class FindAll extends AbstractMethod {
   @Override
   public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
       String sqlMethod = "findAll";
       String sql = "select * from " + tableInfo.getTableName();
       SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql,modelClass);
       return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource,modelClass, tableInfo);
  }
}

9.4、注册到Spring容器

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

👧自动填充功能:

插入或者更新数据时,希望有些字段可以自动填充数据,比如默认密码、version 等。在MP中提供了这样的功能,可以实现自动填充。

10.1、添加@TableField注解

@TableField(fill = FieldFill.INSERT) //插入数据时进行填充
private String password;
public enum FieldFill {
   /**
   * 默认不处理
   */
   DEFAULT,
   /**
   * 插入时填充字段
   */
   INSERT,
   /**
   * 更新时填充字段
   */
   UPDATE,
   /**
   * 插入和更新时填充字段
   */
   INSERT_UPDATE
}

10.2、编写MyMetaObjectHandler

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
   @Override
   public void insertFill(MetaObject metaObject) {
       Object password = getFieldValByName("password", metaObject);
       if(null == password){
           //字段为空,可以进行填充
           setFieldValByName("password", "123456", metaObject);
      }
  }
   @Override
   public void updateFill(MetaObject metaObject) {
  }
}

🐻逻辑删除

逻辑删除就是将数据标记为删除,而并非真正 的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免 数据被真正的删除。

11.1、修改表结构

为tb_user表增加deleted字段,用于表示数据是否被删除,1代表删除,0代表未删除。

ALTER TABLE `tb_user`
ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER`version`;

同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:

@TableLogic
private Integer deleted;

11.2、配置

application.properties:

# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0

💘通用枚举

解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!

12.1、修改表结构

ALTER TABLE `tb_user`ADD COLUMN `sex` int(1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER `deleted`;

12.2、定义枚举

public enum SexEnum implements IEnum<Integer> {

   MAN(1,"男"),WOMAN(2,"女");

   private int value;
   private String desc;

   SexEnum(int value, String desc) {
       this.value = value;
       this.desc = desc;
  }

   @Override
   public Integer getValue() {
       return this.value;
  }

   @Override
   public String toString() {
       return "SexEnum{" +
               "value=" + value +
               ", desc='" + desc + '\'' +
               '}';
  }
}

12.3、配置

# 枚举包扫描
mybatis-plus.type-enums-package=cn.itcast.mp.enums

12.4、修改实体

private SexEnum sex;

12.5、测试

@Test
public void testInsert(){
   User user = new User();
   user.setName("貂蝉");
   user.setUserName("diaochan");
   user.setAge(20);
   user.setEmail("diaochan@itast.cn");
   user.setVersion(1);
   user.setSex(SexEnum.WOMAN);
   int result = this.userMapper.insert(user);
   System.out.println("result = " + result);
}

👑代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

13.1、创建工程

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>

   <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
   
   <groupId>cn.itcast.mp</groupId>
   <artifactId>itcast-mp-generator</artifactId>
   <version>1.0-SNAPSHOT</version>

   <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>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-generator</artifactId>
           <version>3.1.1</version>
       </dependency>
       <dependency>
           <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>
   </dependencies>
   <build>
       <plugins>
        <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
  </plugins>
   </build>
</project>

13.2、代码

/**
* <p>
* mysql 代码生成器演示例子
* </p>
*/
public class MysqlGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
   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("itcast");
       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("root");
       mpg.setDataSource(dsc);
       // 包配置
       PackageConfig pc = new PackageConfig();
       pc.setModuleName(scanner("模块名"));
       pc.setParent("cn.itcast.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 + "/itcast-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();
  }
}

13.3、测试

生成代码

🏘 MybatisX 快速开发插件

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。

功能:

  • Java 与 XML 调回跳转

  • Mapper 方法自动生成 XML

posted @ 2022-01-11 19:35  蓝色IU  阅读(50)  评论(0)    收藏  举报