11. sharding-jdbc集成--基于ssm

阿飛Javaer躁愿,轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝来候!

本篇文章講解如何在ssm(spring营搅、springmvc剧防、mybatis)結(jié)構(gòu)的程序上集成sharding-jdbc(版本為1.5.4.1)進(jìn)行分庫(kù)分表峭拘;
假設(shè)分庫(kù)分表行為如下:

  • 將auth_user表分到4個(gè)庫(kù)(user_0~user_3)中鸡挠;
  • 其他表不進(jìn)行分庫(kù)分表拣展,保留在default_db庫(kù)中缔逛;

1. POM配置

以spring配置文件為例褐奴,新增如下POM配置:

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>1.5.4.1</version>
</dependency>

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>sharding-jdbc-config-spring</artifactId>
    <version>1.5.4.1</version>
</dependency>

此次集成sharding-jdbc以1.5.4.1版本為例辅搬,如果是2.x版本的sharding-jdbc脖旱,那么需要將坐標(biāo)<groupId>com.dangdang</groupId>修改為<groupId>io.shardingjdbc</groupId>;另外币旧,如果是yaml配置竿滨,那么需要將坐標(biāo)<artifactId>sharding-jdbc-config-spring</artifactId>修改為<artifactId>sharding-jdbc-config-yaml</artifactId>于游;

2. 配置數(shù)據(jù)源

spring-datasource.xml配置所有需要的數(shù)據(jù)源如下--auth_user分庫(kù)分表后需要的4個(gè)庫(kù)user_0~user_3倾剿,以及不分庫(kù)分表的默認(rèn)庫(kù)default_db:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置數(shù)據(jù)源 -->
    <bean id="sj_ds_0" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${sj_user_0.url}" />
        <property name="username" value="${sj_user_0.username}" />
        <property name="password" value="${sj_user_0.password}" />
        <!--druid配置優(yōu)化可以放在這里-->
    </bean>

    <!-- 配置數(shù)據(jù)源 -->
    <bean id="sj_ds_1" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${sj_user_1.url}" />
        <property name="username" value="${sj_user_1.username}" />
        <property name="password" value="${sj_user_1.password}" />
        <!--druid配置優(yōu)化可以放在這里-->
    </bean>

    <!-- 配置數(shù)據(jù)源 -->
    <bean id="sj_ds_2" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${sj_user_2.url}" />
        <property name="username" value="${sj_user_2.username}" />
        <property name="password" value="${sj_user_2.password}" />
        <!--druid配置優(yōu)化可以放在這里-->
    </bean>

    <!-- 配置數(shù)據(jù)源 -->
    <bean id="sj_ds_3" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${sj_user_3.url}" />
        <property name="username" value="${sj_user_3.username}" />
        <property name="password" value="${sj_user_3.password}" />
        <!--druid配置優(yōu)化可以放在這里-->
    </bean>

    <!-- 配置數(shù)據(jù)源 -->
    <bean id="sj_ds_default" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${sj_default.url}" />
        <property name="username" value="${sj_default.username}" />
        <property name="password" value="${sj_default.password}" />
        <!--druid配置優(yōu)化可以放在這里-->
    </bean>
</beans>

properties配置文件內(nèi)容如下:

sj_user_0.driver=com.mysql.jdbc.Driver
sj_user_0.url=jdbc:mysql://localhost:3306/user_0
sj_user_0.username=root
sj_user_0.password=RootAfei_1

sj_user_1.driver=com.mysql.jdbc.Driver
sj_user_1.url=jdbc:mysql://localhost:3306/user_1
sj_user_1.username=root
sj_user_1.password=RootAfei_1

sj_user_2.driver=com.mysql.jdbc.Driver
sj_user_2.url=jdbc:mysql://localhost:3306/user_2
sj_user_2.username=root
sj_user_2.password=RootAfei_1

sj_user_3.driver=com.mysql.jdbc.Driver
sj_user_3.url=jdbc:mysql://localhost:3306/user_3
sj_user_3.username=root
sj_user_3.password=RootAfei_1

sj_default.driver=com.mysql.jdbc.Driver
sj_default.url=jdbc:mysql://localhost:3306/default_db
sj_default.username=root
sj_default.password=RootAfei_1

3. 集成sharding數(shù)據(jù)源

spring-sharding.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:rdb="http://www.dangdang.com/schema/ddframe/rdb"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.dangdang.com/schema/ddframe/rdb
                        http://www.dangdang.com/schema/ddframe/rdb/rdb.xsd">

    <!--數(shù)據(jù)庫(kù)sharding策略:以id列進(jìn)行sharding芹缔,sharding邏輯在AuthUserDatabaseShardingAlgorithm中-->
    <rdb:strategy id="databaseStrategy" sharding-columns="id"
                  algorithm-class="com.crt.fin.ospsso.service.shardingjdbc.AuthUserDatabaseShardingAlgorithm" />
    <!--auth_user表sharding策略:無(wú) -->

    <!--定義sharding數(shù)據(jù)源-->
    <rdb:data-source id="shardingDataSource">
        <!--default-data-source指定默認(rèn)數(shù)據(jù)源, 即沒有在<rdb:table-rules>申明的logic-table表,
        即不需要分庫(kù)分表的表, 全部走默認(rèn)數(shù)據(jù)源-->
        <rdb:sharding-rule data-sources="sj_ds_0,sj_ds_1,sj_ds_2,sj_ds_3,sj_ds_default"
                           default-data-source="sj_ds_default">
            <rdb:table-rules>
                <!--auth_user只分庫(kù)不分表, actual-tables的值一定要加上:sj_ds_${0..3}.,
                否則會(huì)遍歷data-sources, 而sj_ds_default中并沒有auth_user表 -->
                <rdb:table-rule logic-table="auth_user" actual-tables="sj_ds_${0..3}.auth_user"
                                database-strategy="databaseStrategy"/>
            </rdb:table-rules>
            <rdb:default-database-strategy sharding-columns="none" algorithm-class="com.dangdang.ddframe.rdb.sharding.api.strategy.database.NoneDatabaseShardingAlgorithm"/>
            <rdb:default-table-strategy sharding-columns="none" algorithm-class="com.dangdang.ddframe.rdb.sharding.api.strategy.table.NoneTableShardingAlgorithm"/>
        </rdb:sharding-rule>
        <rdb:props>
            <prop key="sql.show">true</prop>
            <prop key="executor.size">2</prop>
        </rdb:props>
    </rdb:data-source>

    <!-- 配置sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!---datasource交給sharding-jdbc托管-->
        <property name="dataSource" ref="shardingDataSource"/>
        <property name="mapperLocations" value="classpath*:mybatis/*Mapper.xml"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.crt.fin.ospsso.dal.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

</beans>

說(shuō)明:spring-sharding.xml配置的分庫(kù)分表規(guī)則:auth_user表分到id為sj_ds_${0..3}的四個(gè)庫(kù)中,表名保持不變芝硬;其他表在id為sj_ds_default庫(kù)中轧房,不分庫(kù)也不分表;集成sharding-jdbc的核心就是將SqlSessionFactoryBean需要的dataSource屬性修改為shardingDataSource迟赃,把數(shù)據(jù)源交給sharding-jdbc處理捺氢;

分庫(kù)邏輯AuthUserDatabaseShardingAlgorithm的代碼很簡(jiǎn)單剪撬,源碼如下:

/**
 * @author wangzhenfei9
 * @version 1.0.0
 * @since 2018年02月08日
 */
public class AuthUserDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final int SHARDING_NUMBER = 4;

    @Override
    public String doEqualSharding(final Collection<String> availableTargetNames, final ShardingValue<Long> shardingValue) {
        for (String each : availableTargetNames) {
            if (each.endsWith(shardingValue.getValue() % SHARDING_NUMBER + "")) {
                logger.debug("the target database name: {}", each);
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }

    @Override
    public Collection<String> doInSharding(final Collection<String> availableTargetNames, final ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
        Collection<Long> values = shardingValue.getValues();
        for (Long value : values) {
            for (String each : availableTargetNames) {
                if (each.endsWith(value % SHARDING_NUMBER + "")) {
                    result.add(each);
                }
            }
        }
        return result;
    }

    @Override
    public Collection<String> doBetweenSharding(final Collection<String> availableTargetNames, final ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
        Range<Long> range = shardingValue.getValueRange();
        for (Long value = range.lowerEndpoint(); value <= range.upperEndpoint(); value++) {
            for (String each : availableTargetNames) {
                if (each.endsWith(value % SHARDING_NUMBER + "")) {
                    result.add(each);
                }
            }
        }
        return result;
    }
}

這段代碼參考sharding-jdbc源碼中DatabaseShardingAlgorithm.java接口的實(shí)現(xiàn)即可,例如ModuloDatabaseShardingAlgorithm.java梨水;

4. 注意事項(xiàng)

無(wú)法識(shí)別sharding-jdbc分庫(kù)分表規(guī)則inline-expression問題舅世,例如:
<rdb:table-rule logic-table="auth_user" actual-tables="sj_ds_${0..3}.auth_user" database-strategy="databaseStrategy"/>

  1. 根本原因:
    根本原因是spring把${}當(dāng)做占位符雏亚,${0..3}這種表達(dá)式摩钙,spring會(huì)嘗試去properties文件中找key為0..3的屬性胖笛。但是這里是sharding-jdbc分庫(kù)分表規(guī)則的inline表達(dá)式长踊,需要spring忽略這種行為。否則會(huì)拋出異常:
    java.lang.IllegalArgumentException: Could not resolve placeholder '0..3' in value "sj_ds_${0..3}.auth_user"

  2. 解決辦法:
    配置: <property name="ignoreUnresolvablePlaceholders" value="true"/>
    或者: <context:property-placeholder ****** ignore-unresolvable="true" />

5. Main測(cè)試

Main.java用來(lái)測(cè)試分庫(kù)分表是否OK,其源碼如下:

/**
 * @author wangzhenfei9
 * @version 1.0.0
 * @since 2018年02月08日
 */
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "/META-INF/spring/spring-*.xml");

        // auth_user有進(jìn)行分庫(kù)莉擒,
        AuthUserMapper authUserMapper = context.getBean(AuthUserMapper.class);
        AuthUser authUser = authUserMapper.selectByPrimaryKey(7L);
        System.out.println("-----> The auth user: "+JSON.toJSONString(authUser));

        // user_permission沒有分庫(kù)分表
        UserPermissionMapper userPermissionMapper = context.getBean(UserPermissionMapper.class);
        UserPermission userPermission = userPermissionMapper.selectPermissionByUsername("wangzhenfei", "FINANCE_WALLET");
        System.out.println("-----< The user permission: "+JSON.toJSONString(userPermission));
    }

}

AuthUserMapper.selectByPrimaryKey()和UserPermissionMapper.selectPermissionByUsername()的代碼和沒有分庫(kù)分表的代碼完全一樣涨冀;

6. 遺留問題

Main方法測(cè)試鹿鳖,或者啟動(dòng)服務(wù)后的調(diào)用測(cè)試都沒有問題翅帜,但是通過(guò)junit測(cè)試用例訪問就會(huì)拋出異常命满,作為一個(gè)待解決的遺留問題:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 6: sj_ds_0,sj_ds_1,sj_ds_2,sj_ds_3,sj_ds_default,shardingDataSource
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末歼疮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子缩麸,更是在濱河造成了極大的恐慌杭朱,老刑警劉巖弧械,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異唁桩,居然都是意外死亡耸棒,警方通過(guò)查閱死者的電腦和手機(jī)与殃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門幅疼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人悴晰,你說(shuō)我怎么就攤上這事≈鸸ぃ” “怎么了铡溪?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)泪喊。 經(jīng)常有香客問我棕硫,道長(zhǎng),這世上最難降的妖魔是什么袒啼? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任哈扮,我火速辦了婚禮纬纪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灶泵。我一直安慰自己育八,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布膳犹。 她就那樣靜靜地躺著,像睡著了一般钠惩。 火紅的嫁衣襯著肌膚如雪坦刀。 梳的紋絲不亂的頭發(fā)上沐寺,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼泳猬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拷呆,可吹牛的內(nèi)容都是我干的项秉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼唉侄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼候生!你這毒婦竟也來(lái)了须蜗?” 一聲冷哼從身側(cè)響起柿估,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤足陨,失蹤者是張志新(化名)和其女友劉穎镊讼,沒想到半個(gè)月后褥芒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颜及,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酌呆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片度迂。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡而姐,死狀恐怖政鼠,靈堂內(nèi)的尸體忽然破棺而出瞬雹,到底是詐尸還是另有隱情,我是刑警寧澤稍刀,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布拢锹,位于F島的核電站,受9級(jí)特大地震影響份企,放射性物質(zhì)發(fā)生泄漏钉鸯。R本人自食惡果不足惜钞脂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一狼钮、第九天 我趴在偏房一處隱蔽的房頂上張望剥悟。 院中可真熱鬧,春花似錦娱节、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窜觉。三九已至驶睦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間举瑰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搅窿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像脚乡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谓传,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)功偿,斷路器盆佣,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,806評(píng)論 6 342
  • 要加“m”說(shuō)明是MB,否則就是KB了. -Xms:初始值 -Xmx:最大值 -Xmn:最小值 java -Xms8...
    dadong0505閱讀 4,829評(píng)論 0 53
  • 《我不想多幾條命》 上帝給了它幾條命 蚯蚓不知道那一次死是最后一次 一分為二各自逃生 多年以后相見 它們知不知道它...
    吳寒署閱讀 174評(píng)論 2 0
  • 今天無(wú)意加了膘悍一只貓的公眾號(hào)征堪,并且一口氣看完了那100頁(yè)的文章瘩缆,看完后感覺豁然開朗,于是馬上注冊(cè)了簡(jiǎn)書佃蚜,想在簡(jiǎn)書...
    May_loving閱讀 144評(píng)論 0 0