Mybatis进阶02-MybatisPlus

Mybatis进阶02-MybatisPlus

1.修改数据

  1. 测试用例,SQL自动拼接不为null的字段。
@Test
void testUpdate() {
    User user = new User();
    user.setId(1011L);
    user.setName("");
    // 通过id更新,会自动填充更新不为null的列
    int result = userMapper.updateById(user);
    System.out.println(result);
}

2.修改数据-自动填充

  1. 阿里开发手册上说:数据库中的表需要有create_timeupdate_time字两个字段,有两种方式可以实现。

    1. 实现方式一:数据库层面。
    # 通过Navicat增加两个字段
    #创建列 create_time 类型datetime 默认值为 current_timestamp
    #创建列 update_time 类型datetime 默认值为 current_timestamp 并勾选 根据当前时间戳更新
    
    1. 实现方式二。使用MybatisPlus的MetaObjectHandler,自动补全create_timeupdate_time
  2. 实现方式二,数据库和Java实体类增加字段。

//`create_time` datetime(0) NULL DEFAULT NULL COMMENT ‘创建时间‘,
//`update_time` datetime(0) NULL DEFAULT NULL COMMENT ‘更新时间‘,

// 插入时更新
@TableField(fill = FieldFill.INSERT)
private Date createTime;
// 插入和更新时更新数据
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
  1. 实现方式二,增加Handle进行SQL拦截。
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 插入数据时,设置create_time和update_time值,值为new Date()
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert data {}", metaObject);
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    /**
     * 更新时设置update_time的值
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

3.MybatisPlus实现乐观锁

  1. 数据库和Java类增加Version字段。
//`version` int(0) NULL DEFAULT 1 COMMENT ‘乐观锁‘,

@Version
private Integer version;
  1. 配置乐观锁的拦截器。
@MapperScans({
        @MapperScan("com.my.mybatisplus.demo01.dao")
})
@Configuration
public class MybatisPlusConfig {

    /**
     * 注入乐观锁组件。本质是通过拦截器进行SQL拼接。
     * update set version=oldVersion+1 where id=? and version=oldVersion
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 乐观锁
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

4.分页查询

  1. 配置分页查询的拦截器。
@MapperScans({
        @MapperScan("com.my.mybatisplus.demo01.dao")
})
@Configuration
public class MybatisPlusConfig {

    /**
     * 注入分页插件
     * @return
     */
    //@Bean
    public MybatisPlusInterceptor interceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
  1. 代码实现。执行分页查询前,会执行count(*)操作获取总记录数。
@Test
void testPage() {
    // 分页本质,通过拦截器在SQL执行前进行 LIMIT ? OFFSET ? 的拼接
    // MybatisPlus会将数据存放在Page中
    Page<User> page = new Page<>(2, 3);

    userMapper.selectPage(page, null);
    page.getRecords().forEach(System.out::println);

    // 获得查询的数据
    page.getRecords();

    // 获取总共的数据
    page.getTotal();

    // 是否存在上一页
    page.hasPrevious();
    // 是否存在下一页
    page.hasNext();
}

5.逻辑删除

  1. 数据库和Java实体类增加字段。
//`deleted` int(0) NULL DEFAULT 0 COMMENT ‘逻辑删除‘,

@TableLogic
private Integer deleted;
  1. 新旧版本的配置。

    1. 旧版本,配置拦截器。
    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }
    
    1. 新版本直接在yaml中进行配置。
    mybatis-plus:
      global-config:
        db-config:
          logic-delete-field: deleted  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以不在实体类上加 @TableLogic)
          logic-delete-value: 1 # 逻辑已删除值(默认为 1)
          logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
    
  2. 局逻辑删除的实体字段名(since 3.3.0,在yaml配置logic-delete-field后可以不在实体类上加 @TableLogic)

  3. 逻辑删除测试代码,delete变为update操作。

// 测试 逻辑删除
@Test
void testDelete01() {
    // 逻辑删除 delete变为
    // UPDATE user SET deleted=1 WHERE id=? AND deleted=0
    int i = userMapper.deleteById(99L);

    // 查询时会添加 WHERE id=? AND deleted=0

    // 分页查询的count(*)操作
    // SELECT COUNT(*) FROM user WHERE deleted = 0
    userMapper.selectById(99L);
    System.out.println(i);
}

6.MybatisPlus中SQL性能分析插件

  1. 旧版本,配置拦截器。
@MapperScans({
        @MapperScan("com.my.mybatisplus.demo01.dao")
})
@Configuration
public class MybatisPlusConfig {
    
    @Bean
    开发和测试环境使用
    @Profile({"dev", "test"})
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        // 设置SQL最大执行时间100秒,超过100秒将不执行当前SQL,并且报错。
        performanceInterceptor.setMaxTime(100);
        // 控制台格式化输出SQL
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    }
}
  1. 新版本,官方推荐使用p6spy

    1. 导入依赖。
    <!-- MyBatis-Plus3.2.0以上版本移除了PerformanceInterceptor,可以使用第三方 -->
    <dependency>
        <groupId>p6spy</groupId>
        <artifactId>p6spy</artifactId>
        <version>3.9.1</version>
    </dependency>
    
    1. yaml改变 DataSource的配置。
    spring:
      datasource:
        #driver-class-name: com.mysql.cj.jdbc.Driver
        driver-class-name: com.p6spy.engine.spy.P6SpyDriver
        url: jdbc:p6spy:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
    
    1. resources下配置spy.properties
    #3.2.1以上使用
    modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
    #3.2.1以下使用或者不配置
    #modulelist=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,commit,resultset
    # 日期格式
    dateformat=yyyy-MM-dd HH:mm:ss
    # 实际驱动可多个
    #driverlist=org.h2.Driver
    # 是否开启慢SQL记录
    outagedetection=true
    # 慢SQL记录标准 2 秒
    outagedetectioninterval=2
    

7.MybatisPlus条件查询构造器Wrapper

  1. 查询多个数据selectList()。
// 查询多个数据 selectList()
// 不是null isNotNull
// 大于等于 ge
@Test
void test01() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // WHERE (name IS NOT NULL AND age >= ?)
    wrapper.isNotNull("name")
        .ge("age", "25");
    // 查询 多个数据
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
  1. 查询一条数据selectOne()。
// 查询一个,如果结果有多个数据报错
// eq
@Test
void test02() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // WHERE (name = ?)
    wrapper.eq("name", "tom");
    // 查询 多个数据
    User user = userMapper.selectOne(wrapper);
    System.out.println(user);
}
  1. count(*)查询。
// count(*) between
@Test
void test03() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // (age BETWEEN ? AND ?)
    wrapper.between("age", 20, 25);
    // 查询 多个数据
    Integer count = userMapper.selectCount(wrapper);
    System.out.println(count);
}
  1. like模糊查询。
// maps
// like
@Test
void test04() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // wrapper.like("name", "t"); ==>> WHERE (name LIKE ?) %t%
    // wrapper.like(true, "name", "t"); ==>> WHERE (name LIKE ?) %t%
    // wrapper.like(false, "name", "t"); ==>> 条件失效
    //wrapper.like(true, "name", "t");

    // wrapper.likeRight("name", "t"); ==>> WHERE (name LIKE ?) t%(String)
    wrapper.likeRight(false, "name", "t");


    // 查询 多个数据 将查询的多个数据封装为Map
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}
  1. selectObjs(),将查询结果封装为List(Object),inSql()进行内查询。
// selectObjs()
// inSql 内查询
@Test
void test05() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // 内查询 (name IN (select name from user where id in (1,2)))
    wrapper.inSql("name", "select name from user where id in (1,2)");

    // 查询 多个数据
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}
  1. allEq()使用Map作为查询参数。
// allEq()
@Test
void test06() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    Map<String, Object> map = new HashMap<>();
    map.put("name", "tom");
    map.put("age", 12);
    map.put("id", null);

    // wrapper.allEq(map); WHERE (name = ? AND id IS NULL AND age = ?)
    // 默认为true,map中value为null,调用 id IS NULL。
    // false,则忽略map中为null的value
    //wrapper.allEq(map, false); WHERE (name = ? AND age = ?)

    wrapper.allEq((k, v) -> {
        // 返回true时,将才使用map的key和value拼接SQL
        // 相当与 WHERE (name = ?) tom(String)
        return k.equals("name") ? v.equals("tom") : false;
    },map);
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
  1. func()。
// func 方法(主要方便在出现if...else下调用不同方法能不断链)
//例: func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
@Test
void test07() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    int i = 10;
    wrapper.func(w -> {
        if (i > 10) {
            w.orderByAsc("id");
        }else {
            w.orderByAsc("name");
        }
    });
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
  1. or()。
@Test
void test08() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();

    // 默认使用and连接
    // WHERE (name = ? OR name = ? AND name = ?)
    //wrapper.eq("name", "tom")
    //.or().eq("name", "bob")
    //.eq("name", "alice");

    // WHERE (name = ? OR (id = ? AND id = ?))
    // or中拼接参数
    wrapper.eq("name", "tom")
        .or(w -> {
            w.eq("id", 1).eq("id", 2);
        });
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
  1. LambdaQueryWrapper。
@Test
void test08() {
    LambdaQueryWrapper<User> w = new LambdaQueryWrapper<>();
    w.eq(User::getAge, "10");
    List<User> users = userMapper.selectList(w);
    users.forEach(System.out::println);
}
  1. 其他查询。
// ne <> 不等于
// ge 大于等于 gt 大于
// le 小于等于 lt 小于
// notBetween("age", 18, 30) --->age not between 18 and 30
// in("age", 1, 2, 3)--->age in (1,2,3)
// notIn("age", 1, 2, 3)--->age not in (1,2,3)
// groupBy("id", "name")--->group by id,name
// orderByAsc("id", "name")--->order by id ASC,name ASC 升序
// orderByDesc("id", "name")--->order by id DESC,name DESC 降序
// having("sum(age) > 10")--->having sum(age) > 10
// having("sum(age) > {0}", 11)--->having sum(age) > 11

8.代码生成器

  1. 配置依赖。
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>

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

<!-- freemarker模板需要的依赖 -->
<!--<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.28</version>
</dependency>
<dependency>
    <groupId>com.ibeetl</groupId>
    <artifactId>beetl</artifactId>
    <version>3.0.7.RELEASE</version>
</dependency>-->
  1. 代码生成。
public class CodeGenerator {

    public static void main(String[] args) {
        AutoGenerator ag = new AutoGenerator();

        // 1 全局配置
        GlobalConfig gc = new GlobalConfig();
        // 获取用户工程目录
        String dir = System.getProperty("user.dir");
        System.out.println(dir);
        gc.setOutputDir(dir + "\\src\\main\\java");
        gc.setAuthor("wwt");
        // 生成代码后是否自动打开文件管理器
        gc.setOpen(false);
        // 文件存在是否覆盖
        gc.setFileOverride(false);
        // 去掉Server接口的 I。如默认为IUserService,去掉后为UserService
        //gc.setServiceName("%sServer");
        //gc.setServiceImplName("");
        gc.setIdType(IdType.NONE);
        // 只使用 java.util.date 代替
        gc.setDateType(DateType.ONLY_DATE);
        // 是否生成swagger注释,通过数据库字段注释,生成实体类swagger的注释
        gc.setSwagger2(true);
        ag.setGlobalConfig(gc);

        // 2 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=Asia/Shanghai");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        ag.setDataSource(dsc);

        // 3 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("demo03");
        pc.setParent("com.my.mybatisplus");
        //pc.setEntity("entity");
        //pc.setMapper("mapper");
        //pc.setService("service");
        //pc.setServiceImpl("service/impl");
        //pc.setController("controller");

        pc.setXml(null);
        ag.setPackageInfo(pc);

        // 4 策略配置
        StrategyConfig strategy = new StrategyConfig();
        // 要生成代码的表
        strategy.setInclude("user");
        // 数据库表映射到实体的命名策略 下划线转驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 数据库表字段映射到实体的命名策略 下划线转驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        // lombok开启注解
        strategy.setEntityLombokModel(true);
        // restful 形式的url
        //strategy.setRestControllerStyle(true);

        // 逻辑删除
        strategy.setLogicDeleteFieldName("deleted");

        // 自动填充
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
        strategy.setTableFillList(Arrays.asList(createTime, updateTime));

        // 公共父类
        //strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
        // 写于父类中的公共字段
        //strategy.setSuperEntityColumns("id");

        // 乐观锁
        strategy.setVersionFieldName("version");
        // controller相关
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);
        ag.setStrategy(strategy);

        // 将mapper.xml生成到resource下
        String templatePath = "/templates/mapper.xml.vm";
        InjectionConfig cfg = new InjectionConfig(){
            @Override
            public void initMap() {

            }
        };

        List<FileOutConfig> list = new ArrayList<>();
        list.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {

                return dir + "/demo03/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(list);
        ag.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        templateConfig.setXml(null);
        ag.setTemplate(templateConfig);

        ag.execute();
    }
}

Mybatis进阶02-MybatisPlus

上一篇:高格-统计核销出货单明细【16】


下一篇:数字电视的传输流及复用技术