Shardbatis开源框架源码的修改实践经验分享

2023-10-12 10:48

本文主要是介绍Shardbatis开源框架源码的修改实践经验分享,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Shardbatis开源框架源码按自身业务的改进

 

摘要

在研发过程中,我们遇到了单表数据量瓶颈问题,同时又不能增加数据库的费用,最后选择了分表技术来解决性能问题。在分表技术的调用过程中,我们有2种技术实现方案。第一种基于mybatis的plugin 插件自研发,一种是采用开源的shardbatis框架。在对比研究分析过程中,发现shardbatis的框架设计理念扩展性良好,对于团队开发有很好规范作用,同时采用配置设计理念,修改配置文件就能满足业务应用场景的需求,最后选择了shardbatis框架,在实际应用过程中发现shardbatis采用sqlparse框架解析SQL,对编写的MySQL的查询语句进行了修正,查询SQL分页需要特殊处理,同时parselist不能到类级别拦截,最后我们下载源码进行了改进,替换了sqlparse解析框架,覆盖了isParse方法,达到满足自己的一套业务需求的框架。

 

Shardbatis的设计原理

 

1.      容器启动加载mybatis-config.xml配置文件

2.      解析mybatis配置文件的同时加载mybatis拦截器接口的实现类,通过拦截器接口的setProperties 方法加载shard_config.xml参数,注册分表策略

3.      Intercept方法拦截,轮询所有的mapperid与分表策略中配置的mapperid比较,是否相等,相等表示需要进行分表,如果需要分表,就走分表代码执行逻辑,不分表直接调用invocation.proceed() 返回

4.      分表逻辑,读取StatementHandler sql 进行表替换,表的替换按照分表对应的具体策略进行替换,替换完成,重新绑定SQL,调用invocation.proceed() 返回

5.      业务分表策略必须实现此接口,才能被拦截器解析.

 

mybatis-config.xml

 

<?xml version="1.0"encoding="UTF-8" ?>

<!DOCTYPE configuration

        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <settings>

        <setting name="lazyLoadingEnabled"value="false" />

        <!-- 全局延迟加载 ,在启用延迟加载的同时,需要禁用"aggressiveLazyLoading" -->

        <setting name="aggressiveLazyLoading"value="false" />

        <!-- 这个设置项在用户手册当中的定义当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->

        <setting name="lazyLoadTriggerMethods"value="clone" />

        <!-- <setting name="defaultExecutorType"value="BATCH" /> -->

    </settings>

 

    <plugins>

        <plugin interceptor="com.google.code.shardbatis.plugin.ShardPlugin">

            <propertyname="shardingConfig" value="shard_config.xml"/>

        </plugin>

    </plugins>

<plugins>

</plugins>

 

shard_config.xml

 

<?xml version="1.0"encoding="UTF-8"?> 

<!DOCTYPE shardingConfigPUBLIC "-//shardbatis.googlecode.com//DTDShardbatis 2.0//EN" 

  "http://shardbatis.googlecode.com/dtd/shardbatis-config.dtd">

<shardingConfig>

    <!--parseList可选配置如果配置了parseList,只有在parseList范围内的sql才会被解析和修改,配置到dao -->

    <parseList>

        <value>com.xxx.member.dao.xxxEdt.insert</value>

    </parseList>

    <!-- 配置分表策略 -->

    <strategy tableName="t_crm_member"strategyClass="com.xxx.member.dao.UserIdStrategy" />

</shardingConfig>

 

代码分析如下:

com.google.code.shardbatis.plugin.ShardPlugin核心类

com.google.code.shardbatis.strategy.ShardStrategy  核心接口

 

@Intercepts( { @Signature(type = StatementHandler.class, method = "prepare",args = { Connection.class })})

publicclass ShardPlugin implements Interceptor {

     public Object intercept(Invocation invocation) throws Throwable{

              StatementHandler statementHandler =(StatementHandler) invocation.getTarget();

         MappedStatement mappedStatement = null;

         if (statementHandlerinstanceofRoutingStatementHandler) {

              StatementHandler delegate = (StatementHandler)ReflectionUtils

                       .getFieldValue(statementHandler, "delegate");

              mappedStatement = (MappedStatement)ReflectionUtils.getFieldValue(delegate, "mappedStatement");

         } else {

              mappedStatement = (MappedStatement)ReflectionUtils.getFieldValue(statementHandler, "mappedStatement");

         }

         String mapperId = mappedStatement.getId();        

         if (isShouldParse(mapperId)) {

              String sql = statementHandler.getBoundSql().getSql();

              if (log.isDebugEnabled()) {

                   log.debug("Original Sql ["+ mapperId+ "]:"+ sql);

              }

              Object params = statementHandler.getBoundSql().getParameterObject();

             

              SqlConverterFactory cf = SqlConverterFactory.getInstance();

              sql= cf.convert(sql,params, mapperId);

              if (log.isDebugEnabled()) {

                   log.debug("Converted Sql ["+ mapperId+ "]:"+ sql);

              }

              ReflectionUtils.setFieldValue(statementHandler

                       .getBoundSql(), "sql", sql);

         }

         returninvocation.proceed();

}

     publicObject plugin(Object target){

 

}

public void setProperties(Properties properties){

 

}

}

publicinterface ShardStrategy{

     /**

      * 得到实际表名

      * @param baseTableName 逻辑表名,一般是没有前缀或者是后缀的表名

      * @param params mybatis执行某个statement时使用的参数

      * @param mapperId mybatis配置的statement id

      * @return

      */

     String getTargetTableName(String baseTableName,Object params,String mapperId);

}

Shardbatis优化改进

1. SqlConverterFactory.convert 方法

源码:

SqlConverterFactory cf =SqlConverterFactory.getInstance();

sql = cf.convert(sql,params, mapperId);

 

重写convert方法:

sql = convert(sql, params, mapperId);

 

protected String convert(String sql,Object params, String mapperId) {

        ShardConfigHolder configFactory =ShardConfigHolder.getInstance();

        Map<String, ShardStrategy> strategyRegister=configFactory.getStrategyRegister();

        Iterator<String> iterators=strategyRegister.keySet().iterator();

        while(iterators.hasNext()){

            String tableName = iterators.next().toUpperCase();

            //获取分表策略并转换sql

            ShardStrategy strategy =configFactory.getStrategy(tableName);

            String shardTable =strategy.getTargetTableName(tableName, params, mapperId);

            sql =sql.replaceAll(jointRegex(tableName), jointShard(shardTable));

        }

        returnsql ;

    }

    /**

     * 生成匹配正则

     *

     * @param tabelName 表名

     * @return

     */

    private String jointRegex(String tabelName) {

        returnnew StringBuffer(REGEX_PREFIX).append(tabelName).append(REGEX_SUFIX).toString();

    }

    /**

     * 拼接分表名

     *

     * @param shardTable 分表名

     * @return

     */

    private String jointShard(String shardTable) {

        returnnew StringBuffer(SPACE).append(shardTable).append(SPACE).toString();

}

 

2.       ShardConfigHolder. isParseId方法

shardbatis框架配置必须要到方法,不能到类级别,而我们业务中都是类级别的,对源码进行了修改,满足自身的要求

 

源码:

<?xml version="1.0"encoding="UTF-8"?> 

<!DOCTYPE shardingConfigPUBLIC "-//shardbatis.googlecode.com//DTDShardbatis 2.0//EN" 

  "http://shardbatis.googlecode.com/dtd/shardbatis-config.dtd">

<shardingConfig>

    <!--parseList可选配置如果配置了parseList,只有在parseList范围内的sql才会被解析和修改,配置到dao -->

    <parseList>

        <value>com.xxx.member.dao.xxxEdt.insert</value>

    </parseList>

    <!-- 配置分表策略 -->

    <strategy tableName="t_crm_member"strategyClass="com.xxx.member.dao.UserIdStrategy" />

</shardingConfig>

修改:

<?xml version="1.0"encoding="UTF-8"?> 

<!DOCTYPE shardingConfigPUBLIC "-//shardbatis.googlecode.com//DTDShardbatis 2.0//EN" 

  "http://shardbatis.googlecode.com/dtd/shardbatis-config.dtd">

<shardingConfig>

    <!--parseList可选配置如果配置了parseList,只有在parseList范围内的sql才会被解析和修改,配置到dao -->

    <parseList>

        <value> com.xxx.member.dao.xxxEdt </value>

    </parseList>

    <!-- 配置分表策略 -->

    <strategy tableName="t_crm_member"strategyClass="com.xxx.member.dao.UserIdStrategy" />

</shardingConfig>

 

publicclass ShardConfigHolder {

publicboolean isParseId(String id) {

        id=id.substring(0,id.lastIndexOf("."));

        returnparseSet != null && parseSet.contains(id);

   }

}

 

最后,由于笔者水平有限,文章中如有不足之处,敬请读者斧正,文明交流,沟通分享。

 


这篇关于Shardbatis开源框架源码的修改实践经验分享的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/195326

相关文章

GSON框架下将百度天气JSON数据转JavaBean

《GSON框架下将百度天气JSON数据转JavaBean》这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言一、百度天气jsON1、请求参数2、返回参数3、属性映射二、GSON属性映射实战1、类对象映

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

解决若依微服务框架启动报错的问题

《解决若依微服务框架启动报错的问题》Invalidboundstatement错误通常由MyBatis映射文件未正确加载或Nacos配置未读取导致,需检查XML的namespace与方法ID是否匹配,... 目录ruoyi-system模块报错报错详情nacos文件目录总结ruoyi-systnGLNYpe

Linux从文件中提取特定内容的实用技巧分享

《Linux从文件中提取特定内容的实用技巧分享》在日常数据处理和配置文件管理中,我们经常需要从大型文件中提取特定内容,本文介绍的提取特定行技术正是这些高级操作的基础,以提取含有1的简单需求为例,我们可... 目录引言1、方法一:使用 grep 命令1.1 grep 命令基础1.2 命令详解1.3 高级用法2

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

OpenCV在Java中的完整集成指南分享

《OpenCV在Java中的完整集成指南分享》本文详解了在Java中集成OpenCV的方法,涵盖jar包导入、dll配置、JNI路径设置及跨平台兼容性处理,提供了图像处理、特征检测、实时视频分析等应用... 目录1. OpenCV简介与应用领域1.1 OpenCV的诞生与发展1.2 OpenCV的应用领域2

Python Web框架Flask、Streamlit、FastAPI示例详解

《PythonWeb框架Flask、Streamlit、FastAPI示例详解》本文对比分析了Flask、Streamlit和FastAPI三大PythonWeb框架:Flask轻量灵活适合传统应用... 目录概述Flask详解Flask简介安装和基础配置核心概念路由和视图模板系统数据库集成实际示例Stre

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是