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: rootpassword: 123456url: jdbc:mysql://8.137.78.153:3306/fccar-driver?serverTimezone=Asia/Shanghai&characterEncoding=utf8driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource # 配置阿里的连接池druid: # Druid 【监控】相关的全局配置# 配置初始化大小、最小、最大initial-size: 5minIdle: 10max-active: 20# 配置获取连接等待超时的时间(单位:毫秒)max-wait: 60000########### 启用内置过滤器(第一个 stat必须,否则监控不到SQL)##########filters: stat,wall,log4j2web-stat-filter:enabled: truestat-view-servlet:enabled: trueallow: # 设置白名单,不填则允许所有访问url-pattern: /druid/*login-username: fccar # 控制台管理用户名和密码login-password: fccarfilter:stat:enabled: truelog-slow-sql: true # 慢 SQL 记录slow-sql-millis: 2000merge-sql: truewall: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: 0logic-delete-value: 1configuration: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需要此配置对象@Beanpublic PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();}//乐观锁插件@Beanpublic 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.分页拦截器

@Beanpublic 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);//生成 resultMapgc.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"}); // 需要生成的表//使用lombokstrategy.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.xxxpc.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() {@Overridepublic void initMap() {this.setMap(new HashMap<String, Object>());}};//文件生成配置List<FileOutConfig> focList = new ArrayList<FileOutConfig>();//controller的输出配置focList.add(new FileOutConfig("/templates/controller.java.vm") {@Overridepublic String outputFile(TableInfo tableInfo) {//合并好的内容输出到哪儿?return outputDir+ "/cn/lgc/controller/manager/" + tableInfo.getEntityName() + "Controller.java";}});// 调整 domain 生成目录演示focList.add(new FileOutConfig("/templates/entity.java.vm") {@Overridepublic String outputFile(TableInfo tableInfo) {return outputDir+ "/cn/lgc/pojo/domain/" + tableInfo.getEntityName() + ".java";}});// 调整 xml 生成目录演示focList.add(new FileOutConfig("/templates/mapper.xml.vm") {@Overridepublic 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{@Autowiredpublic ${table.serviceName} ${table.entityPath}Service;@Operation( summary= "保存${entity}",description = "基础对象保存接口")@Parameter(name = "${table.entityPath}",description = "保存的对象",required = true)@PostMappingpublic 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)@PutMappingpublic 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
#endprivate ${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")#endpublic ${field.propertyType} ${getprefix}${field.capitalName}() {return ${field.propertyName};}#if(${entityBuilderModel})public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {#elsepublic void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {#endthis.${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})@Overrideprotected Serializable pkVal() {#if(${keyPropertyName})return this.${keyPropertyName};#elsereturn null;#end}#end
#if(!${entityLombokModel})@Overridepublic 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.xdnf.cn/news/1547810.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

如何在 Windows PC 或笔记本电脑上恢复未保存的 Word 文档

辛苦工作成果消失得无影无踪可能是任何人最可怕的噩梦&#xff0c;尤其是如果这是一篇长篇论文或项目报告。此问题可能是由于 Windows PC 或笔记本电脑上未保存的 Word 文档造成的。不过&#xff0c;不要惊慌&#xff1b;您仍然有机会在 Windows 机器上恢复未保存的 Word 文档。…

AI驱动的Java开发框架:Spring AI Alibaba实战部署教程

前言 随着生成式 AI 的快速发展&#xff0c;基于 AI 开发框架构建 AI 应用的诉求迅速增长&#xff0c;涌现出了包括 LangChain、LlamaIndex 等开发框架&#xff0c;但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言&a…

51 驱动 ADS1115 AD采集

文章目录 一、ADS1115简介二、引脚功能三、功能介绍1.MULTIPLEXER2.量程3.数字比较器4.寄存器写入或读取时序5.数据格式 四、寄存器介绍1.地址指针寄存器2.转化数据存放寄存器3.配置寄存器4.比较器高低阈值寄存器 五、程序六、实验现象 一、ADS1115简介 ADS1115是高精度模数转…

软考高级:软件架构风格 AI 解读

软件架构风格指的是构建软件系统时常用的一些设计模式或设计方法。它们帮助开发人员从高层次组织代码、功能模块和数据流的方式。让我们通俗地解释一下几种常见的软件架构风格。 生活化例子 假设我们在做一桌丰盛的晚餐&#xff0c;分别由不同的厨师负责炒菜、煲汤、做甜点&a…

了解独享IP的概念及其独特优势

在网络世界中&#xff0c;IP地址是用来识别和定位设备的标识符。独享IP是一种服务模式。使用代理服务器时&#xff0c;用户拥有一个不与其他用户共享的专用独立IP地址。与共享IP相比&#xff0c;独享IP为用户提供了更高的独立性和隐私保护。下面详细介绍独享IP的定义、工作原理…

idea 2024.2切换到旧版的UI

在 IntelliJ IDEA 2024.2 中&#xff0c;新 UI 现在成为所有用户的默认选项&#xff0c;经典 UI 则作为插件提供。 新 UI 简洁而现代&#xff0c;提供更大、更易用的控件、一致的调色盘、明亮清晰的图标、增强的对比度和更好的强调色。 为了使用原来的旧版UI操作其实很简单&am…

构建企业数字化转型的架构指南——基于TOGAF框架的实用方法论

数字化转型的驱动力与挑战 随着全球经济的数字化转型加速&#xff0c;企业正面临技术、业务模式以及组织架构的深刻变革。要实现这一复杂而系统性的转型&#xff0c;仅靠引入新技术是远远不够的&#xff0c;企业必须从战略层面重塑其业务架构&#xff0c;以确保技术投资与业务…

死磕P7: JVM垃圾回收那点事,轻松拿捏不是事儿(一)

这是「死磕P7」系列第 003 篇文章&#xff0c;欢迎大家来跟我一起 死磕 100 天&#xff0c;争取在 2025 年来临之际&#xff0c;给自己一个交代。 上两篇介绍了 JVM 内存区域划分&#xff0c;简单记忆一下就可以了&#xff0c;后面再不断深入吧。 死磕P7: JVM内存划分必知必会…

php 平滑重启 kill -SIGUSR2 <PID> pgrep命令查看进程号

有时候我们使用nginx 大家都知道平滑重启命令: /web/nginx/sbin/nginx -s reload 但大家对php-fpm 重启 可能就是简单暴力的kill 直接搞起了 下面介绍一个sh 文件名保存为start_php.sh 来对php-fpm 进行平滑重启 #!/bin/bash# 检查 PHP-FPM 是否运行 if ! pgrep php-…

常用并发设计模式精讲

1. 优雅终止线程的设计模式 思考&#xff1a;在一个线程 T1 中如何优雅的终止线程 T2&#xff1f; 正确思路&#xff1a;两阶段终止模式 1.1 两阶段终止&#xff08;Two-phase Termination&#xff09;模式——优雅的终止线程 两阶段终止&#xff08;Two-phase Termination…

新160个crackme - 065-Eternal Bliss

运行分析 选择验证方式&#xff0c;破解字符串标题提示为vb程序 PE分析 VB程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 使用VB Decompiler进行分析&#xff0c;发现Command1_Click_403604为check按钮事件&#xff0c;需要使Me 1 CBool(expression) 将表达…

CSS 中的文本相关属性(line - height、font、letter - 属性、text - 属性)

目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 line - height属性 字号与行高的取值约定 行高与盒子高度的关系 font、letter -属性 、text -属性 font属性 letter -属性 text - 属性 非 VIP 用户可前往公众号回复“css”进行免费阅读 line - height属性 字号与…

SQLI—LABS刷题 | SQL总结

Less1-2&#xff08;联合注入&#xff09; ?id1 查询到用户名及密码 ​​​​​​​?id1 报错&#xff1a;You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 1 LIMIT 0,1 at li…

C++结尾

面试题 1.什么是虚函数&#xff1f;什么是纯虚函数 在定义函数时前面加virtual。虚函数是为了&#xff0c;父子类中只有一个该函数。如果在子类重写虚函数&#xff0c;那么用的就是子类重写的虚函数&#xff1b;如果子类没有重写虚函数&#xff0c;那么调用的是父类继承的虚函…

IP地址与5G时代的万物互联

5G时代&#xff0c;海量的设备将接入网络&#xff0c;从智能手机、平板电脑到智能家电、工业传感器等&#xff0c;每一个设备都需要一个独特的IP地址来进行标识和通信。可以说&#xff0c;IP地址就如同这些设备在数字世界中的“身份证”&#xff0c;确保它们能够准确地找到彼此…

vue嵌套路由刷新页面空白问题

问题描述 在vue项目开发中遇到这样一个问题&#xff0c;在history模式下通过页面点击路由跳转可以打开页面&#xff0c;但是在当前页面刷新就空白了&#xff0c;如下&#xff1a; 点击路由跳转页面是有的 刷新页面就空白 代码 {path: "/home",name: "home&qu…

PCIe6.0 AIC金手指和板端CEM连接器信号完整性设计规范

先附上我之前写的关于PCIe5.0金手指的设计解读&#xff1a; PCIe5.0的Add-in-Card(AIC)金手指layout建议&#xff08;一&#xff09;_pcie cem-CSDN博客 PCIe5.0的Add-in-Card(AIC)金手指layout建议&#xff08;二&#xff09;_gnd bar-CSDN博客 首先&#xff0c;相较于PCI…

vscode【实用插件】Code Runner 运行代码

安装 在 vscode 插件市场的搜索 Code Runner点 安装 使用 运行指定文件的代码 用 vscode 打开目标文件&#xff0c;右键快捷菜单运行即可 运行选中的代码 选中要执行的代码右键快捷菜单执行

配置树莓派打开SSH服务

在树莓派终端中查看IP 在终端中输入命令来查看IP地址。最常用的命令是&#xff1a;hostname -I注意&#xff0c;这里的参数I是大写的&#xff0c;它表示查看本机上所有配置的IP地址&#xff08;包括IPv4和IPv6&#xff0c;如果有的话&#xff09;。如果你只需要查看IPv4地址&am…

【NTN 卫星通信】基于NR的NTN RAN架构

1 引言 3GPP中,38.821协议中,研究了如何最大限度地减少对NG-RAN中新接口和协议的需求,以支持非地面网络。 研究了包括透传星和再生星的RAN架构。 2 基于透传星的NG-RAN架构 2.1 概述: 对于透传模式,卫星有效载荷在上行链路和下行链路方向上实现频率转换和射频放大器。它…