您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
手把手教你如何扩展(破解)mybatisplus的sql生成
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
手把手教你如何扩展(破解)mybatisplus的sql生成
gy****
2023-11-02
IP归属:北京
5440浏览
## mybatisplus 的常用CRUD方法 众所周知,**mybatisplus**提供了强大的代码生成能力,他默认生成的常用的CRUD方法(例如插入、更新、删除、查询等)的定义,能够帮助我们节省很多体力劳动。 他的`BaseMapper`中定义了这些常用的CRUD方法,我们在使用时,继承这个`BaseMapper`类就默认拥有了这些能力。 ![BaseMapper.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-10-30-14-39398TFuaCselEW9ak.png) 如果我们的业务中,需要类似的通用Sql时,该如何实现呢? 是每个Mapper中都定义一遍类似的Sql吗? 显然这是最笨的一种方法。 此时我们可以借助`mybatisplus`这个成熟框架,来实现我们想要的通用Sql。 ## 扩展常用CRUD方法 ### 新增一个通用sql 比如有一个这样的需求,项目中所有表或某一些表,都要执行一个类似的查询,如`SelectByErp`,那么可以这样实现。(这是一个最简单的sql实现,使用时可以根据业务需求实现更为复杂的sql:比如多租户系统自动增加租户id参数、分库分表系统增加分库分表字段条件判断) 1. 定义一个`SelectByErp`类,继承`AbstractMethod` 类,并实现`injectMappedStatement`方法 2. 定义sql方法名、sql模板、实现sql的拼接组装 ```java /** * 新增一个通用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`对象 ```java // 需注入到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. 定义一个基础Mapper `GyhBaseMapper`,添加`selectByErp`方法 ```java /** * 自定义的通用Mapper */ public interface GyhBaseMapper<T> extends BaseMapper<T> { List<T> selectByErp(String erp); } ``` 5. 应用中需要使用该`SelectByErp`方法的表,都继承`GyhBaseMapper`,那么这些表将都拥有了`selectByErp`这个查询方法,程序启动后会自动为这些表生成该sql。 ```java public interface XXXMapper extends GyhBaseMapper<XXXTable> ``` ### 添加一个mybatisplus已有sql 1. mybatisplus 常用CRUD方法如最上图,这些方法已经默认会自动生成,但mybatisplus其实提供了更多的方法,如下图,只要我们在启动时添加进去,就可以使用了。 ![method.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-10-30-14-41AdvTQRyDl30JJwoG.png) 2. 比如我想使用`AlwaysUpdateSomeColumnById`方法,该方法可以在更新时只更新我需要的字段,不进行全字段更新。添加步骤如下。 3. 定义一个sql注入器 ,如`GyhSqlInjector`,添加`AlwaysUpdateSomeColumnById`对象 ```java @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`方法 ```java /** * 自定义的通用Mapper */ public interface GyhBaseMapper<T> extends BaseMapper<T> { int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity); } ``` 5. 继承`GyhBaseMapper`的其他Mapper,将自动拥有`alwaysUpdateSomeColumnById`方法 ### 编辑一个mybatisplus已有sql 1. 如果想编辑一个mybatisplus已有sql,比如分库分表系统,执行`updateById`操作时,虽然主键Id已确定,但目标表不确定,此时可能导致该sql在多张表上执行,造成资源浪费,并且分库分表字段不可修改,默认的`updateById`不能用,需要改造。以下以`shardingsphere`分库分表为例。 2. 定义一个`UpdateByIdWithSharding`类,继承`UpdateById` 类 ```java 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`对象 ```java // 需注入到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. 定义一个基础Mapper `GyhBaseMapper`,添加新的`selectById`方法 ```java /** * 自定义的通用Mapper */ public interface GyhBaseMapper<T> extends BaseMapper<T> { int updateById(@Param(Constants.ENTITY) T entity); } ``` 5. 所有参与分表的表,在定义Mapper时继承`GyhBaseMapper`,那么在使用他的`updateById`方法时,将自动增加分库分表判断,准确命中目标表,减少其他分表查询的资源浪费。 *** 以上是针对`mybatisplus`的一些简单改造,希望能为你提供一点点帮助~
上一篇:你还在为SFTP连接超时而困惑么?
下一篇:Java表达式引擎选型调研分析
gy****
文章数
4
阅读量
647
作者其他文章
01
Spring缓存是如何实现的?如何扩展使其支持过期删除功能?
前言:在我们的应用中,有一些数据是通过rpc获取的远端数据,该数据不会经常变化,允许客户端在本地缓存一定时间。该场景逻辑简单,缓存数据较小,不需要持久化,所以不希望引入其他第三方缓存工具加重应用负担,非常适合使用Spring Cache来实现。但有个问题是,我们希望将这些rpc结果数据缓存起来,并在一定时间后自动删除,以实现在一定时间后获取到最新数据。类似Redis的过期时间。接下来是我的调研步骤
01
spring多数据源动态切换的实现原理及读写分离的应用
简介AbstractRoutingDataSource是Spring框架中的一个抽象类,可以实现多数据源的动态切换和路由,以满足复杂的业务需求和提高系统的性能、可扩展性、灵活性。应用场景多租户支持:对于多租户的应用,根据当前租户来选择其对应的数据源,实现租户级别的隔离和数据存储。分库分表:为了提高性能和扩展性,将数据分散到多个数据库或表中,根据分片规则来选择正确的数据源,实现分库分表。读写分离:为
01
从源码层面深度剖析Spring循环依赖
以下举例皆针对单例模式讨论图解参考 https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce1、Spring 如何创建Bean?对于单例Bean来说,在Spring容器整个生命周期内,有且只有一个对象。Spring 在创建 Bean 过程中,使用到了三级缓存,即 DefaultSingletonBeanRegistry.java 中定
01
手把手教你如何扩展(破解)mybatisplus的sql生成
mybatisplus 的常用CRUD方法众所周知,mybatisplus提供了强大的代码生成能力,他默认生成的常用的CRUD方法(例如插入、更新、删除、查询等)的定义,能够帮助我们节省很多体力劳动。他的BaseMapper中定义了这些常用的CRUD方法,我们在使用时,继承这个BaseMapper类就默认拥有了这些能力。如果我们的业务中,需要类似的通用Sql时,该如何实现呢?是每个Mapper中都
gy****
文章数
4
阅读量
647
作者其他文章
01
Spring缓存是如何实现的?如何扩展使其支持过期删除功能?
01
spring多数据源动态切换的实现原理及读写分离的应用
01
从源码层面深度剖析Spring循环依赖
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号