@TableField(fill = FieldFill.UPDATE)
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private String operator;
}

整体原理

  • 当发生insert或者updatesql脚本时候
  • 看下当前发生相关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);

spring 手脚架_自定义

自定义属性注入

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 的所有值都可用。

spring 手脚架_mybatis_02

/**

• 渲染对象 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;