MyBatis-Plus

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

1. 特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

2. 入门

2.1. 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.0</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>
</dependencies>

2.2. 数据库配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///blog?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimeZone=GMT%2B8
    username: root
    password: root

2.3. 增加注解

在Spring Boot启动类上增加Mapper扫描注解

@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")

或者在Mapper类上增加@Mapper注解

2.4. 编写代码

编写对应的Entity、Mapper即可开始测试。

3. 使用代码生成器

代码生成器可以自动生成Entity, Mapper, Service, Controller模块的代码。

代码生成器(新)

代码生成器配置

代码生成器(旧)

3.1. 过程

依赖

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

代码

package person.rootwhois.blog;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;

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

/**
 * @Author: 陈广生
 * @Date: 2022/01/01/4:34 PM
 * @Description:
 */
public class MbgGeneratorMain {
    public static void main(String[] args) {
        List<String> tables = new ArrayList<>();
        tables.add("admin");
        tables.add("article");
        tables.add("article_sorts");
        tables.add("article_tags");
        tables.add("comment");
        tables.add("history");
        tables.add("sort");
        tables.add("tag");
        tables.add("web");

        FastAutoGenerator.create("jdbc:mysql:///blog?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true","root","root")
                .globalConfig(builder -> {
                    builder.author("陈广生")               //作者
                            .outputDir(System.getProperty("user.dir")+"/src/main/java")    //输出路径(写到java目录)
                            .enableSwagger()           //开启swagger
                            .commentDate("yyyy-MM-dd")
                            .fileOverride();            //开启覆盖之前生成的文件

                })
                .packageConfig(builder -> {
                    builder.parent("person.rootwhois.blog")
                            .moduleName("")
                            .entity("entity")
                            .service("service")
                            .serviceImpl("service.impl")
                            .controller("controller")
                            .mapper("mapper")
                            .xml("mapper")
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml,System.getProperty("user.dir")+"/src/main/resources/mapper"));
                })
                .strategyConfig(builder -> {
                    builder.addInclude(tables)
                            .addTablePrefix("")
                            .serviceBuilder()
                            .formatServiceFileName("%sService")
                            .formatServiceImplFileName("%sServiceImpl")
                            .entityBuilder()
                            .addTableFills(new Column("create_time", FieldFill.INSERT))
                            .addTableFills(new Column("updateTime", FieldFill.INSERT))
                            .versionColumnName("version")
                            .enableLombok()
                            .logicDeleteColumnName("deleted")
                            .enableTableFieldAnnotation()
                            .controllerBuilder()
                            .formatFileName("%sController")
                            .enableRestStyle()
                            .mapperBuilder()
                            .superClass(BaseMapper.class)
                            .formatMapperFileName("%sMapper")
                            .enableMapperAnnotation()
                            .formatXmlFileName("%sMapper")
                            .enableBaseColumnList()
                            .enableMapperAnnotation()
                            .enableBaseResultMap();
                })
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }
}

生成到其他目录,选择性拷贝,或者不开启覆盖选项,这样不会覆盖之前已经修改过的类。

4. 注解

https://baomidou.com/pages/223848/#tablename

注解 描述
@TableName 作用在实体类上,表名注解,标识实体类对应的表。
@TableId 作用在实体类的属性中,主键注解。
@IdType 作用在实体类的属性中,设置主键的类型(如自增,无,雪花算法等)。
@TableField 作用在实体类的属性中,字段注解(非主键)。
@Version 乐观锁注解,标记在字段上。
@EnumValue 通枚举类注解(注解在枚举字段上)。
@TableLogic 表字段逻辑处理注解(逻辑删除)
@KeySequence 序列主键策略
@OrderBy 内置 SQL 默认指定排序,优先级低于 wrapper 条件查询

5. CURD

官方文档

用法:Mapper接口继承MyBatis-Plus的BaseMapper类,并传入实体的范型。

package person.rootwhois.blog.mapper;

import person.rootwhois.blog.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author 陈广生
 * @since 2022-01-05
 */
@Mapper
public interface UserMapper extends BaseMapper<User> {

}
// 根据id查询对应的记录
User user = userMapper.selectById(1L);

// 根据id查询对应的记录
User user = userMapper.selectById(1L);

// 查询全部用户
List<User> users = userMapper.selectList(null);

// 根据id插入传入的参数,并返回受影响的行数
int result = userMapper.insert(user);

// 根据id更新对应记录,并返回受影响的行数
int i = userMapper.updateById(user);

// 批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));

// 使用map指定条件查询
List<User> users = userMapper.selectByMap(map);

// 删除操作,把查询操作的select换成delete即可

6. 条件构造器Wrapper

6.1. QueryWrapper

6.2. UpdateWrapper

条件构造器

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

wrapper.isNotNull("name")
  .isNotNull("email")
  .between("age",20,30)
  .notLike("name","e")
  .likeRight("email","t")
  .eq("name","n")
  .orderByAsc("id");
wrapper.inSql("id","select id from user where id<3");    // where id in (sql)

List<User> users = userMapper.selectList(wrapper);

7. 主键生成策略

这里对应@TableId的一个属性IdType

    /**
     * 数据库ID自增
     * <p>该类型请确保数据库设置了 ID自增 否则无效</p>
     */
    AUTO(0),
    /**
     * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
     */
    NONE(1),
    /**
     * 用户输入ID
     * <p>该类型可以通过自己注册自动填充插件进行填充</p>
     */
    INPUT(2),

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 分配ID (主键类型为number或string),
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
     *
     * @since 3.3.0
     */
    ASSIGN_ID(3),
    /**
     * 分配UUID (主键类型为 string)
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
     */
    ASSIGN_UUID(4);

8. 自动填充

package person.rootwhois.blog.handler;

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

import java.util.Date;

/**
 * @Author: 陈广生
 * @Date: 2022/01/05/12:05 PM
 * @Description:
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
  @Override
  public void insertFill(MetaObject metaObject) {
    this.setFieldValByName("create_time", new Date(), metaObject);
    this.setFieldValByName("update_time", new Date(), metaObject);
  }

  @Override
  public void updateFill(MetaObject metaObject) {
    this.setFieldValByName("update_time", new Date(), metaObject);
  }
}
@ApiModelProperty("创建时间")
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;

@ApiModelProperty("更新时间")
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;

9. 乐观锁配置

@ApiModelProperty("版本号")
@TableField("version")
@Version
private Integer version;
package person.rootwhois.blog.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * @Author: 陈广生
 * @Date: 2022/01/05/2:06 PM
 * @Description:
 */
@Configuration
@EnableTransactionManagement
@MapperScan("person.rootwhois.blog.mapper")
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

}

10. 分页配置

package person.rootwhois.blog.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * @Author: 陈广生
 * @Date: 2022/01/05/2:06 PM
 * @Description:
 */
@Configuration
@EnableTransactionManagement
@MapperScan("person.rootwhois.blog.mapper")
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL))
        return interceptor;
    }
}
Page<Article> articlePage = new Page<>(1,5);
articleMapper.selectPage(articlePage, null);
articlePage.getRecords().forEach(System.out::println);
System.out.println(articlePage.getTotal());

11. 逻辑删除

@ApiModelProperty("是否已经删除")
@TableField("deleted")
@TableLogic
private Integer deleted;
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

12. TypeHandler

使用TypeHandler可以实现实体和JSON相互转化。

@Data
@Accessors(chain = true)
// 使用autoResultMap开启结果自动映射
@TableName(value = "info",autoResultMap = true)
public class Info implements Serializable {
   private static final long serialVersionUID = 1L;
   /**
    * 这个字段数据库存的是json字符串
    */
    // 这里有两个可选,JacksonTypeHandler和FastjsonTypeHandler。
   @TableField(typeHandler = JacksonTypeHandler.class)
   private User value;
}

JacksonTypeHandler和FastjsonTypeHandler的区别:

@TableField(typeHandler = JacksonTypeHandler.class)
private User value;

@TableField(typeHandler = FastjsonTypeHandler.class)
private User value;

JacksonTypeHandler可以支持MVC JSON解析和MySQL JSON解析。可以兼容MP的功能和满足支持MySQL JSON解析。

FastjsonTypeHandler不支持MySQL JSON的解析。

坑:使用的时候,不能自动识别泛型。但JSON的key必须使用双括号括起来。在实际使用中,Map<Integer, List<Integer>>的类型会被解析成Map<String, List<Integer>>,从而导致出现类型转换异常。

12.1. 自定义TypeHandler

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;


@MappedJdbcTypes(JdbcType.VARBINARY)
@MappedTypes({List.class})
public abstract class ListTypeHandler<T> extends BaseTypeHandler<List<T>> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType) throws SQLException {
        String content = CollUtil.isEmpty(parameter) ? null : JSON.toJSONString(parameter);
        ps.setString(i, content);
    }


    @Override
    public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.getListByJsonArrayString(rs.getString(columnName));
    }

    @Override
    public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.getListByJsonArrayString(rs.getString(columnIndex));
    }
     @Override
    public List<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.getListByJsonArrayString(cs.getString(columnIndex));
    }


    private List<T> getListByJsonArrayString(String content) {
        return StrUtil.isBlank(content) ? new ArrayList<>() : JSON.parseObject(content, this.specificType());
    }

    /**
     * 具体类型,由子类提供
     *
     * @return 具体类型
     */
    protected abstract TypeReference<List<T>> specificType();
}

使用:

import com.alibaba.fastjson.TypeReference;

import java.util.List;

public class UserListTypeHandler extends ListTypeHandler<User> {
    @Override
    protected TypeReference<List<User>> specificType() {
        return new TypeReference<List<User>>(){

        };
    }
}
Copyright © rootwhois.cn 2021-2022 all right reserved,powered by GitbookFile Modify: 2023-03-05 10:55:52

results matching ""

    No results matching ""