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

相关文章

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

MySQL8.2.0安装教程分享

《MySQL8.2.0安装教程分享》这篇文章详细介绍了如何在Windows系统上安装MySQL数据库软件,包括下载、安装、配置和设置环境变量的步骤... 目录mysql的安装图文1.python访问网址2javascript.点击3.进入Downloads向下滑动4.选择Community Server5.

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

MySQL修改密码的四种实现方式

《MySQL修改密码的四种实现方式》文章主要介绍了如何使用命令行工具修改MySQL密码,包括使用`setpassword`命令和`mysqladmin`命令,此外,还详细描述了忘记密码时的处理方法,包... 目录mysql修改密码四种方式一、set password命令二、使用mysqladmin三、修改u

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具

使用Python在Excel中插入、修改、提取和删除超链接

《使用Python在Excel中插入、修改、提取和删除超链接》超链接是Excel中的常用功能,通过点击超链接可以快速跳转到外部网站、本地文件或工作表中的特定单元格,有效提升数据访问的效率和用户体验,这... 目录引言使用工具python在Excel中插入超链接Python修改Excel中的超链接Python

10个Python自动化办公的脚本分享

《10个Python自动化办公的脚本分享》在日常办公中,我们常常会被繁琐、重复的任务占据大量时间,本文为大家分享了10个实用的Python自动化办公案例及源码,希望对大家有所帮助... 目录1. 批量处理 Excel 文件2. 自动发送邮件3. 批量重命名文件4. 数据清洗5. 生成 PPT6. 自动化测试

10个Python Excel自动化脚本分享

《10个PythonExcel自动化脚本分享》在数据处理和分析的过程中,Excel文件是我们日常工作中常见的格式,本文将分享10个实用的Excel自动化脚本,希望可以帮助大家更轻松地掌握这些技能... 目录1. Excel单元格批量填充2. 设置行高与列宽3. 根据条件删除行4. 创建新的Excel工作表5

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

Redis多种内存淘汰策略及配置技巧分享

《Redis多种内存淘汰策略及配置技巧分享》本文介绍了Redis内存满时的淘汰机制,包括内存淘汰机制的概念,Redis提供的8种淘汰策略(如noeviction、volatile-lru等)及其适用场... 目录前言一、什么是 Redis 的内存淘汰机制?二、Redis 内存淘汰策略1. pythonnoe