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

相关文章

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

基于Flask框架添加多个AI模型的API并进行交互

《基于Flask框架添加多个AI模型的API并进行交互》:本文主要介绍如何基于Flask框架开发AI模型API管理系统,允许用户添加、删除不同AI模型的API密钥,感兴趣的可以了解下... 目录1. 概述2. 后端代码说明2.1 依赖库导入2.2 应用初始化2.3 API 存储字典2.4 路由函数2.5 应

Python GUI框架中的PyQt详解

《PythonGUI框架中的PyQt详解》PyQt是Python语言中最强大且广泛应用的GUI框架之一,基于Qt库的Python绑定实现,本文将深入解析PyQt的核心模块,并通过代码示例展示其应用场... 目录一、PyQt核心模块概览二、核心模块详解与示例1. QtCore - 核心基础模块2. QtWid

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Linux修改pip临时目录方法的详解

《Linux修改pip临时目录方法的详解》在Linux系统中,pip在安装Python包时会使用临时目录(TMPDIR),但默认的临时目录可能会受到存储空间不足或权限问题的影响,所以本文将详细介绍如何... 目录引言一、为什么要修改 pip 的临时目录?1. 解决存储空间不足的问题2. 解决权限问题3. 提

最新Spring Security实战教程之Spring Security安全框架指南

《最新SpringSecurity实战教程之SpringSecurity安全框架指南》SpringSecurity是Spring生态系统中的核心组件,提供认证、授权和防护机制,以保护应用免受各种安... 目录前言什么是Spring Security?同类框架对比Spring Security典型应用场景传统

Linux文件名修改方法大全

《Linux文件名修改方法大全》在Linux系统中,文件名修改是一个常见且重要的操作,文件名修改可以更好地管理文件和文件夹,使其更具可读性和有序性,本文将介绍三种在Linux系统下常用的文件名修改方法... 目录一、引言二、使用mv命令修改文件名三、使用rename命令修改文件名四、mv命令和rename命

Python解析器安装指南分享(Mac/Windows/Linux)

《Python解析器安装指南分享(Mac/Windows/Linux)》:本文主要介绍Python解析器安装指南(Mac/Windows/Linux),具有很好的参考价值,希望对大家有所帮助,如有... 目NMNkN录1js. 安装包下载1.1 python 下载官网2.核心安装方式3. MACOS 系统安

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory