欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作

 更新時(shí)間:2022年08月10日 14:43:55   作者:jothan_lee  
這篇文章主要介紹了springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下

1.前言

業(yè)務(wù)飛速發(fā)展導(dǎo)致了數(shù)據(jù)規(guī)模的急速膨脹,單機(jī)數(shù)據(jù)庫已經(jīng)無法適應(yīng)互聯(lián)網(wǎng)業(yè)務(wù)的發(fā)展。由于MySQL采用 B+樹索引,數(shù)據(jù)量超過閾值時(shí),索引深度的增加也將使得磁盤訪問的 IO 次數(shù)增加,進(jìn)而導(dǎo)致查詢性能的下降;高并發(fā)訪問請(qǐng)求也使得集中式數(shù)據(jù)庫成為系統(tǒng)的最大瓶頸。我們團(tuán)隊(duì)結(jié)合公司業(yè)務(wù)背景商議最終選定一份表的形式進(jìn)行解決這一瓶頸問題,由我作為主要開發(fā)主導(dǎo)完成,故寫篇博客記錄沉淀。

2.MyBatis 允許使用插件來攔截的方法

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

總體概括為:

  • 攔截執(zhí)行器的方法
  • 攔截參數(shù)的處理
  • 攔截結(jié)果集的處理
  • 攔截Sql語法構(gòu)建的處理

這4各方法在MyBatis的一個(gè)操作(新增,刪除,修改,查詢)中都會(huì)被執(zhí)行到,執(zhí)行的先后順序是Executor,ParameterHandler,ResultSetHandler,StatementHandler。

3、Interceptor接口

package org.apache.ibatis.plugin;
import java.util.Properties;
public interface Interceptor {
//intercept方法就是要進(jìn)行攔截的時(shí)候要執(zhí)行的方法。
  Object intercept(Invocation invocation) throws Throwable;
//plugin方法是攔截器用于封裝目標(biāo)對(duì)象的,通過該方法我們可以返回目標(biāo)對(duì)象本身,也可以返回一個(gè)它的代理。當(dāng)返回的是代理的時(shí)候我們可以對(duì)其中的方法進(jìn)行攔截來調(diào)用intercept方法,當(dāng)然也可以調(diào)用其他方法。
  Object plugin(Object target);
//setProperties方法是用于在Mybatis配置文件中指定一些屬性的。
  void setProperties(Properties properties);

}  

 4分表實(shí)現(xiàn)

4.1、大體思路

分表的表結(jié)構(gòu)已經(jīng)預(yù)設(shè)完畢,所以現(xiàn)在我們只需要在進(jìn)行增刪改查的時(shí)候直接一次鎖定目標(biāo)表,然后替換目標(biāo)sql。

4.2、逐步實(shí)現(xiàn)

4.2.1 Mybatis如何找到我們新增的攔截服務(wù)

對(duì)于攔截器Mybatis為我們提供了一個(gè)Interceptor接口,前面有提到,通過實(shí)現(xiàn)該接口就可以定義我們自己的攔截器。自定義的攔截器需要交給Mybatis管理,這樣才能使得Mybatis的執(zhí)行與攔截器的執(zhí)行結(jié)合在一起,即,利用springboot把自定義攔截器注入。

package com.shinemo.insurance.common.config;
 
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class TableShardConfig {

    /**
     * 注冊(cè)插件
     */
    @Bean
    public Interceptor tableShardInterceptor() {
        return new TableShardInterceptor();
    }

}

4.2.2 應(yīng)該攔截什么樣的對(duì)象

因?yàn)閿r截器是全局?jǐn)r截的,我們只需要攔截我們需要攔截的mapper,故需要用注解進(jìn)行標(biāo)識(shí)

package com.shinemo.insurance.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = { ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TableShard {

    // 表前綴名
    String tableNamePrefix();

    // 值
    String value() default "";

    // 是否是字段名,如果是需要解析請(qǐng)求參數(shù)改字段名的值(默認(rèn)否)
    boolean fieldFlag() default false;

}

我們只需要把這個(gè)注解標(biāo)識(shí)在我們要攔截的mapper上

@Mapper
@TableShard(tableNamePrefix = "t_insurance_video_people_", value = "deviceId", fieldFlag = true)
public interface InsuranceVideoPeopleMapper {
 
//VideoPeople對(duì)象中包含deviceId字段
  int insert(VideoPeople videoPeople);
}

4.2.3 實(shí)現(xiàn)自定義攔截器

package com.shinemo.insurance.common.config;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Map;

import com.shinemo.insurance.common.annotation.TableShard;
import com.shinemo.insurance.common.util.HashUtil;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.SystemMetaObject;
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class,
                                                                                    Integer.class }) })
public class TableShardInterceptor implements Interceptor {
    private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        // MetaObject是mybatis里面提供的一個(gè)工具類,類似反射的效果
        MetaObject metaObject = getMetaObject(invocation);
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        MappedStatement mappedStatement = (MappedStatement) metaObject
            .getValue("delegate.mappedStatement");

        // 獲取Mapper執(zhí)行方法
        Method method = invocation.getMethod();

        // 獲取分表注解
        TableShard tableShard = getTableShard(method, mappedStatement);

        // 如果method與class都沒有TableShard注解或執(zhí)行方法不存在,執(zhí)行下一個(gè)插件邏輯
        if (tableShard == null) {
            return invocation.proceed();
        }

        //獲取值,此值就是拿的注解上value值,注解上value設(shè)定的值,并在傳入對(duì)象中獲取,根據(jù)業(yè)務(wù)可以選擇適當(dāng)?shù)闹导纯桑疫x取此值的目的是同一臺(tái)設(shè)備的值存入一張表中,有hash沖突的值也存在一張表中
        String value = tableShard.value();
        //value是否字段名,如果是,需要解析請(qǐng)求參數(shù)字段名的值
        boolean fieldFlag = tableShard.fieldFlag();

        if (fieldFlag) {
            //獲取請(qǐng)求參數(shù)
            Object parameterObject = boundSql.getParameterObject();

            if (parameterObject instanceof MapperMethod.ParamMap) {
                // ParamMap類型邏輯處理
                MapperMethod.ParamMap parameterMap = (MapperMethod.ParamMap) parameterObject;
                // 根據(jù)字段名獲取參數(shù)值
                Object valueObject = parameterMap.get(value);
                if (valueObject == null) {
                    throw new RuntimeException(String.format("入?yún)⒆侄?s無匹配", value));
                }
                //替換sql
                replaceSql(tableShard, valueObject, metaObject, boundSql);

            } else {
                // 單參數(shù)邏輯

                //如果是基礎(chǔ)類型拋出異常
                if (isBaseType(parameterObject)) {
                    throw new RuntimeException("單參數(shù)非法,請(qǐng)使用@Param注解");
                }

                if (parameterObject instanceof Map) {
                    Map<String, Object> parameterMap = (Map<String, Object>) parameterObject;
                    Object valueObject = parameterMap.get(value);
                    //替換sql
                    replaceSql(tableShard, valueObject, metaObject, boundSql);
                } else {
                    //非基礎(chǔ)類型對(duì)象
                    Class<?> parameterObjectClass = parameterObject.getClass();
                    Field declaredField = parameterObjectClass.getDeclaredField(value);
                    declaredField.setAccessible(true);
                    Object valueObject = declaredField.get(parameterObject);
                    //替換sql
                    replaceSql(tableShard, valueObject, metaObject, boundSql);
                }
            }

        } else {//無需處理parameterField
            //替換sql
            replaceSql(tableShard, value, metaObject, boundSql);
        }
        //把原有的簡(jiǎn)單查詢語句替換為分表查詢語句了,現(xiàn)在是時(shí)候?qū)⒊绦虻目刂茩?quán)交還給Mybatis下一個(gè)攔截器處理
        return invocation.proceed();
    }

    /**
     * @description:
     * @param target
     * @return: Object
     */
    @Override
    public Object plugin(Object target) {
        // 當(dāng)目標(biāo)類是StatementHandler類型時(shí),才包裝目標(biāo)類,否者直接返回目標(biāo)本身, 減少目標(biāo)被代理的次數(shù)
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    /**
     * @description: 基本數(shù)據(jù)類型驗(yàn)證,true是,false否
     * @param object
     * @return: boolean
     */
    private boolean isBaseType(Object object) {
        if (object.getClass().isPrimitive() || object instanceof String || object instanceof Integer
            || object instanceof Double || object instanceof Float || object instanceof Long
            || object instanceof Boolean || object instanceof Byte || object instanceof Short) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * @description: 替換sql
     * @param tableShard 分表注解
     * @param value      值
     * @param metaObject mybatis反射對(duì)象
     * @param boundSql   sql信息對(duì)象
     * @return: void
     */
    private void replaceSql(TableShard tableShard, Object value, MetaObject metaObject,
                            BoundSql boundSql) {
        String tableNamePrefix = tableShard.tableNamePrefix();
        //        // 獲取策略class
        //        Class<? extends ITableShardStrategy> strategyClazz = tableShard.shardStrategy();
        //        // 從spring ioc容器獲取策略類
        //        ITableShardStrategy tableShardStrategy = SpringBeanUtil.getBean(strategyClazz);
        // 生成分表名
        String shardTableName = generateTableName(tableNamePrefix, (String) value);
        // 獲取sql
        String sql = boundSql.getSql();
        // 完成表名替換
        metaObject.setValue("delegate.boundSql.sql",
            sql.replaceAll(tableNamePrefix, shardTableName));
    }

    /**
     * 生成表名
     *
     * @param tableNamePrefix 表名前綴
     * @param value           價(jià)值
     * @return {@link String}
     */
    private String generateTableName(String tableNamePrefix, String value) {

//我們分了1024張表
        int prime = 1024;
//hash取模運(yùn)算過后,鎖定目標(biāo)表
        int rotatingHash = HashUtil.rotatingHash(value, prime);
        return tableNamePrefix + rotatingHash;
    }
    /**
     * @description: 獲取MetaObject對(duì)象-mybatis里面提供的一個(gè)工具類,類似反射的效果
     * @param invocation
     * @return: MetaObject
     */
    private MetaObject getMetaObject(Invocation invocation) {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // MetaObject是mybatis里面提供的一個(gè)工具類,類似反射的效果
        MetaObject metaObject = MetaObject.forObject(statementHandler,
            SystemMetaObject.DEFAULT_OBJECT_FACTORY,
            SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);

        return metaObject;
    }
    /**
     * @description: 獲取分表注解
     * @param method
     * @param mappedStatement
     * @return: TableShard
     */
    private TableShard getTableShard(Method method,
                                     MappedStatement mappedStatement) throws ClassNotFoundException {
        String id = mappedStatement.getId();
        // 獲取Class
        final String className = id.substring(0, id.lastIndexOf("."));
        // 分表注解
        TableShard tableShard = null;
        // 獲取Mapper執(zhí)行方法的TableShard注解
        tableShard = method.getAnnotation(TableShard.class);
        // 如果方法沒有設(shè)置注解,從Mapper接口上面獲取TableShard注解
        if (tableShard == null) {
            // 獲取TableShard注解
            tableShard = Class.forName(className).getAnnotation(TableShard.class);
        }
        return tableShard;
    }
}

到此這篇關(guān)于springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作的文章就介紹到這了,更多相關(guān)springboot mybatis水平分表操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring中的Sentinel熔斷降級(jí)原理詳解

    Spring中的Sentinel熔斷降級(jí)原理詳解

    這篇文章主要介紹了Spring中的Sentinel熔斷降級(jí)原理詳解,熔斷是為了起到保護(hù)作用,如果某個(gè)目標(biāo)服務(wù)調(diào)用比較慢或者大量的超時(shí),這個(gè)時(shí)候如果觸發(fā)熔斷機(jī)制,則可以保證后續(xù)的請(qǐng)求不會(huì)繼續(xù)發(fā)送到目標(biāo)服務(wù)上,而是直接返回降級(jí)的邏輯并且快速釋放資源,需要的朋友可以參考下
    2023-09-09
  • Java?PTA?計(jì)算3到7位?水仙花數(shù)實(shí)例

    Java?PTA?計(jì)算3到7位?水仙花數(shù)實(shí)例

    這篇文章主要介紹了Java?PTA?計(jì)算3到7位?水仙花數(shù)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Windows下Java環(huán)境配置的超詳細(xì)教程

    Windows下Java環(huán)境配置的超詳細(xì)教程

    這篇文章主要給大家介紹了關(guān)于Windows下Java環(huán)境配置的超詳細(xì)教程,文中通過圖文將配置的過程介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-01-01
  • 去掉IntelliJ IDEA 中 mybatis 對(duì)應(yīng)的 xml 文件警告的教程圖解

    去掉IntelliJ IDEA 中 mybatis 對(duì)應(yīng)的 xml 文件警告的教程圖解

    本文通過圖文并茂的形式給大家介紹了去掉IntelliJ IDEA 中 mybatis 對(duì)應(yīng)的 xml 文件警告的教程,需要的朋友可以參考下
    2018-06-06
  • SpringBoot實(shí)現(xiàn)動(dòng)態(tài)配置及項(xiàng)目打包部署上線功能

    SpringBoot實(shí)現(xiàn)動(dòng)態(tài)配置及項(xiàng)目打包部署上線功能

    本文講解的是如何使用Spring動(dòng)態(tài)配置文件,實(shí)現(xiàn)不同環(huán)境不同配置,靈活切換配置文件;以及講述了如何使用?Maven?打包,然后上傳至Linux服務(wù)器進(jìn)行部署,對(duì)SpringBoot打包部署上線過程感興趣的朋友一起看看吧
    2022-10-10
  • 詳解java注解相關(guān)知識(shí)

    詳解java注解相關(guān)知識(shí)

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識(shí),文章圍繞著java注解的使用展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • 計(jì)算機(jī)編程語言發(fā)展史

    計(jì)算機(jī)編程語言發(fā)展史

    這篇文章主要介紹了Java計(jì)算機(jī)編程語言發(fā)展史,編程語言?可以簡(jiǎn)單的理解為一種計(jì)算機(jī)和人都能識(shí)別的語言。一種計(jì)算機(jī)語言讓程序員能夠準(zhǔn)確地定義計(jì)算機(jī)所需要使用的數(shù)據(jù),并精確地定義在不同情況下所應(yīng)當(dāng)采取的行動(dòng),下面詳細(xì)內(nèi)容,需要的小伙伴可以參考一下
    2022-01-01
  • SpringBoot3.x版本與Mybatis-Plus不兼容問題

    SpringBoot3.x版本與Mybatis-Plus不兼容問題

    當(dāng)使用3.x版本的SpringBoot結(jié)合Mybatis-Plus時(shí)版本不兼容就會(huì)報(bào)錯(cuò),本文就來介紹一下這個(gè)問題的解決方法,感興趣的可以了解一下
    2024-03-03
  • Java實(shí)現(xiàn)規(guī)則幾何圖形的繪制與周長(zhǎng)面積計(jì)算詳解

    Java實(shí)現(xiàn)規(guī)則幾何圖形的繪制與周長(zhǎng)面積計(jì)算詳解

    隨著計(jì)算機(jī)的發(fā)展,人們對(duì)圖形的計(jì)算要求會(huì)越來越高。在各行各業(yè)中的計(jì)算人員會(huì)對(duì)圖形的計(jì)算要有便利的要求,規(guī)則幾何圖形問題求解程序應(yīng)運(yùn)而生!本文將用Java編寫一個(gè)程序,可以實(shí)現(xiàn)規(guī)則幾何圖形的繪制與周長(zhǎng)面積計(jì)算,感興趣的可以了解一下
    2022-07-07
  • @TransactionalEventListener的使用和實(shí)現(xiàn)原理分析

    @TransactionalEventListener的使用和實(shí)現(xiàn)原理分析

    這篇文章主要介紹了@TransactionalEventListener的使用和實(shí)現(xiàn)原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論