mybatisplus 的常用 CRUD 方法
众所周知,mybatisplus 提供了强大的代码生成能力,他默认生成的常用的 CRUD 方法(例如插入、更新、删除、查询等)的定义,能够帮助我们节省很多体力劳动。
他的BaseMapper
中定义了这些常用的 CRUD 方法,我们在使用时,继承这个BaseMapper
类就默认拥有了这些能力。
如果我们的业务中,需要类似的通用 Sql 时,该如何实现呢?
是每个 Mapper 中都定义一遍类似的 Sql 吗?
显然这是最笨的一种方法。
此时我们可以借助mybatisplus
这个成熟框架,来实现我们想要的通用 Sql。
扩展常用 CRUD 方法
新增一个通用 sql
比如有一个这样的需求,项目中所有表或某一些表,都要执行一个类似的查询,如`SelectByErp`,那么可以这样实现。(这是一个最简单的 sql 实现,使用时可以根据业务需求实现更为复杂的 sql:比如多租户系统自动增加租户 id 参数、分库分表系统增加分库分表字段条件判断)
定义一个SelectByErp
类,继承AbstractMethod
类,并实现injectMappedStatement
方法
定义 sql 方法名、sql 模板、实现 sql 的拼接组装
/**
* 新增一个通用sql
*/
public class SelectByErp extends AbstractMethod {
// 需要查询的列名
private final String erpColumn = "erp";
// sql方法名
private final String method = "selectByErp";
// sql模板
private final String sqlTemplate = "SELECT %s FROM %s WHERE %s=#{%s} %s";
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
// 获取需要查询的字段名及属性名
TableFieldInfo erpFiled = getErpProperty(tableInfo);
// 拼接组装sql
SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlTemplate,
sqlSelectColumns(tableInfo, false),
tableInfo.getTableName(),
erpFiled.getColumn(), erpFiled.getProperty(),
tableInfo.getLogicDeleteSql(true, false)), Object.class);
return this.addSelectMappedStatementForTable(mapperClass, method, sqlSource, tableInfo);
}
/**
* 查询erp列信息
*/
private TableFieldInfo getErpProperty(TableInfo tableInfo) {
List<TableFieldInfo> fieldList = tableInfo.getFieldList();
TableFieldInfo erpField = fieldList.stream().filter(filed -> filed.getColumn().equals(erpColumn)).findFirst().get();
return erpField;
}
复制代码
3.定义一个 sql 注入器GyhSqlInjector
,添加SelectByErp
对象
// 需注入到spring容器中
@Component
public class GyhSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 增加 SelectByErp对象,程序启动后自动加载
methodList.add(new SelectByErp());
return methodList;
}
}
复制代码
4.定义一个基础 MapperGyhBaseMapper
,添加selectByErp
方法
/**
* 自定义的通用Mapper
*/
public interface GyhBaseMapper<T> extends BaseMapper<T> {
List<T> selectByErp(String erp);
}
复制代码
5.应用中需要使用该SelectByErp
方法的表,都继承GyhBaseMapper
,那么这些表将都拥有了selectByErp
这个查询方法,程序启动后会自动为这些表生成该 sql。
public interface XXXMapper extends GyhBaseMapper<XXXTable>
复制代码
添加一个 mybatisplus 已有 sql
1.mybatisplus 常用 CRUD 方法如最上图,这些方法已经默认会自动生成,但 mybatisplus 其实提供了更多的方法,如下图,只要我们在启动时添加进去,就可以使用了。
2.比如我想使用AlwaysUpdateSomeColumnById
方法,该方法可以在更新时只更新我需要的字段,不进行全字段更新。添加步骤如下。
3.定义一个 sql 注入器 ,如GyhSqlInjector
,添加AlwaysUpdateSomeColumnById
对象
@Component
public class GyhSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 添加 AlwaysUpdateSomeColumnById 对象
methodList.add(new AlwaysUpdateSomeColumnById());
return methodList;
}
}
复制代码
4.定义一个基础 Mapper 如GyhBaseMapper
,添加alwaysUpdateSomeColumnById
方法
/**
* 自定义的通用Mapper
*/
public interface GyhBaseMapper<T> extends BaseMapper<T> {
int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity);
}
复制代码
5.继承GyhBaseMapper
的其他 Mapper,将自动拥有alwaysUpdateSomeColumnById
方法
/**
* 自定义的通用Mapper
*/
public interface GyhBaseMapper<T> extends BaseMapper<T> {
int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity);
}
复制代码
6.继承GyhBaseMapper
的其他 Mapper,将自动拥有alwaysUpdateSomeColumnById
方法
编辑一个 mybatisplus 已有 sql
1.如果想编辑一个 mybatisplus 已有 sql,比如分库分表系统,执行updateById
操作时,虽然主键 Id 已确定,但目标表不确定,此时可能导致该 sql 在多张表上执行,造成资源浪费,并且分库分表字段不可修改,默认的updateById
不能用,需要改造。以下以shardingsphere
分库分表为例。
2.定义一个UpdateByIdWithSharding
类,继承UpdateById
类
public class UpdateByIdWithSharding extends UpdateById {
private String columnDot = "`";
private YamlShardingRuleConfiguration yamlShardingRuleConfiguration;
// 注入shardingsphere的分库分表配置信息
public UpdateByIdWithSharding(YamlShardingRuleConfiguration yamlShardingRuleConfiguration) {
this.yamlShardingRuleConfiguration = yamlShardingRuleConfiguration;
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String tableName = tableInfo.getTableName();
// shardingsphere 分库分表配置信息
Map<String, YamlTableRuleConfiguration> tables = yamlShardingRuleConfiguration.getTables();
// 判断当前表是否设置了分表字段
if (tables.containsKey(tableName)) {
YamlTableRuleConfiguration tableRuleConfiguration = tables.get(tableName);
// 获取分表字段
String shardingColumn = tableRuleConfiguration.getTableStrategy().getStandard().getShardingColumn();
// 构建sql
boolean logicDelete = tableInfo.isLogicDelete();
SqlMethod sqlMethod = SqlMethod.UPDATE_BY_ID;
// 增加分表字段判断
String shardingAdditional = getShardingColumnWhere(tableInfo, shardingColumn);
// 是否判断逻辑删除字段
final String additional = optlockVersion() + tableInfo.getLogicDeleteSql(true, false);
shardingAdditional = shardingAdditional + additional;
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(),
getSqlSet(logicDelete, tableInfo, shardingColumn),
tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(),
shardingAdditional);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
} else {
return super.injectMappedStatement(mapperClass, modelClass, tableInfo);
}
}
/**
* where条件增加分表字段
*/
private String getShardingColumnWhere(TableInfo tableInfo, String shardingColumn) {
StringBuilder shardingWhere = new StringBuilder();
shardingWhere.append(" AND ").append(shardingColumn).append("=#{");
shardingWhere.append(ENTITY_DOT);
TableFieldInfo fieldInfo = tableInfo.getFieldList().stream()
.filter(f -> f.getColumn().replaceAll(columnDot, StringUtils.EMPTY).equals(shardingColumn))
.findFirst().get();
shardingWhere.append(fieldInfo.getEl());
shardingWhere.append("}");
return shardingWhere.toString();
}
/**
* set模块去掉分表字段
*/
public String getSqlSet(boolean ignoreLogicDelFiled, TableInfo tableInfo, String shardingColumn) {
List<TableFieldInfo> fieldList = tableInfo.getFieldList();
// 去掉分表字段的set设置,即不修改分表字段
String rmShardingColumnSet = fieldList.stream()
.filter(i -> ignoreLogicDelFiled ? !(tableInfo.isLogicDelete() && i.isLogicDelete()) : true)
.filter(i -> !i.getColumn().equals(shardingColumn))
.map(i -> i.getSqlSet(ENTITY_DOT))
.filter(Objects::nonNull).collect(joining(NEWLINE));
return rmShardingColumnSet;
}
}
复制代码
3.定义一个 sql 注入器GyhSqlInjector
,添加UpdateByIdWithSharding
对象
// 需注入到spring容器中
@Component
public class GyhSqlInjector extends DefaultSqlInjector {
/**
* shardingsphere 配置信息
*/
@Autowired
private YamlShardingRuleConfiguration yamlShardingRuleConfiguration;
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 添加 UpdateByIdWithSharding 对象,并注入分库分表信息
methodList.add(new UpdateByIdWithSharding(yamlShardingRuleConfiguration));
return methodList;
}
}
复制代码
4.定义一个基础 MapperGyhBaseMapper
,添加新的selectById
方法
/**
* 自定义的通用Mapper
*/
public interface GyhBaseMapper<T> extends BaseMapper<T> {
int updateById(@Param(Constants.ENTITY) T entity);
}
复制代码
5.所有参与分表的表,在定义 Mapper 时继承GyhBaseMapper
,那么在使用他的updateById
方法时,将自动增加分库分表判断,准确命中目标表,减少其他分表查询的资源浪费。
以上是针对mybatisplus
的一些简单改造,希望能为你提供一点点帮助~
作者:京东科技 郭艳红
来源:京东云开发者社区 转载请注明来源
评论