SpringBoot Mybatis Mycat 多租戶數據庫實現

0x01: Mycat

下載

wget http://dl.mycat.io/1.6.7.3/20190927161129/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz

配置

server.xml,Mycat 服務器配置,默認端口 8066

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
    <system>
        <property >0</property>
        <property >0</property>
        <property >2</property>
        <property >0</property>
        <property >1</property>
        <property >1m</property>
        <property >1k</property>
        <property >0</property>
        <property >384m</property>
        <property >true</property>
    </system>

    <!--Mycat用戶名-->
    <user >
        <!--Mycat密碼-->
        <property >root</property>
        <!--Mycat數據庫名-->
        <property >mycat_test</property>
    </user>

</mycat:server>

schema.xml,Mycat 和 Mysql 節點映射配置

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
    <!--Mycat數據庫名-->
    <schema >
        <!--Mycat表名,節點名稱列表-->
        <table />
    </schema>
    <!--Mycat節點名稱、節點地址、mysql數據庫名-->
    <dataNode  />
    <dataNode  />

    <!--Mycat節點地址-->
    <dataHost 
              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <!-- mysql數據庫服務器、賬戶、密碼 -->
        <writeHost host="hostM1" url="192.168.1.71:3306" user="test"
                   password="test@1234">
        </writeHost>
    </dataHost>
</mycat:schema>

0x02:Spring Boot

數據源配置

#mycat連接信息
spring.datasource.url=jdbc:mysql://localhost:8066/mycat_test?serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

#mybatis攔截器配置
mybatis.config-location=classpath:mybatis.xml

Mybatis

mybatis.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  />
    </settings>

    <typeAliases>
        <typeAlias alias="TestPojo" type="xx.xx.TestPojo"/>
    </typeAliases>

        <!-- 攔截器插件,改寫sql -->
    <plugins>
        <plugin interceptor="xx.interceptor.MyInterceptor">
        </plugin>
    </plugins>

</configuration>

攔截器

//攔截StatementHandler的prepare方法
@Intercepts(value = {
        @Signature(type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class,Integer.class})})
public class MyInterceptor implements Interceptor {
    // 修改sql,添加前後綴
    private static final String preState="/*!mycat:datanode=";
    private static final String afterState="*/";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler=(StatementHandler)invocation.getTarget();
        MetaObject metaStatementHandler=SystemMetaObject.forObject(statementHandler);
        Object object=null;
        //分離代理對象鏈
        while(metaStatementHandler.hasGetter("h")){
            object=metaStatementHandler.getValue("h");
            metaStatementHandler=SystemMetaObject.forObject(object);
        }

        //獲取sql
        String sql=(String)metaStatementHandler.getValue("delegate.boundSql.sql");

        //根據會話上下文,獲取節點標識
        String node=(String) SessionUtil.getSession().getAttribute("appId");
        if(node!=null) {
            //重寫sql,適配mycat
            sql = preState + node + afterState + sql;
        }

        System.out.println("sql is "+sql);
        metaStatementHandler.setValue("delegate.boundSql.sql",sql);
        Object result = invocation.proceed();
        System.out.println("Invocation.proceed()");
        return result;
    }

    // 返回當前攔截的對象(StatementHandler)的動態代理
    // 當攔截對象的方法被執行時, 動態代理中執行攔截器intercept方法.
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        String prop1 = properties.getProperty("prop1");
        String prop2 = properties.getProperty("prop2");
        System.out.println(prop1 + "------" + prop2);
    }
}

總結

以上爲關鍵實現,主要過程如下:

注意

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Z9AeQAhfwl2UlHXi2PlZnA