@TableField(fill = FieldFill.UPDATE)
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private String operator;
}
整体原理
- 当发生
insert
或者update
的sql脚本时候 - 看下当前发生相关sql 的实体中相应字段的注解
- 注解
FieldFill.INSERT
,即动态添加<if test="...">......</if>
insert相关字段 - 注解
FieldFill.UPDATE
,即动态添加<if test="...">......</if>
update相关字段 - 注解
FieldFill.UPDATE
,即动态添加<if test="...">......</if>
insert和update相关字段
自定义字段类型
================================================================
类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值,本文讲解 mybaits-plus
内置常用类型处理器如何通过TableField
注解快速注入到 mybatis
容器中。
如果报xml中五自定义handler的错误,把xml删除,或者在xml中也配置上
自动映射Json数据
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
private Long id;
/**
• 注意!! 必须开启映射注解
•
• @TableName(autoResultMap = true)
•
• 以下两种类型处理器,二选一 也可以同时存在
•
• 注意!!选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包
*/
@TableField(typeHandler = JacksonTypeHandler.class)
// @TableField(typeHandler = FastjsonTypeHandler.class)
private OtherInfo otherInfo;
}
该注解对应了 XML 中写法为
自动映射空间数据格式
可以看我的另一篇很详细 从零搭建开发脚手架 mybatis自定义字段类型 以Mysql空间数据存储为例
@Data
@EqualsAndHashCode(callSuper = false)
@TableName( autoResultMap = true)
public class ServiceArea implements Serializable {
@TableId(value = “id”, type = IdType.AUTO)
private Integer id;
/**
• 经纬度 格式:X,Y
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private double[] location;
@TableField(typeHandler = GeoPointTypeHandler.class)
private GeoPoint coordinate;
}
自定义Controller模板
========================================================================
自定义代码模板
//指定自定义模板路径, 位置:/resources/templates/entity2.java.ftl(或者是.vm)
//注意不要带上.ftl(或者是.vm), 会根据使用的模板引擎自动识别
TemplateConfig templateConfig = new TemplateConfig()
.setEntity(“templates/entity2.java”);
AutoGenerator mpg = new AutoGenerator();
//配置自定义模板
mpg.setTemplate(templateConfig);
自定义属性注入
InjectionConfig injectionConfig = new InjectionConfig() {
//自定义属性注入:abc
//在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put(“abc”, this.getConfig().getGlobalConfig().getAuthor() + “-mp”);
this.setMap(map);
}
};
AutoGenerator mpg = new AutoGenerator();
//配置自定义属性注入
mpg.setCfg(injectionConfig);
entity2.java.ftl
自定义属性注入abc=${cfg.abc}
entity2.java.vm
自定义属性注入abc=$!{cfg.abc}
框架自带的可用参数
Github AbstractTemplateEngine 类中方法 getObjectMap 返回 objectMap 的所有值都可用。
/**
• 渲染对象 MAP 信息
•
• @param tableInfo 表信息对象
• @return ignore
*/
public Map<String, Object> getObjectMap(TableInfo tableInfo) {
Map<String, Object> objectMap;
ConfigBuilder config = getConfigBuilder();
if (config.getStrategyConfig().isControllerMappingHyphenStyle()) {
objectMap = CollectionUtils.newHashMapWithExpectedSize(33);
objectMap.put(“controllerMappingHyphenStyle”, config.getStrategyConfig().isControllerMappingHyphenStyle());
objectMap.put(“controllerMappingHyphen”, StringUtils.camelToHyphen(tableInfo.getEntityPath()));
} else {
objectMap = CollectionUtils.newHashMapWithExpectedSize(31);
}
objectMap.put(“restControllerStyle”, config.getStrategyConfig().isRestControllerStyle());
objectMap.put(“config”, config);
objectMap.put(“package”, config.getPackageInfo());
GlobalConfig globalConfig = config.getGlobalConfig();
objectMap.put(“author”, globalConfig.getAuthor());
objectMap.put(“idType”, globalConfig.getIdType() == null ? null : globalConfig.getIdType().toString());
objectMap.put(“logicDeleteFieldName”, config.getStrategyConfig().getLogicDeleteFieldName());
objectMap.put(“versionFieldName”, config.getStrategyConfig().getVersionFieldName());
objectMap.put(“activeRecord”, globalConfig.isActiveRecord());
objectMap.put(“kotlin”, globalConfig.isKotlin());
objectMap.put(“swagger2”, globalConfig.isSwagger2());
objectMap.put(“date”, new SimpleDateFormat(“yyyy-MM-dd”).format(new Date()));
objectMap.put(“table”, tableInfo);
objectMap.put(“enableCache”, globalConfig.isEnableCache());
objectMap.put(“baseResultMap”, globalConfig.isBaseResultMap());
objectMap.put(“baseColumnList”, globalConfig.isBaseColumnList());
objectMap.put(“entity”, tableInfo.getEntityName());
objectMap.put(“entitySerialVersionUID”, config.getStrategyConfig().isEntitySerialVersionUID());
objectMap.put(“entityColumnConstant”, config.getStrategyConfig().isEntityColumnConstant());
objectMap.put(“entityBuilderModel”, config.getStrategyConfig().isEntityBuilderModel());
objectMap.put(“chainModel”, config.getStrategyConfig().isChainModel());
objectMap.put(“entityLombokModel”, config.getStrategyConfig().isEntityLombokModel());
objectMap.put(“entityBooleanColumnRemoveIsPrefix”, config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix());
objectMap.put(“superEntityClass”, getSuperClassName(config.getStrategyConfig().getSuperEntityClass()));
objectMap.put(“superMapperClassPackage”, config.getStrategyConfig().getSuperMapperClass());
objectMap.put(“superMapperClass”, getSuperClassName(config.getStrategyConfig().getSuperMapperClass()));
objectMap.put(“superServiceClassPackage”, config.getStrategyConfig().getSuperServiceClass());
objectMap.put(“superServiceClass”, getSuperClassName(config.getStrategyConfig().getSuperServiceClass()));
objectMap.put(“superServiceImplClassPackage”, config.getStrategyConfig().getSuperServiceImplClass());
objectMap.put(“superServiceImplClass”, getSuperClassName(config.getStrategyConfig().getSuperServiceImplClass()));
objectMap.put(“superControllerClassPackage”, verifyClassPacket(config.getStrategyConfig().getSuperControllerClass()));
objectMap.put(“superControllerClass”, getSuperClassName(config.getStrategyConfig().getSuperControllerClass()));
return Objects.isNull(config.getInjectionConfig()) ? objectMap : config.getInjectionConfig().prepareObjectMap(objectMap);
}
模板修改
• ${table.serviceName?substring(1)} - 删除首字母
• ${table.serviceName?uncap_first} - 首字母大写变小写
package ${package.Controller};
import org.springframework.web.bind.annotation.RequestMapping;
import {entity};import {table.serviceName};<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
import com.laker.map.ext.framework.Response;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>
/**
•
• ${table.comment!} 前端控制器
•
•
• @author ${author}
• @since ${date}
*/
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping(“<#if package.ModuleName?? && package.ModuleName != “”>/KaTeX parse error: Expected 'EOF', got '#' at position 23: …e.ModuleName}</#̲if>/<#if contro…{controllerMappingHyphen}<#else>${table.entityPath}</#if>”)
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>
@Autowired
${table.serviceName} ${table.serviceName?substring(1)?uncap_first};
@GetMapping
@ApiOperation(value = “${table.comment!}分页查询”)
public Response pageAll(@RequestParam(required = false, defaultValue = “1”) long current,
@RequestParam(required = false, defaultValue = “10”) long size) {
Page roadPage = new Page<>(current, size);
LambdaQueryWrapper<${table.entityName}> queryWrapper = new QueryWrapper().lambda();
Page pageList = ${table.serviceName?substring(1)?uncap_first}.page(roadPage, queryWrapper);
return Response.ok(pageList);
}
@PostMapping
@ApiOperation(value = “新增或者更新${table.comment!}”)
public Response saveOrUpdate(${table.entityName} param) {
return Response.ok(${table.serviceName?substring(1)?uncap_first}.saveOrUpdate(param));
}
@GetMapping(“/{id}”)
@ApiOperation(value = “根据id查询${table.comment!}”)
public Response get(@PathVariable Long id) {
return Response.ok(${table.serviceName?substring(1)?uncap_first}.getById(id));
}
@DeleteMapping(“/{id}”)
@ApiOperation(value = “根据id删除${table.comment!}”)
public Response delete(@PathVariable Long id) {
return Response.ok(${table.serviceName?substring(1)?uncap_first}.removeById(id));
}
}
数据权限实现
===============================================================
看我另一篇博文,很详细从零搭建开发脚手架 基于Mybatis-Plus的数据权限实现
Lambda条件构造器
====================================================================
https://baomidou.com/guide/wrapper.html#abstractwrapper
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
建议使用Lambda条件构造器,当属性更改时,可以直接反应出来调用关系。不像QueryWrapper一样是静态字符串配置的
Lambda条件构造器:
Page roadPage = new Page<>(current, size);
LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda();
queryWrapper.eq(roadId != null, Board::getRoadId, roadId);
queryWrapper.eq(deptId != null, Board::getDeptId, deptId);
Page pageList = boardService.page(roadPage, queryWrapper);
普通条件构造器:
Page roadPage = new Page<>(current, size);
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq(roadId != null, “road_id”, roadId);
queryWrapper.eq(deptId != null, “dept_id”, deptId);
Page pageList = boardService.page(roadPage, queryWrapper);
通用Wrapper
| 条件 | 解释 | 例子 |
| — | — | — |
| allEq | 全部相等 | 例1: allEq({id:1,name:"老王",age:null})
—>id = 1 and name = '老王' and age is null
例2: allEq({id:1,name:"老王",age:null}, false)
—>id = 1 and name = '老王'
|
| eq | 等于 = | eq(“name”, “老王”)--->
name = ‘老王’ |
| ne | 不等于 <> | ne(“name”, “老王”)--->
name <> ‘老王’ |
| gt | 大于 > | gt(“age”, 18)--->
age > 18 |
| ge | 大于等于 >= | ge(“age”, 18)--->
age >= 18 |
| lt | 小于 < | lt(“age”, 18)--->
age < 18 |
| le | 小于等于 <= | le(“age”, 18)--->
age <= 18 |
| between | BETWEEN 值1 AND 值2 | between(“age”, 18, 30)--->
age between 18 and 30 |
| notBetween | NOT BETWEEN 值1 AND 值2 | notBetween(“age”, 18, 30)--->
age not between 18 and 30 |
| like | LIKE ‘%值%’ | like(“name”, “王”)--->
name like ‘%王%’ |
| notLike | NOT LIKE ‘%值%’ | notLike(“name”, “王”)--->
name not like ‘%王%’ |
| likeLeft | LIKE ‘%值’ | likeLeft(“name”, “王”)--->
name like ‘%王’ |
| likeRight | LIKE ‘值%’ | likeRight(“name”, “王”)--->
name like ‘王%’ |
| isNull | 字段 IS NULL | isNull(“name”)--->
name is null |
| isNotNull | 字段 IS NOT NULL | isNotNull(“name”)--->
name is not null |
| in | 字段 IN (value.get(0), value.get(1), …) | in(“age”,{1,2,3})--->
age in (1,2,3) |
| notIn | NOT IN (value.get(0), value.get(1), …) | notIn(“age”,{1,2,3})--->
age not in (1,2,3) |
| inSql | 字段 IN ( sql语句 ) | 例: inSql("age", "1,2,3,4,5,6")
—>age in (1,2,3,4,5,6)
例: inSql("id", "select id from table where id < 3")
—>id in (select id from table where id < 3)
|
| notInSql | 字段 NOT IN ( sql语句 ) | 例: notInSql("age", "1,2,3,4,5,6")
—>age not in (1,2,3,4,5,6)
例: notInSql("id", "select id from table where id < 3")
—>id not in (select id from table where id < 3)
|
| groupBy | 分组:GROUP BY 字段, … | groupBy(“id”, “name”)--->
group by id,name |
| orderByAsc | 排序:ORDER BY 字段, … ASC | orderByAsc(“id”, “name”)--->
order by id ASC,name ASC |
| orderByDesc | 排序:ORDER BY 字段, … DESC | 例: orderByDesc("id", "name")
—>order by id DESC,name DESC
|
| orderBy | 排序:ORDER BY 字段, … | 例: orderBy(true, true, "id", "name")
—>order by id ASC,name ASC
|
| having | HAVING ( sql语句 ) | 例: having("sum(age) > 10")
—>having sum(age) > 10
例: having("sum(age) > {0}", 11)
—>having sum(age) > 11
|
| func | func 方法(主要方便在出现if…else下调用不同方法能不断链) | 例: func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
|
| or 拼接or | 主动调用or
表示紧接着下一个方法不是用and
连接!(不调用or
则默认为使用and
连接) | eq(“id”,1).or().eq(“name”,“老王”)--->
id = 1 or name = ‘老王’ |
| OR 嵌套or | | or(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))--->
or (name = ‘李白’ and status <> ‘活着’) |
| and | AND 嵌套 | 例: and(i -> i.eq("name", "李白").ne("status", "活着"))
—>and (name = '李白' and status <> '活着')
|
| nested | 正常嵌套 不带 AND 或者 OR | nested(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))--->
(name = ‘李白’ and status <> ‘活着’) |
| apply | 拼接 sql,该方法可用于数据库函数 动态入参的params
对应前面applySql
内部的{index}
部分.这样是不会有sql注入风险的,反之会有! | 例: apply("id = 1")
—>id = 1
例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
|
| last | 无视优化规则直接拼接到 sql 的最后 只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用 | last("limit 1")
|
| exists | 拼接 EXISTS ( sql语句 ) | 例: exists("select id from table where age = 1")
—>exists (select id from table where age = 1)
|
| notExists | 拼接 NOT EXISTS ( sql语句 ) | 例: notExists("select id from table where age = 1")
—>not exists (select id from table where age = 1)
|
QueryWrapper
| 类型 | 解释 | 例子 |
| — | — | — |
| select | 设置查询字段 | 例: select("id", "name", "age")
例: select(i -> i.getProperty().startsWith("test"))
|
UpdateWrapper
| 类型 | 解释 | 例子 |
| — | — | — |
| set | SQL SET 字段 | 例: set("name", "老李头")
例: set("name", "")
—>数据库字段值变为空字符串
例: set("name", null)
—>数据库字段值变为null
|
| setSql | 设置 SET 部分 SQL | 例: setSql("name = '老李头'")
|
枚举字段
=============================================================
1.声明枚举
方式一: 使用 @EnumValue 注解枚举属性 完整示例
@Getter
public enum GradeEnum {
PRIMARY(1, “小学”), SECONDORY(2, “中学”), HIGH(3, “高中”);
GradeEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
@EnumValue//标记数据库存的值是code
private final int code;
//。。。
}
方式二: 枚举属性,实现 IEnum 接口如下:
public enum AgeEnum implements IEnum {
ONE(1, “一岁”),
TWO(2, “二岁”),
THREE(3, “三岁”);
private int value;
private String desc;
@Override
public Integer getValue() {
return this.value;
}
}
2.配置扫描枚举
mybatis-plus:
支持统配符 * 或者 ; 分割
typeEnumsPackage: com.baomidou.springboot.entity.enums
3.使用示例
数据库实体使用:
public class User {
/**
• 名字
• 数据库字段: name varchar(20)
*/
private String name;
/**
• 年龄,IEnum接口的枚举处理
• 数据库字段:age INT(3)
*/
private AgeEnum age;
3.使用示例
数据库实体使用:
public class User {
/**
• 名字
• 数据库字段: name varchar(20)
*/
private String name;
/**
• 年龄,IEnum接口的枚举处理
• 数据库字段:age INT(3)
*/
private AgeEnum age;