当前位置: 首页 > article >正文

mybatis-puls快速入门

1.概述

在真实项目开发中我们的服务模块,一般都要进行数据库操作,并且每个domain都有crud,需多次写重复代码。我们使用MybatisPlus,就不用写重复代码,并且还有模板的功能,可以一键生成daomin,query,mapper接口,mapper.xml,service,controller,非常好用。
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。他有如下特点:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 注入攻击

也就是说,我们不仅可以使用MybatisPlus来代替mybatis,还可以使用MybatisPlus给我们生产基础的CRUD代码,下面是一个使用示意图

1.1导入依赖

<!--mybatisplus持久层依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        
        <!-- mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency><!--mysql依赖-->

        <!--连接池依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>

1.2编写yml配置文件

spring:
  # mybaits-plus数据源配置
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://8.137.78.153:3306/fccar-driver?serverTimezone=Asia/Shanghai&characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource # 配置阿里的连接池
    druid: # Druid 【监控】相关的全局配置
      # 配置初始化大小、最小、最大
      initial-size: 5
      minIdle: 10
      max-active: 20
      # 配置获取连接等待超时的时间(单位:毫秒)
      max-wait: 60000
      ########### 启用内置过滤器(第一个 stat必须,否则监控不到SQL)##########
      filters: stat,wall,log4j2
      web-stat-filter:
        enabled: true
      stat-view-servlet:
        enabled: true
        allow: # 设置白名单,不填则允许所有访问
        url-pattern: /druid/*
        login-username: fccar # 控制台管理用户名和密码
        login-password: fccar
      filter:
        stat:
          enabled: true
          log-slow-sql: true # 慢 SQL 记录
          slow-sql-millis: 2000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
# mybatis-plus配置
mybatis-plus:
  type-aliases-package: cn.lgc.domain,cn.lgc.query #别名包扫描
  mapper-locations: classpath:cn/lgc/mapper/*Mapper.xml #SQL映射文件扫描
  global-config:
    db-config: #设置逻辑删除,通过把delete字段改成0而不是直接删除数据
      logic-not-delete-value: 0
      logic-delete-value: 1
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰命名转换法
    cache-enabled: false #禁用缓存
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 配置日志,在控制台输出SQL

1.3mybatis配置类

@Configuration
//扫描Mybatis的mapper映射器
@MapperScan("cn.itsource.mapper")
public class MybatisPlusConfig {

    //分页插件配置对象,Mybatis-plus需要此配置对象
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
    
    //乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor(){
        return new OptimisticLockerInterceptor();
    }
}

常用注解

@TableName("t_teacher")

指定当前类的表名

@TableId

指定当前字段属性,为表主键id,主键增长方式为自动增长

@TableId(type = IdType.AUTO)
private Long id;

IdType属性

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

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

@TableField

数据库表字段标识

@TableField("name")
private String name;

TableField属性

    /**
     * 数据库字段值
     * <p>
     * 不需要配置该值的情况:
     * <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 true 时,
     * (mp下默认是true,mybatis默认是false), 数据库字段值.replace("_","").toUpperCase() == 实体属性名.toUpperCase() </li>
     * <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 false 时,
     * 数据库字段值.toUpperCase() == 实体属性名.toUpperCase() </li>
     */
    String value() default "";

    /**
     * 是否为数据库表字段
     * <p>
     * 默认 true 存在,false 不存在
     */
    boolean exist() default true;

    /**
     * 字段 where 实体查询比较条件
     * <p>
     * 默认 {@link SqlCondition#EQUAL}
     */
    String condition() default "";

    /**
     * 字段 update set 部分注入, 该注解优于 el 注解使用
     * <p>
     * 例1:@TableField(.. , update="%s+1") 其中 %s 会填充为字段
     * 输出 SQL 为:update 表 set 字段=字段+1 where ...
     * <p>
     * 例2:@TableField(.. , update="now()") 使用数据库时间
     * 输出 SQL 为:update 表 set 字段=now() where ...
     */
    String update() default "";

    /**
     * 字段验证策略之 insert: 当insert操作时,该字段拼接insert语句时的策略
     * <p>
     * IGNORED: 直接拼接 insert into table_a(column) values (#{columnProperty});
     * NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
     * NOT_EMPTY: insert into table_a(<if test="columnProperty != null and columnProperty!=''">column</if>) values (<if test="columnProperty != null and columnProperty!=''">#{columnProperty}</if>)
     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
     *
     * @since 3.1.2
     */
    FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;

    /**
     * 字段验证策略之 update: 当更新操作时,该字段拼接set语句时的策略
     * <p>
     * IGNORED: 直接拼接 update table_a set column=#{columnProperty}, 属性为null/空string都会被set进去
     * NOT_NULL: update table_a set <if test="columnProperty != null">column=#{columnProperty}</if>
     * NOT_EMPTY: update table_a set <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
     *
     * @since 3.1.2
     */
    FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;

    /**
     * 字段验证策略之 where: 表示该字段在拼接where条件时的策略
     * <p>
     * IGNORED: 直接拼接 column=#{columnProperty}
     * NOT_NULL: <if test="columnProperty != null">column=#{columnProperty}</if>
     * NOT_EMPTY: <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
     *
     * @since 3.1.2
     */
    FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;

    /**
     * 字段自动填充策略
     * <p>
     * 在对应模式下将会忽略 insertStrategy 或 updateStrategy 的配置,等于断言该字段必有值
     */
    FieldFill fill() default FieldFill.DEFAULT;

    /**
     * 是否进行 select 查询
     * <p>
     * 大字段可设置为 false 不加入 select 查询范围
     */
    boolean select() default true;

    /**
     * 是否保持使用全局的 columnFormat 的值
     * <p>
     * 只生效于 既设置了全局的 columnFormat 也设置了上面 {@link #value()} 的值
     * 如果是 false , 全局的 columnFormat 不生效
     *
     * @since 3.1.1
     */
    boolean keepGlobalFormat() default false;

    /**
     * {@link ResultMapping#property} and {@link ParameterMapping#property}
     *
     * @since 3.4.4
     */
    String property() default "";

    /**
     * JDBC类型 (该默认值不代表会按照该值生效),
     * 只生效于 mp 自动注入的 method,
     * 建议配合 {@link TableName#autoResultMap()} 一起使用
     * <p>
     * {@link ResultMapping#jdbcType} and {@link ParameterMapping#jdbcType}
     *
     * @since 3.1.2
     */
    JdbcType jdbcType() default JdbcType.UNDEFINED;

    /**
     * 类型处理器 (该默认值不代表会按照该值生效),
     * 只生效于 mp 自动注入的 method,
     * 建议配合 {@link TableName#autoResultMap()} 一起使用
     * <p>
     * {@link ResultMapping#typeHandler} and {@link ParameterMapping#typeHandler}
     *
     * @since 3.1.2
     */
    Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;

    /**
     * 只在使用了 {@link #typeHandler()} 时判断是否辅助追加 javaType
     * <p>
     * 一般情况下不推荐使用
     * {@link ParameterMapping#javaType}
     *
     * @since 3.4.0 @2020-07-23
     */
    boolean javaType() default false;

    /**
     * 指定小数点后保留的位数,
     * 只生效于 mp 自动注入的 method,
     * 建议配合 {@link TableName#autoResultMap()} 一起使用
     * <p>
     * {@link ParameterMapping#numericScale}
     *
     * @since 3.1.2
     */
    String numericScale() default "";

基本方法

分页

1.分页代码

     QueryWrapper<Student> wrapper = new QueryWrapper<>();
        Page<Student> page = new Page<>(2,7);
        studentMapper.selectPage(page, wrapper);
        /*总页数*/
        long total = page.getTotal();
        /*当前页内容*/
        List<Student> records = page.getRecords();
        System.out.println("总条数"+total);
        System.out.println("当前页内容"+records);
        System.out.println("当前页码"+page.getCurrent());
        System.out.println("每页显示数"+page.getSize());
        System.out.println("总页数"+page.getPages());

2.分页拦截器

@Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //分页拦截器,添加数据库类型
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        //分页合理化
        paginationInnerInterceptor.setOverflow(true);
        //mybatis拦截器添加分页拦截器
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }

代码生成器

使用velocity模板

依赖

 <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        <!--代码生成模式插件  3.0.3以后需要手动设置依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>
        <!-- 模板引擎 依赖,MyBatis-Plus 支持 Velocity-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.1</version>
        </dependency>
        <!--数据库连接依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>

启动代码

package cn.lgc.utils;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ResourceBundle;

/**
 * genterator
 *
 * @author 陈立高
 * @date 2024/01/26
 */
public class Genterator {

    public static void main(String[] args) {
        //读取配置文件
        ResourceBundle rb = ResourceBundle.getBundle("config-driver");
        //业务代码输出路径
        String outputDir = rb.getString("OutputDir");
        //sql映射文件输出路径
        String outputDirXml = rb.getString("OutputDirXml");
        //作者
        String author = rb.getString("author");

        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        // 代码输出位置
        gc.setOutputDir(outputDir);
        // 作者
        gc.setAuthor(author);
        // 打开代码生成目录
        gc.setOpen(false);
        //生成 resultMap
        gc.setBaseResultMap(true);
        //生成查询列明
        gc.setBaseColumnList(true);
        //日期类型
        gc.setDateType(DateType.ONLY_DATE);
        //ID使用雪花算法
        gc.setIdType(IdType.ASSIGN_ID);

        //添加接口文档注解
        gc.setSwagger2(true);

        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        // 数据库类型
        dsc.setDbType(DbType.MYSQL);
        dsc.setTypeConvert(new MySqlTypeConvert());
        // 连接属性
        dsc.setDriverName(rb.getString("jdbc.driver"));
        dsc.setUsername(rb.getString("jdbc.user"));
        dsc.setPassword(rb.getString("jdbc.pwd"));
        dsc.setUrl(rb.getString("jdbc.url"));
        mpg.setDataSource(dsc);

        // 表策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setTablePrefix(new String[] { "t_" });// 此处可以修改为您的表前缀
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略

        strategy.setInclude(new String[]{
                "t_driver",
                "t_driver_aggrement",
                "t_driver_auth_material",
                "t_driver_material_auth_log",
                "t_driver_setting",
                "t_driver_summary",
                "t_driver_wallet",
                "t_driver_wallet_flow"
        }); // 需要生成的表

        //使用lombok
        strategy.setEntityLombokModel(true);
        strategy.setEntitySerialVersionUID(true);
        //乐观锁字段
        strategy.setVersionFieldName("version");
        //逻辑删除字段
        strategy.setLogicDeleteFieldName("deleted");
        //domain的父类
        //strategy.setSuperEntityClass("cn.itsource.pojo.BaseDomain");

        //controller的父类
        //strategy.setSuperControllerClass("cn.itsource.controller.BaseController");
        //生成注解
        strategy.setEntityTableFieldAnnotationEnable(true);
        strategy.setEntitySerialVersionUID(true);

        mpg.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //基础路径 cn.xxx
        pc.setParent(rb.getString("parent"));
        //controller的包
        pc.setController("controller.manager");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setEntity("pojo.domain");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                this.setMap(new HashMap<String, Object>());
            }
        };

        //文件生成配置
        List<FileOutConfig> focList = new ArrayList<FileOutConfig>();

        //controller的输出配置
        focList.add(new FileOutConfig("/templates/controller.java.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //合并好的内容输出到哪儿?
                return outputDir+ "/cn/lgc/controller/manager/" + tableInfo.getEntityName() + "Controller.java";
            }
        });


        // 调整 domain 生成目录演示
        focList.add(new FileOutConfig("/templates/entity.java.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return outputDir+ "/cn/lgc/pojo/domain/" + tableInfo.getEntityName() + ".java";
            }
        });

        // 调整 xml 生成目录演示
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return outputDirXml+ "/cn/lgc/mapper/" + tableInfo.getEntityName() + "Mapper.xml";
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/templates 下面内容修改,
        // 放置自己项目的 src/main/resources/templates 目录下, 默认名称可以不配置,也可以自定义模板名称
        TemplateConfig tc = new TemplateConfig();
        tc.setService("/templates/service.java.vm");
        tc.setServiceImpl("/templates/serviceImpl.java.vm");
        tc.setMapper("/templates/mapper.java.vm");
        tc.setEntity(null);
        tc.setController(null);
        tc.setXml(null);
        // 如上任何一个模块如果设置 空 OR Null 将不生成该模块。
        mpg.setTemplate(tc);

        // 执行生成
        mpg.execute();
    }

}

配置文件

创建一个resources/config-driver.properties文件,内容如下

OutputDir=D:/devlop/code_space/it-fccar/it-fccar/it-fccar-service/it-fccar-service-driver/src/main/java
OutputDirXml=D:/devlop/code_space/it-fccar/it-fccar/it-fccar-service/it-fccar-service-driver/src/main/resources
author=clid
parent=xxx
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///fccar-drver?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
jdbc.user=root
jdbc.pwd=123456

模板

把准备好的模板 controller.java.vm ; query.java.vm 拷贝到resources /templates目录下

Controller模板
package ${package.Controller};

import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
         import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
         import org.springframework.beans.factory.annotation.Autowired;
         import org.springframework.web.bind.annotation.*;
         import io.swagger.v3.oas.annotations.Operation;
         import io.swagger.v3.oas.annotations.Parameter;
         import io.swagger.v3.oas.annotations.tags.Tag;
         import javax.validation.Valid;
         import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
         import cn.lgc.pojo.query.PageQueryWrapper;
         import cn.lgc.result.R;
         import cn.lgc.result.PageResult;

         @Tag(name = "$!{table.comment}",description = "$!{table.comment}")
         @RestController
         @RequestMapping("/manager/${table.entityPath}")
         public class ${entity}Controller{

             @Autowired
             public ${table.serviceName} ${table.entityPath}Service;

             @Operation( summary= "保存${entity}",description = "基础对象保存接口")
             @Parameter(name = "${table.entityPath}",description = "保存的对象",required = true)
             @PostMapping
             public R<Boolean> save(@RequestBody @Valid ${entity} ${table.entityPath}){
             return R.success(${table.entityPath}Service.save(${table.entityPath}));
         }

                        @Operation( summary= "修改${entity}",description = "基础对象修改接口")
                        @Parameter(name = "${table.entityPath}",description = "修改的对象",required = true)
                        @PutMapping
                        public R<Boolean> update(@RequestBody  @Valid ${entity} ${table.entityPath}){
                            return R.success(${table.entityPath}Service.updateById(${table.entityPath}));
                        }

                        //删除对象
                        @Operation( summary= "删除${entity}",description = "基础对象删除接口,采用状态删除")
                        @Parameter(name = "id",description = "删除的对象ID",required = true)
                        @DeleteMapping(value="/{id}")
                        public R<Boolean> delete(@PathVariable("id") Long id){
                            return R.success(${table.entityPath}Service.removeById(id));
                        }

                        //获取对象
                        @Operation( summary= "获取${entity}",description = "基础对象获取接口")
                        @Parameter(name = "id",description = "查询的对象ID",required = true)
                        @GetMapping(value = "/{id}")
                        public R<${entity}> get(@PathVariable("id")Long id){
                            return R.success(${table.entityPath}Service.getById(id));
                        }

                                   //获取列表:PageQueryWrapper作为通用的查询对象
                                   @Operation( summary= "查询${entity}列表",description = "基础对象列表查询,不带分页")
                                   @Parameter(name = "query",description = "查询条件",required = true)
                                   @PostMapping(value = "/list")
                                   public R<List<${entity}>> list(@RequestBody PageQueryWrapper<${entity}> query){
                                       QueryWrapper<${entity}> wrapper = new QueryWrapper<>();
        //实体类作为查询条件
        wrapper.setEntity(query.getQuery());
        return R.success(${table.entityPath}Service.list(wrapper));
    }

    //分页查询
    @Operation( summary= "查询${entity}分页列表",description = "基础对象列表查询,带分页")
    @Parameter(name = "query",description = "查询条件,需要指定分页条件",required = true)
    @PostMapping(value = "/pagelist")
    public R<PageResult<${entity}>> page(@RequestBody PageQueryWrapper<${entity}> query){
        //分页查询
        Page<${entity}> page = new Page<${entity}>(query.getPage(),query.getRows());
        QueryWrapper<${entity}> wrapper = new QueryWrapper<>();
        //实体类作为查询条件
        wrapper.setEntity(query.getQuery());
        //分页查询
        page = ${table.entityPath}Service.page(page,wrapper);
        //返回结果
        return R.success(new PageResult<${entity}>(page.getTotal(),page.getRecords()));
    }

}
entity模板
package ${package.Entity};

#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.v3.oas.annotations.media.Schema;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.fasterxml.jackson.annotation.JsonFormat;

#end

/**
 * <p>
 * $!{table.comment}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
#if(${entityLombokModel})
@Data
  #if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
  #else
@EqualsAndHashCode(callSuper = false)
  #end
@Accessors(chain = true)
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@Schema(name = "${entity}对象", description = "$!{table.comment}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end

#if(${entitySerialVersionUID})
    private static final long serialVersionUID=1L;
#end
## ----------  BEGIN 字段循环遍历  ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
  #if(${swagger2})

    @Schema(name = "${field.propertyName}", description = "${field.comment}")
  #else
    #if("$!field.comment" != "")
    /**
     * ${field.comment}
     */
  #end
#end
#if(${field.keyFlag})
## 主键
    @JsonFormat(shape = JsonFormat.Shape.STRING)
  #if(${field.keyIdentityFlag})
    @TableId(value = "${field.name}", type = IdType.AUTO)
  #elseif(!$null.isNull(${idType}) && "$!idType" != "")
    @TableId(value = "${field.name}", type = IdType.${idType})
  #elseif(${field.convert})
    @TableId("${field.name}")
  #end
## 普通字段
#elseif(${field.fill})
## -----   存在字段填充设置   -----
  #if(${field.convert})
    @TableField(value = "${field.name}", fill = FieldFill.${field.fill})
  #else
    @TableField(fill = FieldFill.${field.fill})
  #end
#elseif(${field.convert})
    @TableField("${field.name}")
#end
#if(${versionFieldName}==${field.name})
    @Version
#end
#if(${logicDeleteFieldName}==${field.name})
    @TableLogic
#end
    private ${field.propertyType} ${field.propertyName};
#end
## ----------  END 字段循环遍历  ----------
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
  #if(${field.propertyType.equals("boolean")})
    #set($getprefix="is")
  #else
    #set($getprefix="get")
  #end

    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

  #if(${entityBuilderModel})
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
  #else
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
  #end
        this.${field.propertyName} = ${field.propertyName};
  #if(${entityBuilderModel})
        return this;
  #end
    }
#end
## --foreach end---
#end
## --end of #if(!${entityLombokModel})--

#if(${entityColumnConstant})
  #foreach($field in ${table.fields})
    public static final String ${field.name.toUpperCase()} = "${field.name}";

  #end
#end
#if(${activeRecord})
    @Override
    protected Serializable pkVal() {
  #if(${keyPropertyName})
        return this.${keyPropertyName};
  #else
        return null;
  #end
    }

#end
#if(!${entityLombokModel})
    @Override
    public String toString() {
        return "${entity}{" +
  #foreach($field in ${table.fields})
    #if($!{foreach.index}==0)
        "${field.propertyName}=" + ${field.propertyName} +
    #else
        ", ${field.propertyName}=" + ${field.propertyName} +
    #end
  #end
        "}";
    }
#end
}

以下链接中的velocity模板,可以根据需求更改

链接

乐观锁

悲观锁:无论什么时候一上来先加锁,再操作数据

乐观锁:有线程并发才加锁,没有的时候是无锁状态,但是MySql底层在数据操作时其实是加了锁的。MySal实现乐观锁就是依靠version字段,每次修改数据时都把version+1,并且version作为修改条件等于自己查询出来的值如果不相等就修改失败

MybatisPlus提供了update时进行乐观锁自动校验功能,我们只需要在表中提供 version字段,然后配置好MybatisPlus的乐观锁插件即可。

第一步:数据库配置乐观锁

第二步:配置乐观锁插件

第三步骤:实体类标记乐观锁字段

当我们执行 update时,乐观锁就会生效


http://www.kler.cn/news/328404.html

相关文章:

  • Nginx的核心架构和设计原理
  • EnvoyFilter 是 Istio 中用于直接修改 Envoy 配置的一种资源类型
  • 帝都程序猿十二时辰
  • modelsim仿真 wave视图里 数据位宽和进制怎么显示
  • 通信工程学习:什么是CSMA/CD载波监听多路访问/冲突检测
  • 计算机知识科普问答--25(121-125)
  • 关于KKT条件的线性约束下非线性问题-MATLAB
  • 【机器学习】过拟合与欠拟合——如何优化模型性能
  • wx小程序中,商城订单详情显示还有多少分钟关闭
  • 「C++系列」模板
  • 项目实战:构建高效可扩展的Flask Web框架:集成Flask-SQLAlchemy、Marshmallow与日志管理
  • SpringBoot集成Redis及SpringCache缓存管理
  • 了解什么是CMMI认证
  • jenkins项目发布基础
  • 【网络基础】网络常识快速入门知识清单,看这篇文章就够了
  • Docker实践与应用:深度探索与丰富案例
  • 论文阅读- On the Feasibility of Fully AI-automated Vishing Attacks
  • 基于SpringBoot的街道志愿者服务平台设计与实现
  • npm、yarn、pnpm对比
  • 2024年9月个人工作生活总结
  • STM32原理知识查询表
  • linux常用命令汇编(持续更新)
  • 计算机毕业设计之:音乐媒体播放及周边产品运营平台(源码+文档+讲解)
  • 软件供应链安全管理实践之中国科学院软件研究所
  • Python和MATLAB库尔巴克–莱布勒散度信息论统计学生物学和算法模型
  • 李沐深度学习-多层感知机、模型选择、过拟合、欠拟合
  • PHP基础知识
  • 前端独立实现页面是否有发布
  • Android 热点分享二维码功能简单介绍
  • openpnp - 底部相机高级校正的参数设置