一起来学SpringBoot(十五)MybatisPlus的整合

MyBatis-Plus(简称 MP)是一个MyBatis的增强工具 ,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本篇文章介绍的是与springboot的整合。

在这里插入图片描述

特性

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

  • 损耗小:启动即会自动注入基本 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 注入攻击

    不要忘记依赖

    <!-- mp -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>
    

代码生成器

不多说这个非常爽,运行下能生成基本结构。我这里稍微加了点注释,写好表名直接运行就ok了

package com.maoxs.generator;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

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

/**
 * @author fulin
 * @since 2018-09-12
 */
public class MysqlGenerator {

    public static void Generator(String[] tableName) {

        String projectPath = System.getProperty("user.dir");

        //============================== 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(projectPath + "/springboot-mybatisplus/src/main/java")
                .setActiveRecord(true)// 是否支持 AR
                .setAuthor("fulin") //设置作者名字
                .setFileOverride(true) //文件覆盖(全新文件)
                .setIdType(IdType.AUTO)//主键策略
                .setBaseResultMap(true) //SQL 映射文件
                .setBaseColumnList(true)//SQL 片段
                .setOpen(false);
        //============================== 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MARIADB)
                .setUrl("jdbc:mariadb://localhost:3306/test")
                .setDriverName("org.mariadb.jdbc.Driver")
                .setUsername("root")
                //.setSchemaName("public")
                .setPassword("123456");
        //==============================包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.maoxs")//配置父包路径
                .setModuleName("base")//配置业务包路径
                .setMapper("mapper")
                .setEntity("entity")
                .setService("service")
                .setController("controller");
        //.setServiceImpl("service.impl"); 会自动生成 impl,可以不设定
        //============================== 自定义配置
        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 + "/springboot-mybatisplus/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        //============================== 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel)//设置命名规则  underline_to_camel 底线变驼峰
                .setColumnNaming(NamingStrategy.underline_to_camel)//设置设置列命名  underline_to_camel 底线变驼峰
                //.setSuperEntityClass("com.maoxs.pojo")//设置继承类
                //.setSuperControllerClass("com.maoxs.controller")//设置继承类
                .setEntityLombokModel(true)//是否加入lombok
                .setInclude(tableName)//设置表名
                //.setSuperEntityColumns("id") //设置超级超级列
                .setControllerMappingHyphenStyle(true)//设置controller映射联字符
                .setTablePrefix(pc.getModuleName() + "_");//表的前缀
        //============================== 生成配置
        AutoGenerator mpg = new AutoGenerator();
        mpg.setCfg(cfg)
                .setTemplate(new TemplateConfig().setXml(null))
                .setGlobalConfig(gc)
                .setDataSource(dsc)
                .setPackageInfo(pc)
                .setStrategy(strategy)
                // 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
                .setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
    public static void main(String[] args) {
        Generator(new String[]{"x", "xx"});
    }
}

这只是生成代码的工作,然后呢为了确保你生成的Mapper接口可以扫描到,还需要这样的一个配置

package com.maoxs.conf;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author fulin
 * @since 2018-09-10
 */
@Configuration
@MapperScan("com.maoxs.base.mapper")
public class MybatisPlusConfig {

}

@MapperScan 是用来扫描你mapper所在的包,多个包的话英文逗号隔开即可。剩下的大多数MybatisPlus的配置基本都是在这里配置。

crud

这边就阐述什么表名什么的,可以自己尝试创建,这里只提到怎么使用,重要的配置我会贴出.

这边呢就贴出一个简单的增删改查的测试类。

package com.maoxs.crud;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.maoxs.SpringbootMybatisplusApplicationTests;
import com.maoxs.base.entity.User;
import com.maoxs.base.mapper.UserMapper;
import org.junit.Assert;
import org.junit.Test;

import javax.annotation.Resource;

public class CrudTest extends SpringbootMybatisplusApplicationTests {
    @Resource
    private UserMapper userMapper;

    @Test
    public void Insert() {
        User user = new User();
        user.setAge(17);
        user.setName("我是付林");
        int num = userMapper.insert(user);
        Assert.assertTrue(num > 0);
        System.out.println("插入成功Id为" + user.getId());
    }

    @Test
    public void Delete() {
        Assert.assertTrue(userMapper.deleteById(14L) > 0);
        Assert.assertTrue(userMapper.delete(new QueryWrapper<User>().lambda().eq(User::getName, "我是付林")) > 0);
    }

    @Test
    public void Update() {
        Assert.assertTrue(userMapper.updateById(new User().setId(1L).setAge(100).setName("我是付林")) > 0);
        Assert.assertTrue(userMapper.update(new User(),
                new UpdateWrapper<User>().lambda().set(User::getAge, 2).eq(User::getId, 2)
        ) > 0);
    }

    @Test
    public void Select() {
        Assert.assertEquals("cfulin", userMapper.selectById(3L).getName());
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getId, 2));
        Assert.assertEquals("付林1", user.getName());
        Assert.assertTrue(2 == user.getAge());
    }
}

这里呢说明一下Wrapper 这可是个好玩的东西,相当于QBC那样的动态sql拼接,这里mybatisplus是3.0以上的版本,算是一个大版本,这里呢条件的拼接呢都使用了 lambda 方式。如果不想使用,比如new QueryWrapper().lambda().eq(User::getId, 2) 可以写为new QueryWrapper().eq(“id”, 2)

AR(ActiveRecord)

在web后台开发领域,经常会遇到应用程序操作数据库的场景,目前市面上存在的数据库包括mysql、postgresql、oracle、sqlite等,为了屏蔽不同数据库的差异,产生了ORM(Object-Relational-Mapping),在实现层面,又分为DataMapper和ActiveRecord两种。mybatis对其也有支持

在这里插入图片描述

package com.maoxs.crud;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.maoxs.SpringbootMybatisplusApplicationTests;
import com.maoxs.base.entity.User;
import org.junit.Test;

/**
 * @program: plus
 * @description: Ar测试
 * @author: fulin
 * @create: 2018-09-24 12:13
 **/
public class ArTest extends SpringbootMybatisplusApplicationTests {
    @Test
    public void ARInsert() {
        User user = new User();
        user.setName("fulinlin");
        user.setAge(20);
        //字段不为空插入
        user.insert();
        //ID为空插入,否则为更新
        user.insertOrUpdate();
    }

    @Test
    public void ARUpdate() {
        User user = new User();
        user.setName("快乐猫");
        user.setAge(24);
        user.setId(3L);
        //ID 修改
        user.updateById();
        //条件修改
        user.update(new UpdateWrapper<User>().lambda().eq(User::getId, "5"));
    }


    @Test
    public void ARSelect() {
        User user = new User();
        user.setId(4L);
        //setId
        user.selectById();
        //直接键入 Id
        user.selectById(24);
        //条件
        user.selectCount(new QueryWrapper<User>().lambda().eq(User::getId, 0));
        //查询所有
        user.selectAll();
        //查询总记录数
        user.selectList(new QueryWrapper<User>().lambda().eq(User::getName, "fulinlin"));
        //查询一个
        user.selectOne(new QueryWrapper());
        //分页
        user.selectPage(new Page<>(1, 2), new QueryWrapper<>());
    }


    @Test
    public void ARDelete() {
        //删除不存在的数据 在逻辑上也是成功的,返回结果 true
        User user = new User();
        user.setId(4L);
        user.deleteById();
        user.deleteById(31);
        //条件删除
        user.delete(new QueryWrapper<User>().lambda().eq(User::getId, 3L));
    }
}

分页

MybatisPlus也提供了一个分页插件,使用起来及其方便,这里说明两种情况下的分页,一种是自带的,一种是自定义的。不过呢做这些之前你要先引入这个配置。

/**
* 分页插件
* @return
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}

普通分页

普通分页需要传递一个page 的对象,里面说明你当前页和分页的大小,用起来也是非常的舒服。

@Test
public void Page() {
    Page<User> page = new Page<>(1, 5);
    IPage<User> userIPage = userMapper.selectPage(page, null);
    System.out.println(Collections.unmodifiableCollection(userIPage.getRecords()));
}

自定义分页

如果在某些情况下需要自己定义分页,那怎么办呢

首先呢在mapper接口中定义一个方法


/**
* 3.x 的 page 可以进行取值,多个入参记得加上注解
* 自定义 page 类必须放在入参第一位
* 返回值可以用 IPage<T> 接收 也可以使用入参的 MyPage<T> 接收
* todo 目前使用注解会报错,写在 xml 里就没事
* @param myPage 自定义 page
* @return 分页数据
*/
MyPage<User> mySelectPage(@Param("pg") MyPage<User> myPage);

然后呢你需要在xml中写一个select做绑定

<select id="mySelectPage" resultType="com.maoxs.base.entity.User">
	select *  from user where name like '%' #{pg.selectStr} '%'and age > #{pg.selectInt}
</select>

这里呢,把用到的MyPage贴出

package com.maoxs.model;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * @author fulin
 * @since 2018-09-10
 */
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class MyPage<T> extends Page<T> {
    private static final long serialVersionUID = 5194933845448697148L;

    private Integer selectInt;
    private String selectStr;

    public MyPage(long current, long size) {
        super(current, size);
    }
}

怎么使用呢?贴一个测试类

 @Test
 public void MyPage() {
     MyPage<User> myPage = new MyPage<User>(1, 5).setSelectInt(10).setSelectStr("fulin");
     MyPage<User> userMyPage = userMapper.mySelectPage(myPage);
     System.out.println(Collections.unmodifiableCollection(userMyPage.getRecords()));
 }

逻辑删除

有时候在做业务的时候,由于涉及到数据的安全性和方便以后的维护,删除一般都是假删除,当然了mybatisplus也提供的非常简便的做法,如果想使用mybatisplus的逻辑删除,必须要注入LogicSqlInjector 这个类

/**
* 逻辑删除
* @return
*/
@Bean
public ISqlInjector sqlInjector() {
     return new LogicSqlInjector();
}

然后呢需要在实体类的属性中加入@TableLogic 注解

 @TableLogic
 private Integer isDelete;

这里有两种配置,一中是局部一中是全局.

局部配置呢就在注解中设置

@TableLogic(value = "0",delval = "1")
private Integer isDelete;

全局呢就是在yml中配置

mybatis-plus:
  global-config:
    banner: false
    db-config:
      logic-delete-value: 1 #删除后的状态 默认值1
      logic-not-delete-value: 0 #逻辑前的值 默认值0
  mapper-locations: classpath:/mapper/base/*Mapper.xml

然后呢你进行删除的时候,这个加入注解的字段就会为你设置的制定逻辑删除的状态,并且,如果你设置了删除值,查询和修改都不会在对删除的数据进行操作。

@Test
public void testLogicDeleteBatchIds() {
	userMapper.deleteBatchIds(Arrays.asList(4, 5, 6));
	userMapper.deleteById(2L);
}

公共字段填充

对于一些公用的字段,比如插入时间,更新时间等等。这些字段,mybatisplus对其也有非常简化的做法.

想使用填充呢必须先在实体类中的字段上添加注解@TableField

//----------------- 公共字段填充注解
//      ==> DEFAULT         默认不处理
//      ==> INSERT          插入填充字段
//      ==> UPDATE          更新填充字段
//      ==> INSERT_UPDATE   插入/更新填充字段
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime createDate;

然后呢定义一个公用字段的填充类就行了,但是这个类必须实现MetaObjectHandler 接口

package com.maoxs.handler;

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

/**
 * 填充器
 * @author fulin
 * @since 2018-09-12
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        metaObject.setValue("createDate", DateTimeUtils.getCurrentLocalDateTime());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("createDate",DateTimeUtils.getCurrentLocalDateTime(), metaObject);
    }
}

然后呢你的插入和更新就可以自动填充字段了

@Test
public void updateFill() {
	userMapper.update(new User(), new UpdateWrapper<User>().lambda().set(User::getName, "付林").eq(User::getId, 4));
}
@Test
public void installFill() {
	User user = new User();
	user.setName("fulinlin");
	user.setAge(12);
	userMapper.insert(user);
}

序列

Sequence是数据库系统的特性,有的数据库实现了Sequence,有的则没有。比如Oracle、DB2、PostgreSQL数据库实现Sequence,MySQL、SQL Server、Sybase等数据库没有Sequence。那在mybatisplus里怎么设置呢?

mybatisplus 目前支持DB2KeyGenerator 、OracleKeyGenerator 、PostgreKeyGenerator 、H2KeyGenerator

想使用呢就需要把这些Generator 注入比如注入H2KeyGenerator

/**
* sequence主键,需要配置一个主键生成器
* 配合实体类注解  @KeySequence 和  @TableId  type=INPUT
* @return
*/
@Bean
public H2KeyGenerator h2KeyGenerator() {
	return new H2KeyGenerator();
}

然后呢实体类中的@TableId 的 type的IdType 必须设置为INPUT

package com.maoxs.base.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.time.LocalDateTime;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * @author fulin
 * @since 2018-09-24
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@KeySequence("SEQ_USER")
public class User extends Model<User> {
    private static final long serialVersionUID = 1L;
    //----------------- 公共字段填充注解
    //      ==> AUTO            数据库ID自增
    //      ==> NONE            默认状态默认为ID_WORKER 全局唯一ID为空自动填充
    //      ==> INPUT           用户输入配合序列
    //      ==> ID_WORKER       分布式高效有序ID生产黑科技 全局唯一ID 为空自动填充
    //      ==> ID_WORKER_STR   分布式高效字符串ID生产黑科技 全局唯一ID 为空自动填充
    //      ==> UUID            这个大家都懂 全局唯一ID 为空自动填充
    @TableId(value = "id", type = IdType.INPUT)
    private Long id;
    ....
}

测试下看看就行

@Test
public void testInsert() {
    User user = new User();
    user.setAge(18);
    user.setName("sequence");
    userMapper.insert(user);
    Long id1 = user.getId();
    System.out.println(id1);
    Assert.assertTrue("sequence start with 1000", id1 >= 1000);
    user = new User();
    user.setAge(19);
    user.setName("sequence2");
    userMapper.insert(user);
    Long id2 = user.getId();
    Assert.assertTrue("squence increment by 1", id2 - id1 == 1);
}

枚举类型

通常呢我们在做业务的时候,喜欢用枚举定义常量,这样方便我们维护和管理。比如数据库中的一些字段是以 数字来定义状态的,这时候呢不做好注释,会给交接人员和新上手项目的人带来一定的不便,这时候枚举就是解决这种问题的好办法。同样呢,mybatisplus 对枚举有很好的支持。

想使用枚举呢,必须要让mybatisplus扫描到你的枚举类

mybatis-plus:
  typeEnumsPackage: com.maoxs.enums

紧接着我们就来定义一个枚举

package com.maoxs.enums;
import com.baomidou.mybatisplus.core.enums.IEnum;

public enum UserTypeEnum  {
    Default(1, "普通用户"),
    VIP(2, "会员"),
    SUPER_VIP(3, "超级会员");
    private int value;
    private String desc;

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

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

除了这种定义扫描之外呢还可以实现IEnum 接口,申明自动注入为通用枚举转换处理器

package com.maoxs.enums;

import com.baomidou.mybatisplus.core.enums.IEnum;

public enum UserTypeEnum implements IEnum<Integer> {
    Default(1, "普通用户"),
    VIP(2, "会员"),
    SUPER_VIP(3, "超级会员");
    private int value;
    private String desc;

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

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

在实体类中呢直接吧枚举当做类型即可

private UserTypeEnum UserType;

然后我们测试下

package com.maoxs.crud;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.maoxs.SpringbootMybatisplusApplicationTests;
import com.maoxs.base.entity.User;
import com.maoxs.base.mapper.UserMapper;
import com.maoxs.enums.UserTypeEnum;
import org.junit.Test;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;

/**
 * @program: plus
 * @description: 枚举测试
 * @author: fulin
 * @create: 2018-09-24 13:00
 **/
public class EnumTest extends SpringbootMybatisplusApplicationTests {
    @Resource
    private UserMapper userMapper;

    @Test
    public void insert() {
        User user = new User();
        user.setAge(11);
        user.setName("快乐鱼");
        user.setUserType(UserTypeEnum.Default);
        userMapper.insert(user);
    }

    @Test
    public void select() {
        List<User> users = userMapper.selectList(new QueryWrapper<User>().eq("user_type", UserTypeEnum.Default));
        System.out.println(users.get(0).getUserType().getValue());
    }
}

这里需要注意的是,枚举类型不能运用lambda 来片接Wrapper

自定义全局操作

有时候我们想定义自己的全局方法,当然mybatisplus也提供了扩展,首先呢,定义一个类继承AbstractMethod

package com.maoxs.methods;

import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;

/**
 * 删除全部
 * @author fulin
 * 2018/9/11 20:29.
 */
public class DeleteAll extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        /* 执行 SQL ,动态 SQL 参考类 SqlMethod */
        String sql = "delete from " + tableInfo.getTableName();
        /* mapper 接口方法名一致 */
        String method = "deleteAll";
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
    }
}

这里呢注入了一个deleteAll 的方法,用来删除表中所有的记录。然后呢,需要自定义sql注入

package com.maoxs.injector;

import java.util.List;

import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.samples.injector.methods.DeleteAll;

/**
 * 自定义Sql注入
 * @author fulin
 * 2018/8/11 20:23.
 */
@Component
public class MySqlInjector extends DefaultSqlInjector {

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

接下来怎么使用呢?只需要在接口中 定义

package com.maoxs.base.mapper;

import org.apache.ibatis.annotations.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.samples.injector.entity.Student;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    void deleteAll();
}

然后呢,贴个测试

@Test
public void test(){
	userMapper.deleteAll();
}

乐观锁

当你在进行数据库操作的时候,当要更新一条记录的时候,希望这条记录没有被别人更新.这种情况经常用到。

一般乐观锁实现有这几种

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

  • 更新时,带上这个version

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

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

但是在mybatisplus中乐观锁就很好处理,配置只需要两部,首先呢就是要注入OptimisticLockerInterceptor

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

注解字段中一定要加注解

@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 不能复用!!!

给个测试

/**
 * 乐观锁插件
 */
@Test
public void optimisticLockerTest() {
    User user = new User();
    user.setAge(11);
    user.setName("hahha");
    user.setVersion(1);
     //AR 模式没有乐观锁形式
	//employee.updateById();
    //MP 模式才具有乐观锁模式,version修改
    employeeMapper.updateById(employee);
}

这里说明下AR模式还没有乐观锁形势

多租户

可以这样理解,许多组织都将使用同一个应用程序;他们必须能够允许自己的用户访问应用程序,但是应用程序必须只允许每个组织自己的成员访问其组织的数据。

在查询的时候呢肯定要对其进行必要的筛选

 	 /**
     * 多租户属于 SQL 解析部分,依赖 MP 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        /*
         * 【测试多租户】 SQL 解析处理拦截器<br>
         * 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察          *  SQL )<br>
         */
        List<ISqlParser> sqlParserList = new ArrayList<>();
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId() {
                return new LongValue(1L);
            }
            @Override
            public String getTenantIdColumn() {
                return "tenant_id";//租户 ID
            }
            @Override
            public boolean doTableFilter(String tableName) {
                // 这里可以判断是否过滤表
                /*if ("user".equals(tableName)) {
                    return true;
                }*/
                return false;
            }
        });

        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
//        paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
//            @Override
//            public boolean doFilter(MetaObject metaObject) {
//                MappedStatement ms = PluginUtils.getMappedStatement(metaObject);
//                // 过滤自定义查询此时无租户信息约束【 麻花藤 】出现
//                if ("com.maoxs.base.UserMapper.selectListBySQL".equals(ms.getId())) {
//                    return true;
//                }
//                return false;
//            }
//        });
        return paginationInterceptor;
    }

相关 SQL 解析如多租户可通过 @SqlParser(filter=true) 排除 SQL 解析,注意!!全局配置 sqlParserCache 设置为 true 才生效。yml配置如下

# 开启 SQL 解析缓存注解生效
mybatis-plus:
    global-config:
        sql-parser-cache: true

性能分析插件

这个插件呢就是 性能分析拦截器,用于输出每条 SQL 语句及其执行时间,只需要配置下就可以了,不建议生产使用

@Bean
public PerformanceInterceptor performanceInterceptor(){
	//启用性能分析插件
	return new PerformanceInterceptor();
}

需要注意下PerformanceInterceptor里面有两个参数

  • 参数:maxTime SQL 执行最大时长,超过自动停止运行,有助于发现问题。
  • 参数:format SQL SQL是否格式化,默认false。

Sql执行分析插件

这玩意就是吧一些具体的数据显示出来,适合开发使用。

@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){
    SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
    List<ISqlParser> sqlParserList = new ArrayList<>();
    sqlParserList.add(new BlockAttackSqlParser());
    sqlExplainInterceptor.setSqlParserList(sqlParserList);
    return sqlExplainInterceptor;
}

本博文是基于springboot2.x 如果有什么不对的请在下方留言。

相关连接:

个人博客地址 : www.fulinlin.com

csdn博客地址:https://blog.csdn.net/qq_32867467

集合源码地址 : https://gitee.com/Maoxs/springboot-test

注:如果不对联系本宝宝及时改正~~

猜你喜欢

转载自blog.csdn.net/qq_32867467/article/details/82944674