文章目录

  • MyBatis Plus 基础篇
    • 一、简介
    • 三、基本开发环境
      • 1. 准备数据
      • 2. Hello World
        • 第一步:创建一个 Spring Boot 项目
        • 第二步:编辑 pom.xml 文件添加相关的依赖
        • 第三步:配置 application.yml 文件
        • 第四步:创建对应的类
        • 第五步:愉快地测试
    • 四、常见注解
      • 案例:多表联查
        • 1. 准备数据
          • User 用户表(按之前的)
          • Role 角色表
          • Permission 权限表
          • UserRole 用户角色关联表
          • RolePermission 角色权限关联表
        • 2. 创建实体类
          • User 类
          • Role 类
          • Permission 类
          • UserRole 类
          • RolePermission 类
        • 3. 创建 Mapper 接口类
          • UserMapper 接口类
          • RoleMapper 接口类
        • 4. 在启动类中扫描 mapper
        • 5. 编辑 application.yml 文件
        • 6. 一起愉快地测试
  • MyBatis Plus 核心功能篇
    • 一、代码生成器
      • 1. 具体使用
        • 1)添加依赖
        • 2)编写配置
      • 2. 自定义模版引擎
      • 3. 自定义代码模版
      • 4. 自定义属性注入
    • 二、CRUD 接口
      • 1. Mapper CRUD 接口
        • insert
        • deleteById
        • deleteByMap
        • delete
        • deleteBatchIds
        • updateById
        • update
        • selectById
        • selectBatchIds
        • selectByMap
        • selectOne
        • selectCount
        • selectList
        • selectMaps
        • selectObjs
        • selectPage
        • selectMapsPage
        • 【简单示例】
      • 2. Service CRUD 接口
        • save
        • saveBatch
        • saveBatch
        • saveOrUpdateBatch
        • saveOrUpdateBatch
        • removeById
        • removeByMap
        • remove
        • removeByIds
        • updateById
        • update
        • updateBatchById
        • saveOrUpdate
        • getById
        • listByIds
        • listByMap
        • getOne
        • getMap
        • getObj
        • count
        • list
        • page
        • listMaps
        • listObjs
        • pageMaps
      • 3. mapper 层 选装件
    • 三、条件构造器
      • 1. AbstractWrapper
        • allEq
        • eq
        • ne
        • gt
        • ge
        • lt
        • le
        • between
        • notBetween
        • like
        • notLike
        • likeLeft
        • likeRight
        • isNull
        • isNotNull
        • in
        • notIn
        • inSql
        • notInSql
        • groupBy
        • orderByAsc
        • orderByDesc
        • orderBy
        • having
        • or
        • and
        • nested
        • apply
        • last
        • exists
        • notExists
      • 2. QueryWrapper
        • select
      • 3. UpdateWrapper
        • set
        • setSql
        • lambda
      • 4. 使用 Wrapper 自定义 SQL
        • Service.java
        • 方案一 注解方式 Mapper.java
        • 方案二 XML形式 Mapper.xml
    • 四、分页插件
      • 1. 简单示例
      • 2. XML 自定义分页
      • 3. Sequence 主键
        • 如何使用 Sequence 作为主键,但是实体主键类型是 String
  • MyBatis Plus 常用插件
    • 一、逻辑删除
    • 二、通用枚举
      • 1. 使用通用枚举属性的两种方式
        • 方式一:使用 @EnumValue 注解枚举属性
        • 方式二:枚举属性,实现 IEnum 接口
        • 2. 配置扫描通用枚举
        • 3. JSON 序列化处理
          • 1)Jackson
          • 2)Fastjson
    • 三、 自动填充功能
      • 1. 配置 @TableField 注解
    • 四、SQL 注入器
    • 五、攻击 SQL 阻断解析器
    • 六、性能分析插件
    • 七、执行 SQL 分析打印
    • 八、乐观锁插件
      • 1. 主要适用场景
      • 2. 使用方式
        • 插件配置
          • 特别说明
          • 示例
    • 九、动态数据源
      • 1. 快速了解
        • 1. 简介
        • 2. 优势
        • 3. 劣势
        • 4. 约定
        • 5. 建议
      • 2. 具体使用
    • 十、多租户 SQL 解析器
    • 十一、动态表明 SQL 解析
    • 十二、MyBatisX 快速开发插件
    • 十、多租户 SQL 解析器
    • 十一、动态表明 SQL 解析
    • 十二、MyBatisX 快速开发插件

MyBatis Plus 基础篇

一、简介

MyBatis 是一个半自动的 ORM 框架。

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

【提示】当前最新的版本 3.1.1

三、基本开发环境

  1. 基于 Java 开发,建议 jdk 1.8+ 版本。
  2. 需要使用到 Spring Boot
  3. 需要使用到 Maven
  4. 需要使用 MySQL

1. 准备数据

我们需要准备一张 user 表

DROP TABLE IF EXISTS user;CREATE TABLE user
(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);

然后给 user 表中添加一些数据

DELETE FROM user;INSERT INTO user (id, name, age, email) VALUES
(1, '翠花', 18, 'cuihua@kkb.com'),
(2, '春花', 20, 'chunhua@kkb.com'),
(3, '花花', 28, 'huahua@kkb.com'),
(4, '翠翠', 21, 'cuicui@kkb.com'),
(5, '春春', 24, 'chunchun@kkb.com');

2. Hello World

我们的目的:通过几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!

第一步:创建一个 Spring Boot 项目

​ 推荐,登录 https://start.spring.io 构建并下载一个干净的 Spring Boot 工程。

第二步:编辑 pom.xml 文件添加相关的依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.4.RELEASE</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version><scope>provided</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.1.1</version></dependency>    <dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>

【注意】引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring,以避免因版本差异导致的问题。

【如果是 SpringMVC 项目】

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId><version>3.1.1</version>
</dependency>

第三步:配置 application.yml 文件

编辑 application.yml 配置文件,主要是添加 druid 数据库的相关配置:

# DataSource Config
spring:datasource:# 此处使用 druid 数据源type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/aaa?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8username: rootpassword: 1234filters: statmaxActive: 20initialSize: 1maxWait: 60000minIdle: 1timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: truemaxOpenPreparedStatements: 20

在 Spring Boot 启动类中添加 @MapperScan 注解,用于扫描 Mapper 文件夹:

@SpringBootApplication
@MapperScan("com.kkb.mapper")
public class Application {public static void main(String[] args) {SpringApplication.run(QuickStartApplication.class, args);}}

【注意】如果是 SpringMVC 项目,需要在 <bean>标签中配置 MapperScan。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.kkb.mapper"/>
</bean>

然后还可以调整 SqlSessionFactory 为 Mybatis-Plus 的 SqlSessionFactory。

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/>
</bean>

第四步:创建对应的类

新建 User 类:

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

【注意】@Data 是 Lombok 提供的能让代码更加简洁的注解,能够减少大量的模板代码。注解在类上,为类提供读写属性,此外还提供了 equals()、hashCode()、toString() 方法。

如果在 Eclipse 中使用,需要我们手动安装,具体请参考 https://www.projectlombok.org/setup/eclipse

【参考:Lombok 常用注解】

@NonNull : 注解在参数上, 如果该类参数为 null , 就会报出异常,  throw new NullPointException(参数名)
@Cleanup : 注释在引用变量前, 自动回收资源 默认调用 close() 方法
@Getter/@Setter : 注解在类上, 为类提供读写属性
@Getter(lazy=true) :
@ToString : 注解在类上, 为类提供 toString() 方法
@EqualsAndHashCode : 注解在类上, 为类提供 equals() 和 hashCode() 方法
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor : 注解在类上, 为类提供无参,有指定必须参数, 全参构造函数
@Data : 注解在类上, 为类提供读写属性, 此外还提供了 equals()、hashCode()、toString() 方法
@Value :
@Builder : 注解在类上, 为类提供一个内部的 Builder
@SneakThrows :
@Synchronized : 注解在方法上, 为方法提供同步锁
@Log :
@Log4j : 注解在类上, 为类提供一个属性名为 log 的 log4j 的日志对象
@Slf4j : 注解在类上, 为类提供一个属性名为 log 的 log4j 的日志对象

新建 UserMapper 映射接口类:

public interface UserMapper extends BaseMapper<User> {}

第五步:愉快地测试

新建 KKBTest 测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test1 {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect(){// 此处 null 指的是不用根据参数去查询// 可以调用 CRUD 相关的多种方式// 1. 查询所有的数据List<User> userList = userMapper.selectList(null);userList.forEach(user -> System.out.println(user.getName()));// 2. 根据 id 删除userMapper.deleteById(1);// 3. 添加数据User user = new User();user.setName("老王");user.setEmail("laowang@kkb.com");user.setAge(18);userMapper.insert(user);// 4. 更新数据user.setName("老王王");user.setEmail("laowangwang@kkb.com");userMapper.updateById(user);}}

四、常见注解

  1. @TableName:表名描述
  2. @TableId:主键注释
  3. @TableField:字段注解(非主键)
  4. @Version:乐观锁注解,主要用于标注在字段上
  5. @EnumValue:通枚举类注解(注解在枚举字段上)
  6. @TableLogic:表字段逻辑处理注解(逻辑删除)
  7. @SqlParser:租户注解(支持注解在 mapper 上)
  8. @KeySequence:序列主键策略,属性有:value、resultMap

案例:多表联查

1. 准备数据

User 用户表(按之前的)
CREATE TABLE USER
(id BIGINT(20) NOT NULL COMMENT '主键ID',NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',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, 'cuihua', 18, 'test1@baomidou.com'),
(2, 'huahua', 20, 'test2@baomidou.com'),
(3, 'cunhua', 28, 'test3@baomidou.com'),
(4, 'laowang', 21, 'test4@baomidou.com'),
(5, 'xiaomei', 24, 'test5@baomidou.com');
Role 角色表
CREATE TABLE role (id BIGINT(20) NOT NULL COMMENT '主键ID',rolecode VARCHAR(100) NULL DEFAULT NULL COMMENT '角色编号',rolename VARCHAR(100) NULL DEFAULT NULL COMMENT '角色名字',PRIMARY KEY (id));INSERT INTO role(id, rolecode, rolename) VALUES (1, '001', '总裁'),(2, '002', '院长'),(3, '003', '组长');
Permission 权限表
CREATE TABLE permission (id BIGINT(20) NOT NULL COMMENT '主键ID',permissioncode VARCHAR(100) NULL DEFAULT NULL COMMENT '权限编号',permissionname VARCHAR(100) NULL DEFAULT NULL COMMENT '权限名字',path VARCHAR(100) NULL DEFAULT NULL COMMENT '映射路径',PRIMARY KEY (id));INSERT INTO permission(id, permissioncode, permissionname) VALUES (1, '111', '随便乱来'),(2, '222', '偶尔乱来'),(3, '333', '小小乱来');
UserRole 用户角色关联表
CREATE TABLE userrole (id BIGINT(20) NOT NULL COMMENT '主键ID',username VARCHAR(100) NULL DEFAULT NULL COMMENT '用户名',rolecode VARCHAR(100) NULL DEFAULT NULL COMMENT '角色编号',PRIMARY KEY (id));INSERT INTO userrole(id, username, rolecode) VALUES (1, 'cuihua', '001'),(2, 'chunhua', '003'),(3, 'huahua', '002');
RolePermission 角色权限关联表
CREATE TABLE rolepermission (id BIGINT(20) NOT NULL COMMENT '主键ID',rolecode VARCHAR(100) NULL DEFAULT NULL COMMENT '角色编号',permissioncode VARCHAR(100) NULL DEFAULT NULL COMMENT '权限编号',PRIMARY KEY (id));INSERT INTO rolepermission(id, rolecode, permissioncode) VALUES (1, '001', '111'),(2, '002', '222'),(3, '003', '333');

2. 创建实体类

文件路径:com.kkb.pojo

User 类
@Data
@TableName("user")
@ApiModel("用户类")
public class User implements Serializable {@ApiModelProperty(name = "id", value = "ID 主键")@TableId(type = IdType.AUTO)private String id;@ApiModelProperty(name = "name", value = "用户名")@TableField(fill = FieldFill.INSERT_UPDATE)private String name;@ApiModelProperty(name = "age", value = "年龄")@TableField(fill = FieldFill.INSERT_UPDATE)private Integer age;@ApiModelProperty(name = "email", value = "邮箱")@TableField(fill = FieldFill.INSERT_UPDATE)private String email;@TableField(exist = false)private Set<Role> roles = new HashSet<>();}

【简单理解】代码中 @ApiModel 和 @ApiModelProperty 注解出自于 Swagger 这块 API 简化开发开源框架,感兴趣的同学可以去了解一下。

在 Springboot 中使用,需要在 pom.xml 文件中引入其依赖。

<dependency><groupId>com.spring4all</groupId><artifactId>swagger-spring-boot-starter</artifactId><version>1.7.0.RELEASE</version>
</dependency>

另外,也可以单独引入它的依赖,可自己指定对应或最新版本。

<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>lastest version</version>
</dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>lastest version</version>
</dependency>
Role 类
@Data
@TableName("role")
@ApiModel("角色类")
public class Role implements Serializable {@ApiModelProperty(name = "id", value = "ID 主键")@TableId(type = IdType.AUTO)private String id;@ApiModelProperty(name = "roleCode", value = "角色编号")@TableField(fill = FieldFill.INSERT_UPDATE)private String roleCode;@ApiModelProperty(name = "roleName", value = "角色名")@TableField(fill = FieldFill.INSERT_UPDATE)private String roleName;@TableField(exist = false)private Set<Permission> permissions = new HashSet<>();}
Permission 类
@Data
@TableName("permission")
@ApiModel("权限类")
public class Permission implements Serializable {@ApiModelProperty(name = "id", value = "ID 主键")@TableId(type = IdType.AUTO)private String id;@ApiModelProperty(name = "permissionCode", value = "权限编号")@TableField(fill = FieldFill.INSERT_UPDATE)private String permissionCode;@ApiModelProperty(name = "permissionName", value = "权限名")@TableField(fill = FieldFill.INSERT_UPDATE)private String permissionName;@ApiModelProperty(name = "path", value = "映射路径")@TableField(fill = FieldFill.INSERT_UPDATE)private String path;}
UserRole 类
@Data
@TableName("userRole")
@ApiModel("用户角色关系类")
public class UserRole implements Serializable{@ApiModelProperty(name = "id", value = "ID 主键")@TableId(type = IdType.AUTO)private String id;@ApiModelProperty(name = "username", value = "用户名")@TableField(fill = FieldFill.INSERT_UPDATE)private String username;@ApiModelProperty(name = "roleCode", value = "角色编号")@TableField(fill = FieldFill.INSERT_UPDATE)private Integer roleCode;}
RolePermission 类
@Data
@TableName("rolePermission")
@ApiModel("角色权限关系类")
public class RolePermission implements Serializable{@ApiModelProperty(name = "id", value = "ID 主键")@TableId(type = IdType.AUTO)private String id;@ApiModelProperty(name = "roleCode", value = "角色编号")@TableField(fill = FieldFill.INSERT_UPDATE)private Integer roleCode;@ApiModelProperty(name = "permission", value = "权限编号")@TableField(fill = FieldFill.INSERT_UPDATE)private String permissionCode;}

3. 创建 Mapper 接口类

文件路径:com.kkb.mapper

UserMapper 接口类
public interface UserMapper extends BaseMapper<User> {@Select("select * from user where name = #{name}")public User findUserByName(String username);// 获取用户所拥有的角色@Select("select * from  role where rolecode in(select rolecode from userrole where username = #{userName})")public Set<Role> getUserRoles(String username);}
RoleMapper 接口类
public interface RoleMapper extends BaseMapper<User> {// 获取角色所拥有权限@Select("select * from permission where permissioncode in (select permissioncode from rolepermission where rolecode = #{roleCode})")public Set<Permission> getRolePermissions(String roleCode);}

4. 在启动类中扫描 mapper

@SpringBootApplication
@MapperScan("com.kkb.mapper")
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}

5. 编辑 application.yml 文件

mybatis-plus:type-aliases-package: com.kkb.pojoconfiguration:map-underscore-to-camel-case: true

6. 一起愉快地测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleMapper roleMapper;@Testpublic void testUserRole() {User user = userMapper.findUserByName("cuihua");Set<Role> roles = userMapper.getUserRoles("cuihua");for (Role role : roles) {role.setPermissions(roleMapper.getRolePermissions(role.getRoleCode()));}user.setRoles(roles);System.out.println(user);}
}

MyBatis Plus 核心功能篇

一、代码生成器

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

// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {/*** <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 + "!");}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("jobob");gc.setOpen(false);// gc.setSwagger2(true); 实体属性 Swagger2 注解mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");// dsc.setSchemaName("public");dsc.setDriverName("com.mysql.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("密码");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();pc.setModuleName(scanner("模块名"));pc.setParent("com.baomidou.ant");mpg.setPackageInfo(pc);// 自定义配置InjectionConfig cfg = new InjectionConfig() {@Overridepublic void initMap() {// to do nothing}};// 如果模板引擎是 freemarkerString templatePath = "/templates/mapper.xml.ftl";// 如果模板引擎是 velocity// String templatePath = "/templates/mapper.xml.vm";// 自定义输出配置List<FileOutConfig> focList = new ArrayList<>();// 自定义配置会被优先输出focList.add(new FileOutConfig(templatePath) {@Overridepublic String outputFile(TableInfo tableInfo) {// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;}});/*cfg.setFileCreate(new IFileCreate() {@Overridepublic boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {// 判断自定义文件夹是否需要创建checkDir("调用默认方法创建的目录");return false;}});*/cfg.setFileOutConfigList(focList);mpg.setCfg(cfg);// 配置模板TemplateConfig templateConfig = new TemplateConfig();// 配置自定义输出模板//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别// templateConfig.setEntity("templates/entity2.java");// templateConfig.setService();// templateConfig.setController();templateConfig.setXml(null);mpg.setTemplate(templateConfig);// 策略配置StrategyConfig strategy = new StrategyConfig();strategy.setNaming(NamingStrategy.underline_to_camel);strategy.setColumnNaming(NamingStrategy.underline_to_camel);strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");strategy.setEntityLombokModel(true);strategy.setRestControllerStyle(true);strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));strategy.setSuperEntityColumns("id");strategy.setControllerMappingHyphenStyle(true);strategy.setTablePrefix(pc.getModuleName() + "_");mpg.setStrategy(strategy);mpg.setTemplateEngine(new FreemarkerTemplateEngine());mpg.execute();}}

1. 具体使用

1)添加依赖

MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:

  • 添加 代码生成器 依赖

    <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.1.1</version>
    </dependency>
    
  • 添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都用不爽,还可以采用自定义模板引擎。

    Velocity(默认)

    <dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.1</version>
    </dependency>
    

    Freemarker

    <dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.28</version>
    </dependency>
    

    Beetl

    <dependency><groupId>com.ibeetl</groupId><artifactId>beetl</artifactId><version>3.0.0.M1</version>
    </dependency>
    

    【注意】如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。

    AutoGenerator generator = new AutoGenerator();// set freemarker engine
    generator.setTemplateEngine(new FreemarkerTemplateEngine());// set beetl engine
    generator.setTemplateEngine(new BeetlTemplateEngine());// set custom engine (reference class is your custom engine class)
    generator.setTemplateEngine(new CustomTemplateEngine());// other config
    ...
    

2)编写配置

MyBatis-Plus 的代码生成器提供了大量的自定义参数供用户选择,能够满足绝大部分人的使用需求。

  • 配置 GlobalConfig

    GlobalConfig globalConfig = new GlobalConfig();
    globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
    globalConfig.setAuthor("jobob");
    globalConfig.setOpen(false);
    
  • 配置 DataSourceConfig

    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
    dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
    dataSourceConfig.setUsername("root");
    dataSourceConfig.setPassword("password");
    
  • 配置 DataSourceConfig

    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
    dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
    dataSourceConfig.setUsername("root");
    dataSourceConfig.setPassword("password");
    

2. 自定义模版引擎

请继承类 com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine

3. 自定义代码模版

// 指定自定义模板路径, 位置:/resources/templates/entity2.java.ftl(或者是.vm)
// 注意不要带上.ftl(或者是.vm), 会根据使用的模板引擎自动识别
TemplateConfig templateConfig = new TemplateConfig().setEntity("templates/entity2.java");AutoGenerator mpg = new AutoGenerator();
// 配置自定义模板
mpg.setTemplate(templateConfig);

4. 自定义属性注入

InjectionConfig injectionConfig = new InjectionConfig() {//自定义属性注入:abc//在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性@Overridepublic void initMap() {Map<String, Object> map = new HashMap<>();map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");this.setMap(map);}
};
AutoGenerator mpg = new AutoGenerator();
//配置自定义属性注入
mpg.setCfg(injectionConfig);
entity2.java.ftl
自定义属性注入abc=${cfg.abc}entity2.java.vm
自定义属性注入abc=$!{cfg.abc}

二、CRUD 接口

1. Mapper CRUD 接口

【简单说明】

  • 通用 CRUD 封装 BaseMapper 接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
  • 泛型 T 为任意实体对象
  • 参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
  • 对象 Wrapper 为 条件构造器

insert

/*** 插入一条记录** @param entity 实体对象* @return 插入成功记录数*/
int insert(T entity);

deleteById

/*** 根据 ID 删除** @param id 主键ID* @return 删除成功记录数*/
int deleteById(Serializable id);

deleteByMap

/*** 根据 columnMap 条件,删除记录** @param columnMap 表字段 map 对象* @return 删除成功记录数*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

delete

/*** 根据 entity 条件,删除记录** @param wrapper 实体对象封装操作类(可以为 null)* @return 删除成功记录数*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

deleteBatchIds

/*** 删除(根据ID 批量删除)** @param idList 主键ID列表(不能为 null 以及 empty)* @return 删除成功记录数*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

updateById

/*** 根据 ID 修改** @param entity 实体对象* @return 修改成功记录数*/
int updateById(@Param(Constants.ENTITY) T entity);

update

/*** 根据 whereEntity 条件,更新记录** @param entity        实体对象 (set 条件值,可为 null)* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)* @return 修改成功记录数*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

selectById

/*** 根据 ID 查询** @param id 主键ID* @return 实体*/
T selectById(Serializable id);

selectBatchIds

/*** 查询(根据ID 批量查询)** @param idList 主键ID列表(不能为 null 以及 empty)* @return 实体集合*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

selectByMap

/*** 查询(根据 columnMap 条件)** @param columnMap 表字段 map 对象* @return 实体集合*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

selectOne

/*** 根据 entity 条件,查询一条记录** @param queryWrapper 实体对象* @return 实体*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectCount

/*** 根据 Wrapper 条件,查询总记录数** @param queryWrapper 实体对象* @return 满足条件记录数*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectList

/*** 根据 entity 条件,查询全部记录** @param queryWrapper 实体对象封装操作类(可以为 null)* @return 实体集合*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectMaps

/*** 根据 Wrapper 条件,查询全部记录** @param queryWrapper 实体对象封装操作类(可以为 null)* @return 字段映射对象 Map 集合*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectObjs

/*** 根据 Wrapper 条件,查询全部记录* 注意: 只返回第一个字段的值** @param queryWrapper 实体对象封装操作类(可以为 null)* @return 字段映射对象集合*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectPage

/*** 根据 entity 条件,查询全部记录(并翻页)** @param page         分页查询条件(可以为 RowBounds.DEFAULT)* @param queryWrapper 实体对象封装操作类(可以为 null)* @return 实体分页对象*/
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectMapsPage

/*** 根据 Wrapper 条件,查询全部记录(并翻页)** @param page         分页查询条件* @param queryWrapper 实体对象封装操作类* @return 字段映射对象 Map 分页对象*/
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

【简单示例】

@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect(){// 1.  添加数据(一定要保证表的主键是自增状态)/*User user = new User();user.setName("老王");user.setEmail("laowang@kkb.com");user.setAge(18);userMapper.insert(user);*/// 2. 根据 id 删除// userMapper.deleteById(6);// 3. 根据 columnMap 条件,删除记录// Maps 属于 com.google.common.collect.Maps 包,来自于 Guava// Guava 中文是石榴的意思,该项目是 Google 的一个开源项目,包含许多 Google 核心的 Java 常用库/*Map<String, Object> columnMap = Maps.newHashMap();columnMap.put("id", 3);userMapper.deleteByMap(columnMap);*/// 4. 查询所有数据// 不指定条件,直接传递 null/*List<User> userList = userMapper.selectList(null);userList.forEach(user -> System.out.println("用户:" + user));*/// 5. 批量查询List<String> idList = new ArrayList<String>();idList.add("1");idList.add("2");List<User> userList = userMapper.selectBatchIds(idList);userList.forEach(user -> System.out.println("用户:" + user));}
}

2. Service CRUD 接口

【简单说明】

  • 通用 Service CRUD 封装 IService 接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆。
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  • 对象 Wrapper 为 条件构造器

save

/*** 插入一条记录(选择字段,策略插入)** @param entity 实体对象*/
boolean save(T entity);

saveBatch

/*** 插入(批量)** @param entity 实体对象*/
boolean saveBatch(T entity);

saveBatch

/**
* 插入(批量)
*
* @param entityList 实体对象集合
* @param batchSize  插入批次数量*/
boolean saveBatch(Collection<T> entityList, int batchSize);

saveOrUpdateBatch

/*** 批量修改插入** @param entityList 实体对象集合*/
boolean saveOrUpdateBatch(Collection<T> entityList);

saveOrUpdateBatch

/*** 批量修改插入** @param entityList 实体对象集合* @param batchSize  每次的数量*/
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

removeById

/*** 根据 ID 删除** @param id 主键ID*/
boolean removeById(Serializable id);

removeByMap

/*** 根据 columnMap 条件,删除记录** @param columnMap 表字段 map 对象*/
boolean removeByMap(Map<String, Object> columnMap);

remove

/*** 根据 entity 条件,删除记录** @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
boolean remove(Wrapper<T> queryWrapper);

removeByIds

/*** 删除(根据ID 批量删除)** @param idList 主键ID列表*/
boolean removeByIds(Collection<? extends Serializable> idList);

updateById

/*** 根据 ID 选择修改** @param entity 实体对象*/
boolean updateById(T entity);

update

/*** 根据 whereEntity 条件,更新记录** @param entity        实体对象* @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}*/
boolean update(T entity, Wrapper<T> updateWrapper);

updateBatchById

/*** 根据ID 批量更新** @param entityList 实体对象集合* @param batchSize  更新批次数量*/
boolean updateBatchById(Collection<T> entityList, int batchSize);

saveOrUpdate

/*** TableId 注解存在更新记录,否插入一条记录** @param entity 实体对象*/
boolean saveOrUpdate(T entity);

getById

/*** 根据 ID 查询** @param id 主键ID*/
T getById(Serializable id);

listByIds

/*** <p>* 查询(根据ID 批量查询)* </p>** @param idList 主键ID列表*/
Collection<T> listByIds(Collection<? extends Serializable> idList);

listByMap

/*** 查询(根据 columnMap 条件)** @param columnMap 表字段 map 对象*/
Collection<T> listByMap(Map<String, Object> columnMap);

getOne

/*** 根据 Wrapper,查询一条记录** @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}* @param throwEx      有多个 result 是否抛出异常*/
T getOne(Wrapper<T> queryWrapper, boolean throwEx);

getMap

/*** 根据 Wrapper,查询一条记录** @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
Map<String, Object> getMap(Wrapper<T> queryWrapper);

getObj

/*** 根据 Wrapper,查询一条记录** @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
Object getObj(Wrapper<T> queryWrapper);

count

/*** 根据 Wrapper 条件,查询总记录数** @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
int count(Wrapper<T> queryWrapper);

list

/*** 查询列表** @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
List<T> list(Wrapper<T> queryWrapper);

page

/*** 翻页查询** @param page         翻页对象* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);

listMaps

/*** 查询列表** @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);

listObjs

/*** 根据 Wrapper 条件,查询全部记录** @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
List<Object> listObjs(Wrapper<T> queryWrapper);

pageMaps

/*** 翻页查询** @param page         翻页对象* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

3. mapper 层 选装件

选装件位于 com.baomidou.mybatisplus.extension.injector.methods.additional 包下 需要配合 Sql 注入器 使用。

int insertBatchSomeColumn(List<T> entityList);
int deleteByIdWithFill(T entity);

三、条件构造器

【简单说明】

  • 以下出现的第一个入参 boolean condition 表示该条件是否加入最后生成的 sql 中
  • 以下代码块内的多个方法均为从上往下补全个别 boolean 类型的入参,默认为 true
  • 以下出现的泛型 Param 均为 Wrapper 的子类实例(均具有AbstractWrapper 的所有方法)
  • 以下方法在入参中出现的 R 为泛型,在普通 wrapper 中是 String,在LambdaWrapper 中是 函数(例:Entity::getId,Entity 为实体类,getId为字段 idgetMethod
  • 以下方法入参中的 R column 均表示数据库字段,当 RString 时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹),而不是实体类数据字段名。
  • 以下举例均为使用普通 wrapper,入参为 MapList 的均以json形式表现!
  • 使用中如果入参的 Map 或者 List,则不会加入最后生成的 sql 中。
  • 有任何疑问就点开源码看,另外还可以补充下 lambda 函数。

1. AbstractWrapper

QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件

注意:entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

allEq

allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
  • 全部 eq (或个别 isNull)

个别参数说明:

params : key 为数据库字段名,value 为字段值
null2IsNull : 为true则在mapvaluenull时调用 isNull 方法,为false时则忽略valuenull

  • 例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 = '老王'
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 : 过滤函数,是否允许字段传入比对条件中
paramsnull2IsNull : 同上

  • 例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 = '老王'

eq

eq(R column, Object val)
eq(boolean condition, R column, Object val)
  • 等于 =
  • 例: eq("name", "老王")—>name = '老王'

ne

ne(R column, Object val)
ne(boolean condition, R column, Object val)
  • 不等于 <>
  • 例: ne("name", "老王")—>name <> '老王'

gt

gt(R column, Object val)
gt(boolean condition, R column, Object val)
  • 大于 >
  • 例: gt("age", 18)—>age > 18

ge

ge(R column, Object val)
ge(boolean condition, R column, Object val)
  • 大于等于 >=
  • 例: ge("age", 18)—>age >= 18

lt

lt(R column, Object val)
lt(boolean condition, R column, Object val)
  • 小于 <
  • 例: lt("age", 18)—>age < 18

le

le(R column, Object val)
le(boolean condition, R column, Object val)
  • 小于等于 <=
  • 例: le("age", 18)—>age <= 18

between

between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)
  • BETWEEN 值1 AND 值2
  • 例: between("age", 18, 30)—>age between 18 and 30

notBetween

notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)
  • NOT BETWEEN 值1 AND 值2
  • 例: notBetween("age", 18, 30)—>age not between 18 and 30

like

like(R column, Object val)
like(boolean condition, R column, Object val)
  • LIKE ‘%值%’
  • 例: like("name", "王")—>name like '%王%'

notLike

notLike(R column, Object val)
notLike(boolean condition, R column, Object val)
  • NOT LIKE ‘%值%’
  • 例: notLike("name", "王")—>name not like '%王%'

likeLeft

likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)
  • LIKE ‘%值’
  • 例: likeLeft("name", "王")—>name like '%王'

likeRight

likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)
  • LIKE ‘值%’
  • 例: likeRight("name", "王")—>name like '王%'

isNull

isNull(R column)
isNull(boolean condition, R column)
  • 字段 IS NULL
  • 例: isNull("name")—>name is null

isNotNull

isNotNull(R column)
isNotNull(boolean condition, R column)
  • 字段 IS NULL
  • 例: isNotNull("name")—>name is not null

in

in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)
  • 字段 IN (value.get(0), value.get(1), …)
  • 例: in("age",{1,2,3})—>age in (1,2,3)
in(R column, Object... values)
in(boolean condition, R column, Object... values)
  • 字段 IN (v0, v1, …)
  • 例: in("age", 1, 2, 3)—>age in (1,2,3)

notIn

notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)
  • 字段 IN (value.get(0), value.get(1), …)
  • 例: notIn("age",{1,2,3})—>age not in (1,2,3)
notIn(R column, Object... values)
notIn(boolean condition, R column, Object... values)
  • 字段 NOT IN (v0, v1, …)
  • 例: notIn("age", 1, 2, 3)—>age not in (1,2,3)

inSql

inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
  • 字段 IN ( sql语句 )
  • 例: inSql("age", "1,2,3,4,5,6")—>age in (1,2,3,4,5,6)
  • 例: inSql("id", "select id from table where id < 3")—>id in (select id from table where id < 3)

notInSql

notInSql(R column, String inValue)
notInSql(boolean condition, R column, String inValue)
  • 字段 NOT IN ( sql语句 )
  • 例: notInSql("age", "1,2,3,4,5,6")—>age not in (1,2,3,4,5,6)
  • 例: notInSql("id", "select id from table where id < 3")—>age not in (select id from table where id < 3)

groupBy

groupBy(R... columns)
groupBy(boolean condition, R... columns)
  • 分组:GROUP BY 字段, …
  • 例: groupBy("id", "name")—>group by id,name

orderByAsc

orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)
  • 排序:ORDER BY 字段, … ASC
  • 例: orderByAsc("id", "name")—>order by id ASC,name ASC

orderByDesc

orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
  • 排序:ORDER BY 字段, … DESC
  • 例: orderByDesc("id", "name")—>order by id DESC,name DESC

orderBy

orderBy(boolean condition, boolean isAsc, R... columns)
  • 排序:ORDER BY 字段, …
  • 例: orderBy(true, true, "id", "name")—>order by id ASC,name ASC

having

having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
  • HAVING ( sql语句 )
  • 例: having("sum(age) > 10")—>having sum(age) > 10
  • 例: having("sum(age) > {0}", 11)—>having sum(age) > 11

or

or()
or(boolean condition)
  • 拼接 OR

注意事项:

主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)

  • 例: eq("id",1).or().eq("name","老王")—>id = 1 or name = '老王'
or(Function<Param, Param> func)
or(boolean condition, Function<Param, Param> func)
  • OR 嵌套
  • 例: or(i -> i.eq("name", "李白").ne("status", "活着"))—>or (name = '李白' and status <> '活着')

and

and(Function<Param, Param> func)
and(boolean condition, Function<Param, Param> func)
  • AND 嵌套
  • 例: and(i -> i.eq("name", "李白").ne("status", "活着"))—>and (name = '李白' and status <> '活着')

nested

nested(Function<Param, Param> func)
nested(boolean condition, Function<Param, Param> func)
  • 正常嵌套 不带 AND 或者 OR
  • 例: nested(i -> i.eq("name", "李白").ne("status", "活着"))—>(name = '李白' and status <> '活着')

apply

apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
  • 拼接 sql

注意事项:

该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!

  • 例: apply("id = 1")—>id = 1
  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

last

last(String lastSql)
last(boolean condition, String lastSql)
  • 无视优化规则直接拼接到 sql 的最后

注意事项:只能调用一次,多次调用以最后一次为准,有 sql 注入的风险,请谨慎使用。

  • 例:last("limit 1")

exists

exists(String existsSql)
exists(boolean condition, String existsSql)
  • 拼接 EXISTS ( sql语句 )
  • 例: exists("select id from table where age = 1")—>exists (select id from table where age = 1)

notExists

notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
  • 拼接 NOT EXISTS ( sql语句 )
  • 例: notExists("select id from table where age = 1")—>not exists (select id from table where age = 1)

2. QueryWrapper

【简单说明】

继承自 AbstractWrapper,自身的内部属性 entity 也用于生成 where 条件
及 LambdaQueryWrapper,可以通过 new QueryWrapper().lambda() 方法获取。

select

select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
  • 设置查询字段

【简单说明】

以上方分法为两类。
第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper 内的 entity 属性有值, 这两类方法重复调用以最后一次为准。

  • 例: select("id", "name", "age")
  • 例: select(i -> i.getProperty().startsWith("test"))

【简单示例】

@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect2(){IPage<User> page = selectUserPage();List<User> userList = page.getRecords();userList.forEach(user -> System.out.println("用户:" + user));}// 查询年龄在 20-30 名字为 cuihua 的用户public  IPage<User>  selectUserPage() {Page page = new Page<User>(1, 20);QueryWrapper<User> queryWrapper = new QueryWrapper<User>().between("age", 18, 20).eq("name", "cuihua");return userMapper.selectPage(page, queryWrapper);}
}

3. UpdateWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

set

set(String column, Object val)
set(boolean condition, String column, Object val)
  • SQL SET 字段
  • 例: set("name", "老李头")
  • 例: set("name", "")—>数据库字段值变为空字符串
  • 例: set("name", null)—>数据库字段值变为null

setSql

setSql(String sql)
  • 设置 SET 部分 SQL
  • 例: set("name = '老李头')

lambda

  • 获取 LambdaWrapper
    QueryWrapper中是获取LambdaQueryWrapper
    UpdateWrapper中是获取LambdaUpdateWrapper

4. 使用 Wrapper 自定义 SQL

【需求来源】

在使用了 mybatis-plus 之后,自定义 SQL 的同时也想使用 Wrapper 的便利应该怎么办? 在 mybatis-plus 版本 3.0.7 得到了完美解决 版本需要大于或等于3.0.7, 以下两种方案取其一即可。

Service.java

mysqlMapper.getAll(Wrappers.<MysqlData>lambdaQuery().eq(MysqlData::getGroup, 1));

方案一 注解方式 Mapper.java

@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

方案二 XML形式 Mapper.xml

<select id="getAll" resultType="MysqlData">SELECT * FROM mysql_data ${ew.customSqlSegment}
</select>

四、分页插件

1. 简单示例

<!-- spring xml 方式 -->
<plugins><plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"><property name="sqlParser" ref="自定义解析类、可以没有" /><property name="dialectClazz" value="自定义方言类、可以没有" /></plugin>
</plugins>
//Spring boot 方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {/*** 分页插件*/@Beanpublic PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();}
}

2. XML 自定义分页

1)UserMapper 类

public interface UserMapper{//可以继承或者不继承BaseMapper/*** <p>* 查询 : 根据state状态查询用户列表,分页显示* 注意!!: 如果入参是有多个,需要加注解指定参数名才能在xml中取值* </p>** @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)* @param state 状态* @return 分页对象*/IPage<User> selectPageVo(Page page, @Param("state") Integer state);
}

2)UserMapper.xml 等同于编写一个普通的 list 查询,mybatis-plus 自动分页。

<select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">SELECT id,name FROM user WHERE state=#{state}
</select>

3)UserServiceImpl 类,调用分页方法。

public IPage<User> selectUserPage(Page<User> page, Integer state) {// 不进行 count sql 优化,解决 MP 无法自动优化 SQL 问题,这时候你需要自己查询 count 部分// page.setOptimizeCountSql(false);// 当 total 为非 0 时(默认为 0),分页插件不会进行 count 查询// 要点!! 分页返回的对象与传入的对象是同一个return userMapper.selectPageVo(page, state);
}

3. Sequence 主键

实体主键支持Sequence

  • Oracle 等数据库主键策略配置 Sequence

  • GlobalConfiguration 配置 KeyGenerator

  GlobalConfiguration gc = new GlobalConfiguration();gc.setKeyGenerator(new OracleKeyGenerator());
  • mybatis-plus-boot-starter 配置参考
@Bean
public OracleKeyGenerator oracleKeyGenerator(){return new OracleKeyGenerator();
}
  • 实体类配置主键 Sequence,

    指定主键 @TableId(type=IdType.INPUT) // 不能使用AUTO

@TableName("TEST_SEQUSER")
@KeySequence("SEQ_TEST")//类注解
public class TestSequser{@TableId(value = "ID", type = IdType.INPUT)private Long id;}
  • 支持父类定义 @KeySequence, 子类使用,这样就可以几个表共用一个Sequence
@KeySequence("SEQ_TEST")
public abstract class Parent{}public class Child extends Parent{}

以上步骤就可以使用Sequence当主键了。

Spring MVC:xml配置,请参考 https://mp.baomidou.com/install

如何使用 Sequence 作为主键,但是实体主键类型是 String

也就是说,表的主键是varchar2, 但是需要从sequence中取值

  • 1.实体定义@KeySequence 注解clazz指定类型String.class
  • 2.实体定义主键的类型String
@KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
public class YourEntity{@TableId(value = "ID_STR", type = IdType.INPUT)private String idStr;...
}
  • 3.正常配置GlobalConfiguration.keyGenerator
@Bean
public GlobalConfiguration globalConfiguration() {GlobalConfiguration conf = new GlobalConfiguration();conf.setKeyGenerator(new OracleKeyGenerator());return conf;
}

MyBatis Plus 常用插件

一、逻辑删除

在 Spring Boot 中的 application.yml 文件中配置。如果我们保持默认,则此配置就不用写了。

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

然后在实体类上,添加 @TableLogic 注解。

@TableLogic
private Integer deleted;

【注意】使用 mp 自带方法删除和查找都会附带逻辑删除功能 ,但自己写的 xml 不会。

java 逻辑删除简单理解就是“删除”,它是为了方便数据恢复和保护数据本身价值等等的一种方案。如果这个数据你需要再去查出来,就不应该使用逻辑删除,而是以一个状态去表示。比如,员工离职、帐号锁定等都应该是一个状态字段,不适合使用逻辑删除。如果是要查找删除数据,则需要单独手写 SQL 来实现,比如需要查看过往所有数据的统计汇总信息。

二、通用枚举

主要是为了让 MyBatis 更优雅的使用枚举属性,抛弃那些超繁琐的配置。

从 3.1.1 版本开始,直接使用默认枚举类会更好。

推荐的配置方式:

1)使用 IEnum 接口,推荐配置 defaultEnumTypeHandler

2)使用注解枚举处理,推荐配置 typeEnumsPackage

3)注解枚举处理与 IEnum 接口,推荐配置 typeEnumsPackage

4)与原生枚举混用,需配置 defaultEnumTypeHandler 与 typeEnumsPackage

1. 使用通用枚举属性的两种方式

方式一:使用 @EnumValue 注解枚举属性

public enum GradeEnum {PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");GradeEnum(int code, String descp) {this.code = code;this.descp = descp;}@EnumValue//标记数据库存的值是codeprivate final int code;//。。。
}

方式二:枚举属性,实现 IEnum 接口

public enum AgeEnum implements IEnum<Integer> {ONE(1, "一岁"),TWO(2, "二岁"),THREE(3, "三岁");private int value;private String desc;@Overridepublic Integer getValue() {return this.value;}
}

实体属性使用枚举类型

public class User{/*** 名字* 数据库字段: name varchar(20)*/private String name;/*** 年龄,IEnum接口的枚举处理* 数据库字段:age INT(3)*/private AgeEnum age;/*** 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):* 数据库字段:grade INT(2)*/private GradeEnum grade;
}

2. 配置扫描通用枚举

编辑 Spring Boot 项目 applicaton.yml 文件

mybatis-plus:# 支持统配符 * 或者 ; 分割typeEnumsPackage: com.kkb.entity.enums

3. JSON 序列化处理

1)Jackson

在需要响应描述字段的 get 方法上添加 @JsonValue 注解即可

2)Fastjson

全局处理方式

FastJsonConfig config = new FastJsonConfig();// 设置WriteEnumUsingToString    config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);converter.setFastJsonConfig(config);

局部处理方式

@JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
private UserStatus status;

二选一,然后在枚举中重写 toString() 方法。

三、 自动填充功能

在使用的时候,需要实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

1. 配置 @TableField 注解

注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置。

public class User {// 注意!这里需要标记为填充字段@TableField(.. fill = FieldFill.INSERT)private String fillField;....
}

自定义实现类,需要实现 MetaObjectHandler 接口,填充处理器在 Spring Boot 中使用的时候,需要再加上 @Component 注解。

必须使用父类的 setFieldValByName() 或者 setInsertFieldValByName / setUpdateFieldValByName 方法,否则不会根据注解中的 FiledFill.xxx 来区分。

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {private static final Logger LOGGER = LoggerFactory.getLogger(MyMetaObjectHandler.class);@Overridepublic void insertFill(MetaObject metaObject) {LOGGER.info("start insert fill ....");this.setFieldValByName("operator", "Jerry", metaObject);//版本号3.0.6以及之前的版本//this.setInsertFieldValByName("operator", "Jerry", metaObject);//@since 快照:3.0.7.2-SNAPSHOT, @since 正式版暂未发布3.0.7}@Overridepublic void updateFill(MetaObject metaObject) {LOGGER.info("start update fill ....");this.setFieldValByName("operator", "Tom", metaObject);//this.setUpdateFieldValByName("operator", "Tom", metaObject);//@since 快照:3.0.7.2-SNAPSHOT, @since 正式版暂未发布3.0.7}
}
public enum FieldFill {/*** 默认不处理*/DEFAULT,/*** 插入填充字段*/INSERT,/*** 更新填充字段*/UPDATE,/*** 插入和更新填充字段*/INSERT_UPDATE
}

四、SQL 注入器

注入器配置:全局配置 sqlInjector 用于注入 ISqlInjector 接口的子类,实现自定义方法注入。

自定义的通用方法可以实现接口 ISqlInjector ,也可以继承抽象类 AbstractSqlInjector 注入通用方法 SQL 语句, 然后继承 BaseMapper 添加自定义方法,全局配置 sqlInjector 注入 MP 会自动将类所有方法注入到 mybatis 容器中。

public interface ISqlInjector {/*** 检查SQL是否注入(已经注入过不再注入)** @param builderAssistant mapper 信息* @param mapperClass      mapper 接口的 class 对象*/void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);
}

五、攻击 SQL 阻断解析器

主要作用:阻止恶意的全表更新删除。

@Bean
public PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();...List<ISqlParser> sqlParserList = new ArrayList<>();// 攻击 SQL 阻断解析器、加入解析链sqlParserList.add(new BlockAttackSqlParser());paginationInterceptor.setSqlParserList(sqlParserList);...return paginationInterceptor;
}

六、性能分析插件

性能分析拦截器,用于输出每条 SQL 语句及其执行时间。如果对 SQL 的打印效果要求较高,可以使用下面的【执行SQL分析打印】插件。

先配置 <plugins>标签

<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>

然后,在 Spring Boot 中使用

//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {/*** SQL执行效率插件*/@Bean@Profile({"dev","test"})// 设置 dev test 环境开启public PerformanceInterceptor performanceInterceptor() {return new PerformanceInterceptor();}
}

参数说明

  • 参数:maxTime SQL 执行最大时长,超过自动停止运行,有助于发现问题。

  • 参数:format SQL SQL是否格式化,默认false。

**【**注意】该插件只用于开发环境,不建议生产环境使用。

七、执行 SQL 分析打印

在 3.1.0+ 版本,该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长。

1)在 Maven 中引入 p6spy 依赖

<dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>最新版本</version>
</dependency>

2)配置 application.yml 文件

spring:datasource:driver-class-name: com.p6spy.engine.spy.P6SpyDriverurl: jdbc:p6spy:h2:mem:test...

3)配置 spy.properties 文件

module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,batch,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

【注意】

driver-class-name 为 p6spy 提供的驱动类。

​ url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址。

​ ❤ 此插件有性能损耗,不建议生产环境使用。

八、乐观锁插件

1. 主要适用场景

意图:当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式

  • 取出记录时,获取当前 version
  • 更新时,带上这个 version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

2. 使用方式

插件配置

在 Spring Boot 中的配置

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

2)一定要在实体字段上使用 @Version 注解

@Version
private Integer version;
特别说明
  • 支持的数据类型:int, Integer, long, Long, Date, Timestamp, LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
示例

编写 Java 代码,具体如下:

int id = 100;
int version = 2;User u = new User();
u.setId(id);
u.setVersion(version);
u.setXXX(xxx);if(userService.updateById(u)){System.out.println("Update successfully");
}else{System.out.println("Update failed due to modified by others");
}

上面所对应的 SQL 原理非常简单:

update tbl_user set name = 'update',version = 3 where id = 100 and version = 2

九、动态数据源

1. 快速了解

1. 简介

dynamic-datasource-spring-boot-starter:一个基于 Springboot 的快速继承多数据源的启动器。

github 地址:https://github.com/baomidou/dynamic-datasource-spring-boot-starter

2. 优势

关于动态数据源的切换,核心只有两种。

1)构建多套环境,优势是方便控制也容易集成一些简单的分布式事务,缺点是非动态同时代码量较多,配置难度大。

2)基于spring提供原生的 AbstractRoutingDataSource ,参考一些文档自己实现切换。

如果你的数据源较少,场景不复杂,选择以上任意一种都可以。如果你需要更多特性,请尝试本动态数据源。

1)数据源分组,适用于多种场景、纯粹多库、读写分离、一主多从、混合模式。2)简单集成 Druid 数据源监控多数据源,简单集成 Mybatis-Plus 简化单表,简单集成 P6sy 格式化 sql,简单集成 Jndi 数据源。3)简化 Druid 和 HikariCp 配置,提供全局参数配置。4)提供自定义数据源来源(默认使用 yml 或 properties 配置)。5)项目启动后能动态增减数据源。6)使用 spel 动态参数解析数据源,如从 session,header 和参数中获取数据源。(多租户架构神器)7)多层数据源嵌套切换。(一个业务ServiceA调用ServiceB,ServiceB调用ServiceC,每个Service都是不同的数据源)8)使用正则匹配或spel表达式来切换数据源(实验性功能)。

3. 劣势

不能使用多数据源事务(同一个数据源下能使用事务),网上其他方案也都不能提供。

如果你需要使用到分布式事务,那么你的架构应该到了微服务化的时候了。

4. 约定

1)本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何 CRUD。

2)配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。

3)切换数据源即可是组名,也可是具体数据源名称,切换时默认采用负载均衡机制切换。

4)默认的数据源名称为 master ,你可以通过spring.datasource.dynamic.primary修改。

5)方法上的注解优先于类上注解。

5. 建议

强烈建议在 主从模式 下遵循普遍的规则,以便他人能更轻易理解你的代码。

主数据库 建议 只执行 INSERT UPDATE DELETE 操作。

从数据库 建议 只执行 SELECT 操作。

2. 具体使用

1)引入依赖

<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>${version}</version>
</dependency>

2)配置数据源

spring:datasource:dynamic:primary: master #设置默认的数据源或者数据源组,默认值即为masterdatasource:master:username: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://xx.xx.xx.xx:3306/dynamicslave_1:username: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://xx.xx.xx.xx:3307/dynamicslave_2:username: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://xx.xx.xx.xx:3308/dynamic#......省略#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
# 多主多从   纯粹多库(记得设置primary)  混合配置
spring:       spring:                spring:datasource:   datasource:            datasource:dynamic:      dynamic:               dynamic:datasource:   datasource:            datasource:master_1:     mysql:                  master:master_2:     oracle:                 slave_1:slave_1:      sqlserver:              slave_2:slave_2:      postgresql:             oracle_1:slave_3:      h2:                     oracle_2:

3)使用 @DS 切换数据源。

@DS 可以注解在方法上和类上,同时存在方法注解优先于类上注解

【注意】注解在 service 实现或 mapper 接口方法上,但强烈不建议同时在 service和 mapper 注解。 (可能会有问题)

注解 结果
没有@DS 默认数据源
@DS(“dsName”) dsName可以为组名也可以为具体某个库的名称
@Service
@DS("slave")
public class UserServiceImpl implements UserService {@Autowiredprivate JdbcTemplate jdbcTemplate;public List<Map<String, Object>> selectAll() {return  jdbcTemplate.queryForList("select * from user");}@Override@DS("slave_1")public List<Map<String, Object>> selectByCondition() {return  jdbcTemplate.queryForList("select * from user where age >10");}
}

在 mybatis 环境下也可注解在 mapper 接口层。

@DS("slave")
public interface UserMapper {@Insert("INSERT INTO user (name,age) values (#{name},#{age})")boolean addUser(@Param("name") String name, @Param("age") Integer age);@Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}")boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age);@Delete("DELETE from user where id =#{id}")boolean deleteUser(@Param("id") Integer id);@Select("SELECT * FROM user")@DS("slave_1")List<User> selectAll();
}

另可参考更多更细致的内容:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/pages

十、多租户 SQL 解析器

这里配合 分页拦截器 使用, spring boot 例子配置如下。

@Bean
public PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();paginationInterceptor.setLocalPage(true);// 开启 PageHelper 的支持/** 【测试多租户】 SQL 解析处理拦截器<br>* 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )<br>*/List<ISqlParser> sqlParserList = new ArrayList<>();TenantSqlParser tenantSqlParser = new TenantSqlParser();tenantSqlParser.setTenantHandler(new TenantHandler() {@Overridepublic Expression getTenantId() {return new LongValue(1L);}@Overridepublic String getTenantIdColumn() {return "tenant_id";}@Overridepublic boolean doTableFilter(String tableName) {// 这里可以判断是否过滤表/*if ("user".equals(tableName)) {return true;}*/return false;}});sqlParserList.add(tenantSqlParser);paginationInterceptor.setSqlParserList(sqlParserList);paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {@Overridepublic boolean doFilter(MetaObject metaObject) {MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);// 过滤自定义查询此时无租户信息约束【 麻花藤 】出现if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {return true;}return false;}});return paginationInterceptor;
}

十一、动态表明 SQL 解析

实现 ITableNameHandler 接口注入到 DynamicTableNameParser 处理器链中,将动态表名解析器注入到 MP 解析链。

【注意】原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换。例如:真实表名为 user 设定为 mp_dt_user 处理器替换为 user_2019 等。

十二、MyBatisX 快速开发插件

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

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

项目码云地址:https://gitee.com/baomidou/MybatisX

功能:

1)Java 与 XML 调回跳转

2)Mapper 方法自动生成 MXL

e >10");
}
}


在 mybatis 环境下也可注解在 mapper 接口层。```java
@DS("slave")
public interface UserMapper {@Insert("INSERT INTO user (name,age) values (#{name},#{age})")boolean addUser(@Param("name") String name, @Param("age") Integer age);@Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}")boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age);@Delete("DELETE from user where id =#{id}")boolean deleteUser(@Param("id") Integer id);@Select("SELECT * FROM user")@DS("slave_1")List<User> selectAll();
}

另可参考更多更细致的内容:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/pages

十、多租户 SQL 解析器

这里配合 分页拦截器 使用, spring boot 例子配置如下。

@Bean
public PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();paginationInterceptor.setLocalPage(true);// 开启 PageHelper 的支持/** 【测试多租户】 SQL 解析处理拦截器<br>* 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )<br>*/List<ISqlParser> sqlParserList = new ArrayList<>();TenantSqlParser tenantSqlParser = new TenantSqlParser();tenantSqlParser.setTenantHandler(new TenantHandler() {@Overridepublic Expression getTenantId() {return new LongValue(1L);}@Overridepublic String getTenantIdColumn() {return "tenant_id";}@Overridepublic boolean doTableFilter(String tableName) {// 这里可以判断是否过滤表/*if ("user".equals(tableName)) {return true;}*/return false;}});sqlParserList.add(tenantSqlParser);paginationInterceptor.setSqlParserList(sqlParserList);paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {@Overridepublic boolean doFilter(MetaObject metaObject) {MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);// 过滤自定义查询此时无租户信息约束【 麻花藤 】出现if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {return true;}return false;}});return paginationInterceptor;
}

十一、动态表明 SQL 解析

实现 ITableNameHandler 接口注入到 DynamicTableNameParser 处理器链中,将动态表名解析器注入到 MP 解析链。

【注意】原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换。例如:真实表名为 user 设定为 mp_dt_user 处理器替换为 user_2019 等。

十二、MyBatisX 快速开发插件

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

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

项目码云地址:https://gitee.com/baomidou/MybatisX

功能:

1)Java 与 XML 调回跳转

2)Mapper 方法自动生成 MXL

MyBatisPlus 学习文档 2021-9-25相关推荐

  1. FreeMarker中文帮助手册API文档,基础入门学习文档

    FreeMarker中文帮助手册API文档,基础入门学习文档 分类: 编程技术 发布: bywei 浏览: 7 日期: 2011年5月28日 分享到: QQ空间 新浪微博 腾讯微博 人人网 什么是Fr ...

  2. Ext JS 6学习文档-第3章-基础组件

    Ext JS 6学习文档-第3章-基础组件 基础组件 在本章中,你将学习到一些 Ext JS 基础组件的使用.同时我们会结合所学创建一个小项目.这一章我们将学习以下知识点: 熟悉基本的组件 – 按钮, ...

  3. Ext JS 6学习文档-第6章-高级组件

    Ext JS 6学习文档-第6章-高级组件 高级组件 本章涵盖了高级组件,比如 tree 和 data view.它将为读者呈现一个示例项目为 图片浏览器,它使用 tree 和 data view 组 ...

  4. C和C++编程和学习文档

     C和C++编程和学习文档 C和C++编程和学习文档   1 :指针变量名称以p为首字符,这是程序员通常在定义指针时的一个习惯 2 :har * p;    (int *)p 把p强制转换为int型  ...

  5. 安卓学习文档收集汇总

    安卓学习文档收集汇总 https://www.jianshu.com/p/86aed183ce6c?utm_campaign=maleskine&utm_content=note&ut ...

  6. 2w多字总结的VUE学习文档

    VUE学习文档 文章目录 VUE学习文档 回顾: 总结 0 目标 1.前言 2.认识Vue 3.快速入门 3.1.创建工程 3.2.安装vue 3.2.1.下载安装 3.2.2.使用CDN 3.3.v ...

  7. 100个Java项目解析,带源代码和学习文档!

    前言 你是否正在寻找带有源代码的Java项目的免费集合?你的搜索到这里结束,我为你提供了近100多个Java项目. 想要成为一个优秀的程序员写项目是绕不开的,毕竟工程学的最终目标都是要创造东西,所以, ...

  8. 全网最全Flutter的学习文档,不可转载

    Flutter学习文档-Author:Brath 由于文章内容较干,请允许Brath打一波广告- 面试记APP Github:https://github.com/Guoqing815/intervi ...

  9. ffmpeg的中文学习文档

    ffmpeg的中文学习文档 文章目录: 一.ffmpeg介绍 二.学习参考文档 1.中文 一.ffmpeg介绍 ffmpeg是视频处理工具,可选参数非常多,功能也非常的强大,可以用来开发各种视频处理工 ...

最新文章

  1. Java 最常见的 100+ 面试题和答案:面试必备
  2. Android时间选择器对话框的使用
  3. POJ 3498 March of the Penguins
  4. MIUI 10 Android 原生字体,[教程] MIUI10全局字体替换教程,了解一下?
  5. Windows导出所有计划任务方法
  6. 稳定性保障6步走:高可用系统大促作战指南!
  7. js中的Promise简单总结(ES6)
  8. 从程序员角度--解决ipad白苹果问题的最佳办法---纠正网上的错误言论
  9. jq 实现头像(气泡式浮动)
  10. 解决只有单引号的Json格式转换成bean问题
  11. linux脚本中sed -i,Linux Shell 脚本之sed命令详解
  12. python中聚类和分类的区别_聚类与分类有什么区别?
  13. 流量造假:“蔡徐坤微博转发过亿”幕后推手一审获刑五年
  14. 领扣LintCode算法问题答案-983. 棒球游戏
  15. Unity 使用Mirror制作联机游戏
  16. python爬取拉钩网招聘信息分析
  17. vue中浏览器全屏和退出全屏
  18. Python 计算两个连通子图距离_复杂网络分析之python利器NetworkX
  19. Python Network(一)基础入门(节点和边基本概念,网络统计量)
  20. 面试了一位 46 岁的程序员,思绪万千,最后结局竟让我大惊失色!

热门文章

  1. 用GVIM替代Val(a)IDE!
  2. leetcode-Valid Parenthese
  3. css样式大全(整理版)
  4. C语言返回值return简单解释
  5. Go字节数组与字符串相互转换
  6. Flash卡的技术入门
  7. WinRAR v5.11
  8. HDU-5575-Discover Water Tank
  9. Mybatis Plus SQL语句中 字段为数据库关键字的解决方案
  10. 生物信息学练习题-三阳