The article directories

  • Paging query
    • Common paging
  • Automatically fill in new time, update time, operator and other attributes
  • Custom field types
    • Automatically map Json data
    • Automatically map spatial data formats
  • Customize the Controller template
    • Custom code templates
    • Custom property injection
    • Available parameters from the framework
    • The template to modify
  • Data permission implementation
  • Lambda conditional constructor
    • General Wrapper
    • QueryWrapper
    • UpdateWrapper

Spring Boot integration Mybatis- Plus

Paging query

First configure the paging plug-in

    MybatisConfiguration#useDeprecatedExecutor = false to avoid cache problems (this property will be removed when the old plugin is removed) */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(a) {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer(a) {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
Copy the code

Common paging

Example code for paging queries

    @GetMapping
    public Response pageAll(@RequestParam(required = false, defaultValue = "1") long current,
                            @RequestParam(required = false, defaultValue = "10") long size,
                            String roadName) {
        Page roadPage = new Page<>(current, size);
        LambdaQueryWrapper<Road> queryWrapper = new QueryWrapper().lambda();
        queryWrapper.like(StringUtils.isNotBlank(roadName), Road::getRoadName, roadName);
        Page pageList = roadService.page(roadPage, queryWrapper);
        return Response.ok(pageList);
    }
Copy the code

Automatically fill in new time, update time, operator and other attributes

  • First of all,To implement the meta-object processor interface:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) { When Mapper calls insert, it does the following
        log.info("start insert fill ...."); // Set the value for the entity property
        this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class);
        this.strictInsertFill(metaObject, "operator", String.class, "Zhang");
    }
    @Override
    public void updateFill(MetaObject metaObject) {Mapper performs the following operations when calling the update operation
        log.info("start update fill ...."); // Set the value for the entity property
        this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class);
        this.strictUpdateFill(metaObject, "operator", String.class, "Zhang"); }}Copy the code

The default methods provided by MetaObjectHandler have the following strategies:

  • If the attribute has a value, it is not overridden.
  • If the fill-in value is zeronullIs not filled.
  • Second, annotate the corresponding entity class

Note: Specify that this attribute must have a value in the case, or null if there is no value

The field must declare the TableField annotation, and the attribute fill selects the corresponding policy. This declaration tells Mybatis-Plus that it needs to reserve the injection of SQL fields

How to determine if the injected SQL scripts for insert and UPDATE omit the if tag of the fields in the corresponding case…

@Data
@EqualsAndHashCode(callSuper = false)
public class Stake implements Serializable {
    private Long roadId;
    private String stakeName;

    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String operator;

}
Copy the code

The overall principle

  • When an INSERT or update SQL script occurs

  • Take a look at the annotation of the corresponding field in the entity where the relevant SQL is currently occurring

    • annotationsFieldFill.INSERT, that is, dynamic add<if test="..." >... </if>Insert related field
    • annotationsFieldFill.UPDATE, that is, dynamic add<if test="..." >... </if>Update related fields
    • annotationsFieldFill.UPDATE, that is, dynamic add<if test="..." >... </if>Insert and UPDATE related fields

Custom field types

Type handler, used for converting between JavaType and JdbcType, used to set parameter values in PreparedStatement and fetch a value from a ResultSet or CallableStatement. This article explains how the myBaitS-Plus built-in common type processor can be quickly injected into myBatis containers via TableField annotations.

If an error is reported in XML with 5 custom handlers, delete the XML or configure it in XML as well

Automatically map Json data

@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
    private Long id;

    /** * attention!! Mapping annotations must be enabled * *@TableName(autoResultMap = true) * * Either of the following two types of processors can exist simultaneously * * Note!! Selecting the corresponding JSON processor must also have the corresponding JSON parsing dependency package */
    @TableField(typeHandler = JacksonTypeHandler.class)
    // @TableField(typeHandler = FastjsonTypeHandler.class)
    private OtherInfo otherInfo;

}
Copy the code

This annotation corresponds to the XML notation for

<result column="other_info" jdbcType="VARCHAR" property="otherInfo" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
Copy the code

Automatically map spatial data formats

Mybatis custom field types take Mysql space data store as an example

@Data
@EqualsAndHashCode(callSuper = false)
@TableName( autoResultMap = true)
public class ServiceArea implements Serializable {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /** * Latitude and longitude format: X,Y */
    @TableField(typeHandler = JacksonTypeHandler.class)
    private double[] location;

    @TableField(typeHandler = GeoPointTypeHandler.class)
    private GeoPoint coordinate;

}
Copy the code

Customize the Controller template

Custom code templates

/ / specify custom templates path, location: / resources/templates/entity2. Java. FTL (or vm).
// Be careful not to include.ftl(or.vm), which is automatically identified according to the template engine used
TemplateConfig templateConfig = new TemplateConfig()
    .setEntity("templates/entity2.java");

AutoGenerator mpg = new AutoGenerator();
// Configure a custom template
mpg.setTemplate(templateConfig);
Copy the code

Custom property injection

InjectionConfig injectionConfig = new InjectionConfig() {
    // Custom attribute injection: ABC
    // In the.ftl(or.vm) template, get the attributes with ${cfg. ABC}
    @Override
    public void initMap(a) {
        Map<String, Object> map = new HashMap<>();
        map.put("abc".this.getConfig().getGlobalConfig().getAuthor() + "-mp");
        this.setMap(map); }}; AutoGenerator mpg =new AutoGenerator();
// Configure custom attribute injection
mpg.setCfg(injectionConfig);
Copy the code
Entity2.java.ftl custom attribute injection ABC =${cfg. ABC} Entity2.java.vm custom attribute injection ABC =$! {cfg.abc}Copy the code

Available parameters from the framework

Github AbstractTemplateEngine class getObjectMap returns all values of objectMap available.

/** * Render object MAP information **@paramTableInfo Table information object *@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);
    }
Copy the code

The template to modify

  • ${table.serviceName? substring(1)}– Delete the initial letter
  • ${table.serviceName? uncap_first}– Write the first letter in smaller capitals
packageThe ${package.Controller};

import org.springframework.web.bind.annotation.RequestMapping;
importThe ${package.Entity}.${entity};
importThe ${package.Service}.${table.serviceName}; < #if restControllerStyle>
importorg.springframework.web.bind.annotation.RestController; < #else>
importorg.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;
importorg.springframework.web.bind.annotation.*; < #ifsuperControllerClassPackage?? >import${superControllerClassPackage}; < / #if>

/** * <p> * ${table.comment! } Front-end controller * </p> * *@author ${author}
* @since ${date}
*/< #if restControllerStyle>
@RestController< #else>
@Controller< / #if>
@RequestMapping("<#if package.ModuleName?? && package.ModuleName ! = "">/${package.ModuleName}
      /<#if controllerMappingHyphenStyle?? >${controllerMappingHyphen}<#else>${table.entityPath}
      ")< #ifsuperControllerClass?? >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 = newQueryWrapper().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) {
        returnResponse.ok(${table.serviceName? substring(1)? uncap_first}.saveOrUpdate(param)); }@GetMapping("/{id}")
    @apiOperation (value = "${table.comment! } ")
    public Response get(@PathVariable Long id) {
        returnResponse.ok(${table.serviceName? substring(1)? uncap_first}.getById(id)); }@DeleteMapping("/{id}")
    @apiOperation (value = "${table.comment! } ")
    public Response delete(@PathVariable Long id) {
        returnResponse.ok(${table.serviceName? substring(1)?uncap_first}.removeById(id));
    }
}
Copy the code

Data permission implementation

Look at my other blog post, which details how to build a development scaffolding from scratch based on Mybatis-Plus data permissions

Lambda conditional constructor

Baomidou.com/guide/wrapp…

The parent classes of QueryWrapper(LambdaQueryWrapper) and UpdateWrapper(LambdaUpdateWrapper) are used to generate WHERE conditions for SQL, Entity attributes are also used to generate SQL WHERE conditions Note: The WHERE conditions generated by entity do not have any behavior associated with where conditions generated using the various apis

The Lambda conditional constructor is recommended to reflect the call relationship directly when the property changes. Not a static string configuration like QueryWrapper

Lambda conditional constructor:

        Page roadPage = new Page<>(current, size);
        LambdaQueryWrapper<Board> queryWrapper = newQueryWrapper().lambda(); queryWrapper.eq(roadId ! =null, Board::getRoadId, roadId); queryWrapper.eq(deptId ! =null, Board::getDeptId, deptId);
        Page pageList = boardService.page(roadPage, queryWrapper);
Copy the code

Normal conditional constructor:

        Page roadPage = new Page<>(current, size);
        QueryWrapper<Board> queryWrapper = newQueryWrapper(); queryWrapper.eq(roadId ! =null."road_id", roadId); queryWrapper.eq(deptId ! =null."dept_id", deptId);
        Page pageList = boardService.page(roadPage, queryWrapper);
Copy the code

General Wrapper

conditions explain example
allEq All are equal Case 1:AllEq ({id: 1, name: "wang", the age: null})— – >Id = 1 and name = 'old' and age is nullExample 2:AllEq ({id:1, age:null}, false)— – >Id = 1 and id = 1
eq Equal to = Eq (” name “, “Lao Wang”)--->Name = ‘Lao Wang’
ne Is not the same as < > Ne (” Name “, “Lao Wang”)--->Name <> ‘Lao Wang’
gt More than > Gt (” age “, 18)--->age > 18
ge The value is greater than or equal to >= Ge (” age “, 18)--->age >= 18
lt < < 18) lt (” age “,--->age < 18
le Less than or equal to <= Le (” age “, 18)--->age <= 18
between BETWEEN values 1 AND 2 Between (” age “, 18, 30)--->age between 18 and 30
notBetween NOT BETWEEN The value is 1 AND 2 NotBetween (” age “, 18, 30)--->age not between 18 and 30
like Values LIKE ‘% %’ The like (” name “, “the king”)--->Name like ‘% king %’
notLike NOT LIKE ‘% value %’ NotLike (” name “, “the king”)--->Name not like ‘% king %’
likeLeft Values LIKE ‘%’ LikeLeft (” name “, “the king”)--->Name like ‘% king’
likeRight Values LIKE ‘%’ LikeRight (” name “, “the king”)--->Name like ‘king %’
isNull Field IS NULL IsNull (” name “)--->name is null
isNotNull The field IS NOT NULL IsNotNull (” name “)--->name is not null
in IN (value.get(0), value.get(1),… In (” age “, {1, 2, 3})--->The age in (1, 2, 3)
notIn NOT IN (value.get(0), value.get(1),… NotIn (” age “, {1, 2, 3})--->The age not in (1, 2, 3)
inSql Field IN (SQL statement) Ex. :InSql (" age ", "6")— – >The age in (6)Ex. :inSql("id", "select id from table where id < 3")— – >id in (select id from table where id < 3)
notInSql Field NOT IN (SQL statement) Ex. :NotInSql (" age ", "6")— – >The age not in (6)Ex. :notInSql("id", "select id from table where id < 3")— – >id not in (select id from table where id < 3)
groupBy Grouping: GROUP BY field… GroupBy (” id “, “name”)--->group by id,name
orderByAsc Sort: ORDER BY… ASC OrderByAsc (” id “, “name”)--->order by id ASC,name ASC
orderByDesc Sort: ORDER BY… DESC Ex. :orderByDesc("id", "name")— – >order by id DESC,name DESC
orderBy Sort: ORDER BY… Ex. :orderBy(true, true, "id", "name")— – >order by id ASC,name ASC
having HAVING (SQL statement) Ex. :having("sum(age) > 10")— – >having sum(age) > 10Ex. :having("sum(age) > {0}", 11)— – >having sum(age) > 11
func Func method (mainly convenient in the presence of if… Else call can be repeated in different ways) Ex. :func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
The or joining together the or Take the initiative to callorIt means immediately after the nextmethodsNot withandConnection! (don’t callorThe default value is YesandConnection) Eq. (” id “, 1) or (.) eq (” name “, “king”)--->Id = 1 or id = 1
The OR nested OR Or (I -> I.E Q (” name “, “Li Bai”).ne(” Status “, “Alive”)--->Or (name = ‘id’ and status <> ‘id’)
and AND nested Ex. :And (I -> I.E Q ("name", "Li Bai ").ne("status"," alive ")— – >And (name = 'id' and status = 'id ')
nested Normal nesting does not include AND OR OR Nested (I – > appropriate precautions q (” name “, “li bai”). Ne (” status “, “alive”))--->(name = ‘李白’ and status <> ‘活着’)
apply Concatenate SQL, which can be used for databasesfunctionDynamically enteredparamsIn front of the correspondingapplySqlThe inside of the{index}This is not SQL injection risk, on the contrary there will be! Ex. :apply("id = 1")— – >id = 1Ex. :apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")— – >date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")Ex. :apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")— – >date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
last Ignore the optimization rules and directly concatenate the last SQL invocation only once. If you invoke the last SQL invocation multiple times, SQL injection risks. Exercise caution when using this command last("limit 1")
exists Concatenate EXISTS (SQL statement) Ex. :exists("select id from table where age = 1")— – >exists (select id from table where age = 1)
notExists Concatenate NOT EXISTS Ex. :notExists("select id from table where age = 1")— – >not exists (select id from table where age = 1)

QueryWrapper

type explain example
select Setting query fields Ex. :select("id", "name", "age")Ex. :select(i -> i.getProperty().startsWith("test"))

UpdateWrapper

type explain example
set SQL SET field Ex. :Set ("name", "Lao Li tou ")Ex. :set("name", "")– > The database field value changes toAn empty stringEx. :set("name", null)– > The database field value changes tonull
setSql SET part of SET SQL Ex. :SetSql ("name = 'name')