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

相关文章

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

Python虚拟环境与Conda使用指南分享

《Python虚拟环境与Conda使用指南分享》:本文主要介绍Python虚拟环境与Conda使用指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、python 虚拟环境概述1.1 什么是虚拟环境1.2 为什么需要虚拟环境二、Python 内置的虚拟环境工具

Oracle修改端口号之后无法启动的解决方案

《Oracle修改端口号之后无法启动的解决方案》Oracle数据库更改端口后出现监听器无法启动的问题确实较为常见,但并非必然发生,这一问题通常源于​​配置错误或环境冲突​​,而非端口修改本身,以下是系... 目录一、问题根源分析​​​二、保姆级解决方案​​​​步骤1:修正监听器配置文件 (listener.

Linux中修改Apache HTTP Server(httpd)默认端口的完整指南

《Linux中修改ApacheHTTPServer(httpd)默认端口的完整指南》ApacheHTTPServer(简称httpd)是Linux系统中最常用的Web服务器之一,本文将详细介绍如何... 目录一、修改 httpd 默认端口的步骤1. 查找 httpd 配置文件路径2. 编辑配置文件3. 保存

Python处理大量Excel文件的十个技巧分享

《Python处理大量Excel文件的十个技巧分享》每天被大量Excel文件折磨的你看过来!这是一份Python程序员整理的实用技巧,不说废话,直接上干货,文章通过代码示例讲解的非常详细,需要的朋友可... 目录一、批量读取多个Excel文件二、选择性读取工作表和列三、自动调整格式和样式四、智能数据清洗五、

C++ HTTP框架推荐(特点及优势)

《C++HTTP框架推荐(特点及优势)》:本文主要介绍C++HTTP框架推荐的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Crow2. Drogon3. Pistache4. cpp-httplib5. Beast (Boos

JDK9到JDK21中值得掌握的29个实用特性分享

《JDK9到JDK21中值得掌握的29个实用特性分享》Java的演进节奏从JDK9开始显著加快,每半年一个新版本的发布节奏为Java带来了大量的新特性,本文整理了29个JDK9到JDK21中值得掌握的... 目录JDK 9 模块化与API增强1. 集合工厂方法:一行代码创建不可变集合2. 私有接口方法:接口

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

电脑系统Hosts文件原理和应用分享

《电脑系统Hosts文件原理和应用分享》Hosts是一个没有扩展名的系统文件,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应... Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应

Nginx 413修改上传文件大小限制的方法详解

《Nginx413修改上传文件大小限制的方法详解》在使用Nginx作为Web服务器时,有时会遇到客户端尝试上传大文件时返回​​413RequestEntityTooLarge​​... 目录1. 理解 ​​413 Request Entity Too Large​​ 错误2. 修改 Nginx 配置2.1