工作中常用的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