一.持久层接口补充
1.Mapper 层选装件
选装件是 Mybatis-Plus 提供的一些扩展方法,它们位于 com.baomidou.mybatisplus.extension.injector.methods 包下。这些方法需要配合Sql 注入器使用,以扩展 Mapper 接口的功能。
使用这些选装件前,需要确保已经正确配置了 Sql 注入器。
选装件说明
1>alwaysUpdateSomeColumnById
int alwaysUpdateSomeColumnById(T entity);
源码:
package com.baomidou.mybatisplus.extension.injector.methods;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 com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;import java.util.function.Predicate;/*** 根据 ID 更新固定的那几个字段(但是不包含逻辑删除)** <p>* 自己的通用 mapper 如下使用:* <pre>* int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity);* </pre>* </p>** <p> 如何筛选字段参考请 {@link InsertBatchSomeColumn} 里面的注释 </p>** @author hubin* @since 2019-04-12*/
public class AlwaysUpdateSomeColumnById extends AbstractMethod {/*** 字段筛选条件*/@Setter@Accessors(chain = true)private Predicate<TableFieldInfo> predicate;/*** @param name 方法名* @param predicate 筛选条件* @since 3.5.0*/public AlwaysUpdateSomeColumnById(String name, Predicate<TableFieldInfo> predicate) {super(name);this.predicate = predicate;}public AlwaysUpdateSomeColumnById() {super("alwaysUpdateSomeColumnById");}/*** @param predicate 筛选条件*/public AlwaysUpdateSomeColumnById(Predicate<TableFieldInfo> predicate) {super("alwaysUpdateSomeColumnById");this.predicate = predicate;}@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {SqlMethod sqlMethod = SqlMethod.UPDATE_BY_ID;final String additional = optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true);String sqlSet = this.filterTableFieldInfo(tableInfo.getFieldList(), getPredicate(),i -> i.getSqlSet(true, ENTITY_DOT), NEWLINE);sqlSet = SqlScriptUtils.convertSet(sqlSet);String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet,tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional);SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);}private Predicate<TableFieldInfo> getPredicate() {Predicate<TableFieldInfo> noLogic = t -> !t.isLogicDelete();if (predicate != null) {return noLogic.and(predicate);}return noLogic;}}
功能:
这个方法用于在更新操作时,无论实体对象的某些字段是否有变化,都会强制更新这些字段。这在某些业务场景下非常有用,比如更新时间戳字段,确保每次更新操作都会更新该字段。
使用场景:
当你需要在每次更新记录时,都更新某些特定的字段(如更新时间、版本号等),即使这些字段在实体对象中没有变化。
2>insertBatchSomeColumn
int insertBatchSomeColumn(List<T> entityList);
源码:
package com.baomidou.mybatisplus.extension.injector.methods;import com.baomidou.mybatisplus.annotation.IdType;
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 com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;import java.util.List;
import java.util.function.Predicate;/*** 批量新增数据,自选字段 insert* <p> 不同的数据库支持度不一样!!! 只在 mysql 下测试过!!! 只在 mysql 下测试过!!! 只在 mysql 下测试过!!! </p>* <p> 除了主键是 <strong> 数据库自增的未测试 </strong> 外理论上都可以使用!!! </p>* <p> 如果你使用自增有报错或主键值无法回写到entity,就不要跑来问为什么了,因为我也不知道!!! </p>* <p>* 自己的通用 mapper 如下使用:* <pre>* int insertBatchSomeColumn(List<T> entityList);* </pre>* </p>** <li> 注意: 这是自选字段 insert !!,如果个别字段在 entity 里为 null 但是数据库中有配置默认值, insert 后数据库字段是为 null 而不是默认值 </li>** <p>* 常用的 {@link Predicate}:* </p>** <li> 例1: t -> !t.isLogicDelete() , 表示不要逻辑删除字段 </li>* <li> 例2: t -> !t.getProperty().equals("version") , 表示不要字段名为 version 的字段 </li>* <li> 例3: t -> t.getFieldFill() != FieldFill.UPDATE) , 表示不要填充策略为 UPDATE 的字段 </li>** @author miemie* @since 2018-11-29*/
public class InsertBatchSomeColumn extends AbstractMethod {/*** 字段筛选条件*/@Setter@Accessors(chain = true)private Predicate<TableFieldInfo> predicate;/*** 默认方法名*/public InsertBatchSomeColumn() {super("insertBatchSomeColumn");}/*** 默认方法名** @param predicate 字段筛选条件*/public InsertBatchSomeColumn(Predicate<TableFieldInfo> predicate) {super("insertBatchSomeColumn");this.predicate = predicate;}/*** @param name 方法名* @param predicate 字段筛选条件* @since 3.5.0*/public InsertBatchSomeColumn(String name, Predicate<TableFieldInfo> predicate) {super(name);this.predicate = predicate;}@SuppressWarnings("Duplicates")@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;SqlMethod sqlMethod = SqlMethod.INSERT_ONE;List<TableFieldInfo> fieldList = tableInfo.getFieldList();String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(true, null, false) +this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(true, ENTITY_DOT, false) +this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA);String keyProperty = null;String keyColumn = null;// 表包含主键处理逻辑,如果不包含主键当普通字段处理if (tableInfo.havePK()) {if (tableInfo.getIdType() == IdType.AUTO) {/* 自增主键 */keyGenerator = Jdbc3KeyGenerator.INSTANCE;keyProperty = tableInfo.getKeyProperty();// 去除转义符keyColumn = SqlInjectionUtils.removeEscapeCharacter(tableInfo.getKeyColumn());} else {if (null != tableInfo.getKeySequence()) {keyGenerator = TableInfoHelper.genKeyGenerator(this.methodName, tableInfo, builderAssistant);keyProperty = tableInfo.getKeyProperty();keyColumn = tableInfo.getKeyColumn();}}}String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);}}
功能:
这个方法用于批量插入实体对象,但只插入实体对象中指定的某些字段。这在需要批量插入数据,但又不希望插入所有字段时非常有用。
使用场景:
当你需要批量插入数据,并且希望只插入实体对象中的部分字段,以提高插入效率或保护敏感数据。
3>logicDeleteByIdWithFill
int logicDeleteByIdWithFill(T entity);
源码:
package com.baomidou.mybatisplus.extension.injector.methods;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 com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;import java.util.List;import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;/*** 根据 id 逻辑删除数据,并带字段填充功能* <p>注意入参是 entity !!! ,如果字段没有自动填充,就只是单纯的逻辑删除</p>* <p>* 自己的通用 mapper 如下使用:* <pre>* int deleteByIdWithFill(T entity);* </pre>* </p>** @author miemie* @since 2018-11-09* @deprecated 3.5.0 {@link com.baomidou.mybatisplus.core.injector.methods.DeleteById}*/
@Deprecated
public class LogicDeleteByIdWithFill extends AbstractMethod {public LogicDeleteByIdWithFill() {super("deleteByIdWithFill");}/*** @param name 方法名* @since 3.5.0*/public LogicDeleteByIdWithFill(String name) {super(name);}@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {String sql;SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;if (tableInfo.isWithLogicDelete()) {List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream().filter(TableFieldInfo::isWithUpdateFill).filter(f -> !f.isLogicDelete()).collect(toList());if (CollectionUtils.isNotEmpty(fieldInfos)) {String sqlSet = "SET " + fieldInfos.stream().map(i -> i.getSqlSet(EMPTY)).collect(joining(EMPTY))+ tableInfo.getLogicDeleteSql(false, false);sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true));} else {sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlLogicSet(tableInfo),tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),tableInfo.getLogicDeleteSql(true, true));}} else {sqlMethod = SqlMethod.DELETE_BY_ID;sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),tableInfo.getKeyProperty());}SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);}}
功能:
这个方法用于逻辑删除记录,并填充实体对象中的某些字段。逻辑删除意味着不是真正从数据库中删除记录,而是通过更新某个字段(如 deleted 字段)来标记记录已被删除。
使用场景:
当你需要实现逻辑删除功能,并且希望在删除操作时自动填充实体对象中的某些字段(如删除时间、删除人等)。
使用提示
- 在使用这些选装件之前,确保你的项目中已经正确配置了 Sql 注入器。
- 这些方法通常需要在 Mapper 接口中显式声明,以便 Mybatis-Plus 能够识别并生成相应的 SQL 语句。
- 每个选装件都有其特定的使用场景,根据业务需求选择合适的方法。
- 在实际使用中,可能需要结合实体对象的注解(如 @TableField、@TableLogic 等)来实现更复杂的功能。