如何擴展MybatisPlus的BaseMapper(Sql注入器的使用、自動填充功能、邏輯刪除功能)


工作中常用的MybatisPlus的擴展功能:Sql注入器的使用、自動填充功能、邏輯刪除功能

文章底部有git地址

SQL注入器的使用

我們只用MybatisPlus時,MybatisPlus在BaseMapper中提供了很多可以直接調用的方法,這些方法主要是通過ISqlInjector注入器進行注入,然后並提供使用的,

如果我們也想提供一個公用的方法,就可以通過sql注入器來解決

創建mp_user表

CREATE TABLE `mp_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `create_user` varchar(255) DEFAULT NULL COMMENT '創建人',
  `create_date` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
  `update_user` varchar(255) DEFAULT NULL COMMENT '更新人',
  `update_date` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  `delete_flag` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '刪除標識:0-未刪除,1-已刪除',
  `username` varchar(50) DEFAULT NULL COMMENT '用戶名',
  `password` varchar(50) DEFAULT NULL COMMENT '密碼',
  `birthday` varchar(50) DEFAULT NULL COMMENT '生日',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1434793267970568194 DEFAULT CHARSET=utf8;

添加數據

INSERT INTO `test`.`mp_user`(`id`, `create_user`, `create_date`, `update_user`, `update_date`, `delete_flag`, `username`, `password`, `birthday`) VALUES (1, NULL, NULL, NULL, '2021-09-06 15:51:55', 0, 'lily', '123', '2019-12-12');
INSERT INTO `test`.`mp_user`(`id`, `create_user`, `create_date`, `update_user`, `update_date`, `delete_flag`, `username`, `password`, `birthday`) VALUES (2, NULL, NULL, NULL, '2021-09-06 15:51:56', 0, 'tom', '123', '2019-12-12');

創建User實體

package com.qjc.entity;


import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data
@TableName("mp_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId
    private Long id;

    private String createUser;

    private Date createDate;

    private String updateUser;

    private Date updateDate;
    
    private Integer deleteFlag;

    private String username;

    private String password;

    private String birthday;

}

定義MyBaseMapper繼承BaseMapper

並定義一個mySelectList方法

package com.qjc.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @ClassName: MyBaseMapper
 * @Description:
 * @Author: qjc
 * @Date: 2021/9/6 12:59 下午
 */
public interface MyBaseMapper<T> extends BaseMapper {

    List<T> mySelectList(@Param("ew") Wrapper<T> queryWrapper);

}

然后創建注入器MySqlInjector,繼承SqlInjector的實現類DefaultSqlInjector

package com.qjc.sqlInjector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;

import java.util.List;

/**
 * @ClassName: MySqlInjector
 * @Description:
 * @Author: qjc
 * @Date: 2021/9/6 1:01 下午
 */
public class MySqlInjector extends DefaultSqlInjector {


    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new MySelectList());
        return methodList;
    }
}

創建該注入器需要將自定義的方法添加到methodList中,所以需要創建一個方法類(可以參照源碼中定義的類)

定義方法類MySelectList(和剛才MyBaseMapper中定義的方法名不一致也沒關系,這不是重點)

package com.qjc.sqlInjector;

import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

/**
 * @ClassName: FindAll
 * @Description:
 * @Author: qjc
 * @Date: 2021/9/6 1:02 下午
 */
public class MySelectList extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
//        String sqlMethod = "findAll";
//        String sql = "select * from " + tableInfo.getTableName();
//        SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
//        return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource, modelClass, tableInfo);

        SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
        String sql = String.format(sqlMethod.getSql(), sqlFirst(),sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
                sqlWhereEntityWrapper(true, tableInfo), sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, "mySelectList", sqlSource, tableInfo);
    }

    @Override
    public String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
        if (table.isLogicDelete()) {
            String sqlScript = table.getAllSqlWhere(true, true, WRAPPER_ENTITY_DOT);
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY),
                    true);
            sqlScript += (NEWLINE + table.getLogicDeleteSql(true, true) + NEWLINE);
            String normalSqlScript = SqlScriptUtils.convertIf(String.format("AND ${%s}", WRAPPER_SQLSEGMENT),
                    String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                            WRAPPER_NONEMPTYOFNORMAL), true);
            normalSqlScript += NEWLINE;
            normalSqlScript += SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                    String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                            WRAPPER_EMPTYOFNORMAL), true);
            sqlScript += normalSqlScript;
            sqlScript = SqlScriptUtils.convertChoose(String.format("%s != null", WRAPPER), sqlScript,
                    table.getLogicDeleteSql(false, true));
            sqlScript = SqlScriptUtils.convertWhere(sqlScript);
            return newLine ? NEWLINE + sqlScript : sqlScript;
        } else {
            String sqlScript = table.getAllSqlWhere(false, true, WRAPPER_ENTITY_DOT);
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY), true);
            sqlScript += NEWLINE;
            sqlScript += SqlScriptUtils.convertIf(String.format(SqlScriptUtils.convertIf(" AND", String.format("%s and %s", WRAPPER_NONEMPTYOFENTITY, WRAPPER_NONEMPTYOFNORMAL), false) + " ${%s}", WRAPPER_SQLSEGMENT),
                    String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                            WRAPPER_NONEMPTYOFWHERE), true);
            sqlScript = SqlScriptUtils.convertWhere(sqlScript) + NEWLINE;
            sqlScript += SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                    String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                            WRAPPER_EMPTYOFWHERE), true);
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER), true);
            return newLine ? NEWLINE + sqlScript : sqlScript;
        }
    }
}

重寫injectMappedStatement方法,來將方法注入到MappedStatement中,需要注意的是addSelectMappedStatement方法第二個參數,

是方法名,及MyBaseMapper中自定義的mySelectList

然后最重要的是sqlWhereEntityWrapper方法,該方法就是定義sql語句的,可在此修改為自己想要的查詢語句

讓UserMapper繼承我們創建的MyBaseMapper

package com.qjc.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @ClassName: MyBaseMapper
 * @Description:
 * @Author: qjc
 * @Date: 2021/9/6 12:59 下午
 */
public interface MyBaseMapper<T> extends BaseMapper {

    List<T> mySelectList(@Param("ew") Wrapper<T> queryWrapper);

}

最后一步將自定義的sql注入器注冊到Spring容器中

@Bean
    public MySqlInjector mySqlInjector() {
        return new MySqlInjector();
    }

然后進行測試

 自動填充功能

我們創建的表中有創建用戶,創建時間,刪除標識字段,這些都是在創建用戶時應該自動填充的,更新時間和更新用戶字段是在更新用戶信息的時候自動填充的

具體操作方法如下:在插入時需要自動填充的的字段上用注解@TableField(fill = FieldFill.INSERT),在更新時需要自動填充的字段上用注解@TableField(fill = FieldFill.UPDATE)

有其他需要填充的字段可參考FieldFill

User實體如下

package com.qjc.entity;


import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data
@TableName("mp_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId
    private Long id;

    @TableField(fill = FieldFill.INSERT)
    private String createUser;

    @TableField(fill = FieldFill.INSERT)
    private Date createDate;

    @TableField(fill = FieldFill.UPDATE)
    private String updateUser;

    @TableField(fill = FieldFill.UPDATE)
    private Date updateDate;

    @TableField(fill = FieldFill.INSERT)
    private Integer deleteFlag;

    private String username;

    private String password;

    private String birthday;

}

然后定義MyMetaObjectHandler實現MetaObjectHandler接口,重寫insertFill和updateFill方法

package com.qjc.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @ClassName: MyMetaObjectHandler
 * @Description:
 * @Author: qjc
 * @Date: 2021/9/6 4:01 下午
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createUser", String.class, "qjc");
        this.strictInsertFill(metaObject, "createDate", Date.class, new Date());
        this.strictInsertFill(metaObject, "deleteFlag", Integer.class, 0);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateUser", String.class, "qjc");
        this.strictUpdateFill(metaObject, "updateDate", Date.class, new Date());
    }
}

測試代碼

@Test
    public void insert() {
        User user = new User();
        user.setUsername("lucy");
        user.setPassword("55555");
        user.setBirthday("2020-01-01");
        userMapper.insert(user);
        System.err.println(user.getId());
    }

結果如下

 然后進行更新操作

    @Test
    public void update() {
        User user = new User();
        user.setId(1435062640798867458L);
        user.setUsername("lucy");
        user.setPassword("66666");
        user.setBirthday("2020-01-01");
        userMapper.updateById(user);
    }

結果如下

邏輯刪除

我們在實際工作中,一般數據是不被刪除的,要留存下來,所以會在每張表中添加刪除標識來避免數據真的被刪除

我們可以在刪除標識deleteFlag字段上添加注解@TableLogic(value = "0", delval = "1")

然后測試刪除,在刪除之前我們先插入一條數據

    @Test
    public void insert() {
        User user = new User();
        user.setUsername("cat");
        user.setPassword("777");
        user.setBirthday("2021-01-01");
        userMapper.insert(user);
        System.err.println(user.getId());
    }

結果如下

然后使用BaseMapper提供的刪除方法測試刪除

    @Test
    public void delete() {
        userMapper.deleteById(1435064337008955393L);
    }

結果如下

我們發現在刪除的時候更新人字段還是空的,這是因為邏輯刪除並沒有用到自動填充,所以我們自定義一個可以自動填充的刪除方法

在MyBaseMapper中添加自動填充屬性的刪除方法

    int myDeleteByIdWithFill(T t);

然后定義一個MyDeleteByIdWithFill方法類

package com.qjc.sqlInjector;

import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName: MyDeleteById
 * @Description: 填充屬性值的刪除
 * @Author: qjc
 * @Date: 2021/9/6 4:53 下午
 */
public class MyDeleteByIdWithFill extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql;
        SqlMethod sqlMethod;
        if (tableInfo.isLogicDelete()) {
            // 進行邏輯刪除
            List<TableFieldInfo> fieldInfos = getWithUpdateFillFields(tableInfo);
            // 刪除時自動填充需要填充的屬性值
            String sqlLogicSet = "SET " + fieldInfos.stream().map(i -> i.getSqlSet(null)).collect(Collectors.joining(EMPTY)) + tableInfo.getLogicDeleteSql(false, false);
            sql = String.format(
                    "<script>\nUPDATE %s %s WHERE %s=#{%s}\n</script>",
                    tableInfo.getTableName(),
                    sqlLogicSet,
                    tableInfo.getKeyColumn(),
                    tableInfo.getKeyProperty());
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
            return addUpdateMappedStatement(mapperClass, modelClass, "myDeleteByIdWithFill", sqlSource);
        } else {
            sqlMethod = SqlMethod.DELETE_BY_ID;
            sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),
                    tableInfo.getKeyProperty());
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
            return this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
        }
    }

    /**
     * 過濾出更新時進行填充信息的字段列表
     *
     * @param
     * @return
     * @author qjc
     * @date 2021/9/6 5:09 下午
     */
    private List<TableFieldInfo> getWithUpdateFillFields(TableInfo tableInfo) {
        return tableInfo.getFieldList().stream()
                .filter(TableFieldInfo::isWithUpdateFill)
                .collect(Collectors.toList())
                ;
    }
}

將該方法添加到MethodList中

public class MySqlInjector extends DefaultSqlInjector {


    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new MySelectList());
        methodList.add(new MyDeleteByIdWithFill());
        return methodList;
    }
}

然后再插入一條數據

    @Test
    public void insert() {
        User user = new User();
        user.setUsername("dog");
        user.setPassword("888");
        user.setBirthday("2021-02-01");
        userMapper.insert(user);
        System.err.println(user.getId());
    }

結果如下

測試自動填充屬性的刪除方法

    @Test
    public void myDeleteByIdWithFill() {
        User user = new User();
        user.setId(1435066199628091394L);
        userMapper.myDeleteByIdWithFill(user);
    }

結果如下

這時更新用戶信息就填充進去了

demo地址:https://gitee.com/xiaorenwu_dashije/mybatis-plus-demo.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM