首页 > 数据库 >手把手教你如何扩展(破解)mybatisplus的sql生成 | 京东云技术团队

手把手教你如何扩展(破解)mybatisplus的sql生成 | 京东云技术团队

时间:2023-11-10 11:37:52浏览次数:29  
标签:tableInfo mybatisplus String 手把手 mapperClass extends sql public

mybatisplus 的常用CRUD方法

众所周知,mybatisplus提供了强大的代码生成能力,他默认生成的常用的CRUD方法(例如插入、更新、删除、查询等)的定义,能够帮助我们节省很多体力劳动。

他的BaseMapper中定义了这些常用的CRUD方法,我们在使用时,继承这个BaseMapper类就默认拥有了这些能力。

手把手教你如何扩展(破解)mybatisplus的sql生成 | 京东云技术团队_sql

如果我们的业务中,需要类似的通用Sql时,该如何实现呢?

是每个Mapper中都定义一遍类似的Sql吗?

显然这是最笨的一种方法。

此时我们可以借助mybatisplus这个成熟框架,来实现我们想要的通用Sql。

扩展常用CRUD方法

新增一个通用sql

比如有一个这样的需求,项目中所有表或某一些表,都要执行一个类似的查询,如\SelectByErp\,那么可以这样实现。(这是一个最简单的sql实现,使用时可以根据业务需求实现更为复杂的sql:比如多租户系统自动增加租户id参数、分库分表系统增加分库分表字段条件判断)

  1. 定义一个SelectByErp类,继承AbstractMethod类,并实现injectMappedStatement方法
  2. 定义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其实提供了更多的方法,如下图,只要我们在启动时添加进去,就可以使用了。

手把手教你如何扩展(破解)mybatisplus的sql生成 | 京东云技术团队_List_02

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的一些简单改造,希望能为你提供一点点帮助~

作者:京东科技 郭艳红

来源:京东云开发者社区 转载请注明来源

标签:tableInfo,mybatisplus,String,手把手,mapperClass,extends,sql,public
From: https://blog.51cto.com/u_15714439/8293917

相关文章

  • windows系统上如何给mysql导入数据库和表
    1.连接数据库2.输入密码3.进入数据库4.创建数据库 createdatabase数据库名;5.进入数据库use  数据库名;6.查看当前所在数据库selectdatabase();7.把需要导入的数据库放到没有中文名的路径下面(蜜蜂这里放D盘了),之后使用SOURCE导入SOURCE数据库的位置/需要导入的数据库名称(中间......
  • 手把手pip安装教程
    手把手pip安装教程在Python中,pip是最常用的包管理工具之一。它可以用于安装、卸载和管理Python包。在本文中,我们将手把手教你如何安装pip,以便能够更方便地安装和管理Python包。1.Python版本在安装pip之前,我们需要确认已经正确安装了Python,并确定其版本。在命令行中输入以下命令,......
  • 【Qt初入江湖】Qt QSqlRelationalTableModel 底层架构、原理详细描述
    鱼弦:内容合伙人、新星导师、全栈领域创作新星创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen) QtQSqlRelationalTableModel是Qt中用于实现具有关联表格的模型类,它继承自QSqlTableModel。QSqlRelationalTable......
  • mysql基本使用
    MySQL常用图形化工具:NavicatSqlyogMysqlworkbend(msi自动安装) //////////////////////////////////////////////////////////Mysql数据库基本操作1、ddl数据定义语言对数据库的常用操作 l 查看所有的数据库:showdatabases;l 创建数据库:createdatabase[i......
  • Sql server 删除重复记录的SQL语句
     有两个意义上的重复记录:1.完全重复的记录,也即所有字段均重复的记录.2.部分关键字段重复的记录,比如Name字段重复,而其他字段不一定重复或都重复可以忽略。1、对于第一种重复,比较容易解决,使用selectdistinct*fromtableName就可以得到无重复记录的结果集。如果该表需要删除重复......
  • SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM之间
    由于数据库中DateTime类型字段,最小值是1/1/175312:00:00,而.NETFramework中,DateTime类型,最小值是1/1/00010:00:00,显然,超出了sql的值的最小值范围,导致数据溢出错误usingSystem.Data.SqlTypes;namespaceConsoleApplication1{classProgram{staticvoidM......
  • Oracle找出所有表字段中值包含中文并生成扩充字段的SQL脚本
     Oracle找出所有表字段中值包含中文并生成扩充字段的SQL脚本 背景后续计划将Oracle的某个库迁移到云上的达梦库,Oracle字符集为ZHS16GBK,达梦库字符集为UTF-8。我们知道,中文汉字在UTF8中一个汉字占3个字节,而在GBK中则是占2个字节,测试过程发现若字段中存有中文的行,有可能在达......
  • datax抽取mysql数据到hive报错:javax.net.ssl.SSLException: Connection reset
    datax抽取mysql数据报错:[INFO]2023-11-0912:35:14.090+0000-->2023-11-0920:35:13.492[0-0-0-reader]ERRORReaderRunner-ReaderrunnerReceivedExceptions:com.alibaba.datax.common.exception.DataXException:Code:[DBUtilErrorCode-07],Description:[......
  • Oracle怎样查看某个表的约束名和约束的字段名、plsql怎样不提交执行的update
    要查看某个表的约束名和约束的字段名,你可以使用以下的SQL查询:SELECTconstraint_name,column_nameFROMuser_cons_columnsWHEREtable_name='YourTableName';在这个查询中,'YourTableName'是你要查询的表名。这条SQL语句将返回指定表的约束名称和对应的字段名。如果你没有......
  • 远程连接 Mysql 失败的解决方法
    今天在虚拟机Ubuntu上折腾了一晚上mysql,然后试着用java连接,搞了很久都没成功,但是同学配好的Debian上却连接成功了,也就是说我的配置有问题。折腾了很久,最后还是通过理解异常信息来大致猜测。远程连接是输入mysql所在主机的IP和端口来确定主机的逻辑地址,再通过用户和密码来确定登......