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