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

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时,乐观锁就会生效

相关文章:

mybatis-puls快速入门

1.概述 在真实项目开发中我们的服务模块&#xff0c;一般都要进行数据库操作&#xff0c;并且每个domain都有crud&#xff0c;需多次写重复代码。我们使用MybatisPlus&#xff0c;就不用写重复代码&#xff0c;并且还有模板的功能&#xff0c;可以一键生成daomin,query,mapper…...

Pool 和 PG 架构(二)

Ceph 的存储架构设计旨在提供高可用性和可扩展性。其中&#xff0c;Pool&#xff08;存储池&#xff09;和 PG&#xff08;放置组&#xff09;是两个核心概念。下面详细介绍 Ceph 的 Pool 和 PG 架构以及它们之间的关系。 1. Pool池 概念&#xff1a; Pool&#xff08;存储池…...

客户服务升级指南:如何以细节赢得客户忠诚

在当今这个竞争激烈的市场环境中&#xff0c;客户忠诚度已成为企业生存与发展的关键所在。而要想赢得并维持客户的忠诚&#xff0c;仅凭优质的产品或服务已远远不够&#xff0c;更需要企业在客户服务上下足功夫&#xff0c;从每一个细节入手&#xff0c;打造超越客户期待的服务…...

闲盒支持的组网方式和注意事项

1. 直连光猫拨号​ 通过光猫拨号&#xff0c;设备直连光猫的设备&#xff0c;需要对光猫开启UPNP并关闭DMZ 如果只接一个盒子&#xff0c;建议直接针对盒子IP开dmz。 2. 直连路由器​ 通过路由器拨号&#xff0c;设备直连路由器的设备&#xff0c;需要对路由器开启UPNP并关闭…...

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下搭建MaxKB开源知识库问答系统

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下搭建MaxKB开源知识库问答系统 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、MaxKB 介绍2.1 MaxKB简介2.2 MaxKB整体架构…...

[Linux]:信号(上)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 1. 信号的引入 1.1 信号的概念 在Linux系统中&#xff0c;信号&#xff08;…...

浙大数据结构:05-树9 Huffman Codes

这道题难度挺大&#xff0c;写起来较为费劲&#xff0c;这里我依然使用了STL库&#xff0c;使得代码量大幅减少不过百行&#xff0c;便于大家理解。 机翻&#xff1a; 1、条件准备 数组存储字符对应频率&#xff0c;n,student存储输入多少字符&#xff0c;有多少学生测试。 …...

scrapy爬虫基础

一、初识 创建项目&#xff1a; scrapy startproject my_one_project # 创建项目命令 cd my_one_project # 先进去&#xff0c; 后面在里面运行 运行爬虫命令为&#xff1a;scrapy crawl tk spiders下创建test.py 其中name就是scrapy crawl tk &…...

利用H5无插件播放RTSP流的实现方案

文章目录 0. 引言1. 问题分析1.1 RTSP流与浏览器的兼容性1.2 解决思路 2. 方案设计2.1 总体架构2.2 关键组件 3. 实施步骤3.1 环境准备3.2 安装与配置3.2.1 安装FFmpeg3.2.2 安装OpenResty3.2.3 添加nginx-rtmp-module模块3.2.4 配置OpenResty 3.3 推流操作3.4 前端播放3.4.1 引…...

CSS文本格式化

通过 CSS 中的文本属性您可以像操作 Word 文档那样定义网页中文本的字符间距、对齐方式、缩进等等&#xff0c;CSS 中常用的文本属性如下所示&#xff1a; text-align&#xff1a;设置文本的水平对齐方式&#xff1b;text-decoration&#xff1a;设置文本的装饰&#xff1b;te…...

python的 __name__和__doc__属性

__name__属性 __name__属性 用于判断当前模块是不是程序入口&#xff0c;如果当前程序正在使用&#xff0c;__name__的值为__main__。 在编写程序时&#xff0c;通常需要给每个模块添加条件语句&#xff0c;用于单独测试该模块的功能。 每个模块都有一个名称&#xff0c;当一…...

Go语言中的Mutex实现探讨

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在并发编程中,互斥锁(Mutex)是一个重要的工具,它帮助我们控制多个协程对共享资源的访问,从而防止数据竞争和不一致性。本文将深入探讨Go语言中Mutex的实现历程和使用方式,同时分享在处理并发问题时的思路与…...

第五届计算机科学与管理科技国际学术会议(ICCSMT 2024)

梁哲&#xff0c;同济大学长聘特聘教授&#xff0c;国家杰青、首届国家杰青延续项目获得者、上海市曙光学者、上海市优秀学术带头人。本科毕业于新加坡国立大计算机工程系、硕士毕业于新加坡国立大学工业与系统工程系、博士毕业于美国新泽西州立大学工业工程系。理论研究主要集…...

【machine learning-13-线性回归的向量化】

向量化 向量化简洁并行计算 向量化 线性回归的向量化表示如下&#xff0c;其中w 和 x 都分别加了箭头表示这是个向量&#xff0c;后续不加也可以表示为向量&#xff0c;w和x点乘加上b&#xff0c;就构成了多元线性回归的表达方式&#xff0c;如下&#xff1a; 那么究竟为什么…...

【CSS|第2期】探索HTML与CSS中的文档流:从自然流到高级布局技巧

日期&#xff1a;2024年9月9日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉在这里插入代码片得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对…...

MATLAB绘图基础9:多变量图形绘制

参考书&#xff1a;《 M A T L A B {\rm MATLAB} MATLAB与学术图表绘制》(关东升)。 9.多变量图形绘制 9.1 气泡图 气泡图用于展示三个或更多变量变量之间的关系&#xff0c;气泡图的组成要素&#xff1a; 横轴( X {\rm X} X轴)&#xff1a;表示数据集中的一个变量&#xff0c…...

JBOSS中间件漏洞复现

CVE-2015-7501 1.开启环境 cd vulhub/jboss/JMXInvokerServlet-deserialization docker-compose up -d docker ps 2.访问靶场 3.访问/invoker/JMXInvokerServlet目录 4.将反弹shell进⾏base64编码 bash -i >& /dev/tcp/47.121.191.208/6666 0>&1 YmFzaCAt…...

每日论文6—16ISCAS一种新型低电流失配和变化电流转向电荷泵

《A Novel Current Steering Charge Pump with Low Current Mismatch and Variation》16ISCAS 本文首先介绍了传统的current steering charge pump&#xff0c;如下图&#xff1a; 比起最简单的电荷泵&#xff0c;主要好处是UP和DN开关离输出节点较远&#xff0c;因此一定程度…...

低代码开发平台:未来五大发展趋势预测

在数字化转型的浪潮中&#xff0c;低代码开发平台正迅速崛起&#xff0c;成为企业软件开发的重要工具。随着技术的不断进步和市场需求的持续增长&#xff0c;低代码开发平台在未来将展现出更为广阔的发展前景。本文将预测并探讨低代码开发平台的五大发展趋势。 深度融合数字化与…...

国内AI大模型,这篇文章说透了

探索国内顶尖AI企业及其创新产品。 人工智能&#xff08;AI&#xff09;的发展正以前所未有的速度推进。 从简单的自动化任务到复杂的决策制定、自然语言处理、图像识别及自主系统的实现&#xff0c;不断拓宽着人类智慧的边界。 国内AI发展迅猛&#xff0c;不仅在理论研究上…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...