sharding jdbc + mybatis +spring boot的分庫分表實現(xiàn)

一.sharding jdbc簡介(這里你可以不看)

首先,我要在這里先介紹一下sharding jdbc:
Sharding-JDBC定位為輕量級java框架豹悬,使用客戶端直連數(shù)據(jù)庫,以jar包形式提供服務(wù)酬核,未使用中間層庆捺,無需額外部署榨汤,無其他依賴军掂,DBA也無需改變原有的運維方式轮蜕,可理解為增強版的JDBC驅(qū)動,舊代碼遷移成本幾乎為零蝗锥。

它主要的功能:分庫分表跃洛;讀寫分離; 柔性事務(wù)终议;分布式主鍵汇竭;兼容性;靈活多樣的配置穴张;分布式治理能力 (2.0新功能)韩玩;

前兩個功能無需多說,柔性事務(wù)主要表現(xiàn)在:最大努力送達型事務(wù)陆馁,TCC型事務(wù)(TBD);分布式主鍵也不需要多說合愈,兼容性主要體現(xiàn)在:可適用于任何基于java的ORM框架叮贩,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC,可基于任何第三方的數(shù)據(jù)庫連接池,如:DBCP, C3P0, BoneCP, Druid等佛析,理論上可支持任意實現(xiàn)JDBC規(guī)范的數(shù)據(jù)庫益老。目前支持MySQL,Oracle寸莫,SQLServer和PostgreSQL捺萌;靈活多樣的配置主要支持這些配置Java,YAML膘茎,Inline表達式桃纯,Spring命名空間酷誓,Spring boot starter;分布式治理能力 : 配置集中化與動態(tài)化态坦,可支持數(shù)據(jù)源盐数、表與分片策略的動態(tài)切換(2.0.0.M1), 客戶端的數(shù)據(jù)庫治理伞梯,數(shù)據(jù)源失效自動切換(2.0.0.M2)玫氢, 基于Open Tracing協(xié)議的APM信息輸出(2.0.0.M3),分布式治理能力我也沒有嘗試過谜诫。

從sharding jdbc的官網(wǎng)文檔中下載的demo,基本上都不能跑起來漾峡,而它的基本文檔大部分都粘貼的是代碼片段,雖然能理解它的意思喻旷,但是很難將這些代碼拼接起來生逸,我自己做的時候,也踩了很多坑掰邢,在網(wǎng)上搜索的例子很多都是基于當當網(wǎng)的sharding jdbc牺陶,基本上版本是到了1.5(在我使用的時候),我當時在使用的時候遇到一些問題辣之,想加官網(wǎng)群掰伸,發(fā)現(xiàn)加不進去,就去找群主怀估,加群主狮鸭,并且詢問為什么不讓加討論群,后來我才知道那個群主是張亮(當當網(wǎng)架構(gòu)師多搀,負責sharding jdbc的大牛)歧蕉,還好別人沒有理我,太唐突了康铭,后面自己也都把這些問題解決了惯退。不過據(jù)說當當網(wǎng)的sharding jdbc抽離出來了,由Apache負責从藤,所以:我的依賴從dangdang的

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

變?yōu)榱耍?/p>

<dependency>
        <groupId>io.shardingjdbc</groupId>
        <artifactId>sharding-jdbc-core</artifactId>
        <version>2.0.3</version>
</dependency>

我當當網(wǎng)的sharding使用的版本比較低催跪,遇到了一些問題,文章末尾會分享出來夷野。

二.開始動手(這是正文)

廢話已經(jīng)說了那么多了懊蒸,現(xiàn)在進入正題吧,如何進行sharding jdbc的分庫分表悯搔,如有錯誤的地方骑丸,歡迎指正。
我使用的工具:編譯器:IntelliJ IDEA; mysql管理工具:workbench;

1.我們先建立數(shù)據(jù)庫和表(分別建了兩個庫兩張表):

CREATE DATABASE `user_0` /*!40100 DEFAULT CHARACTER SET utf8 */;

CREATE TABLE `user_info_1` (
  `user_id` bigint(19) NOT NULL,
  `user_name` varchar(45) DEFAULT NULL,
  `account` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `user_info_0` (
  `user_id` bigint(19) NOT NULL,
  `user_name` varchar(45) DEFAULT NULL,
  `account` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE DATABASE `user_1` /*!40100 DEFAULT CHARACTER SET utf8 */;

CREATE TABLE `user_info_1` (
  `user_id` bigint(19) NOT NULL,
  `user_name` varchar(45) DEFAULT NULL,
  `account` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `user_info_0` (
  `user_id` bigint(19) NOT NULL,
  `user_name` varchar(45) DEFAULT NULL,
  `account` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.開始我們項目的建立,首先明確一點的通危,我們添加配置的順序要有先后铸豁,思路要清晰,不然在你出錯的時候黄鳍,你都不知道朝哪個方向去思考推姻,我們是spring boot+mybatis+sharding jdbc,我們首先是一個spring boot的項目,所以我們首先建立一個spring boot項目框沟,你可以從https://start.spring.io這個網(wǎng)址去創(chuàng)建一個spring boot項目藏古,也可以從idea編譯器上:File->new->project

創(chuàng)建項目.PNG

最后生成的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--sharding-jdbc -->
        <!--<dependency>-->
            <!--<groupId>com.dangdang</groupId>-->
            <!--<artifactId>sharding-jdbc-core</artifactId>-->
            <!--<version>1.3.3</version>-->
        <!--</dependency>-->
        <dependency>
            <groupId>io.shardingjdbc</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>2.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

其中,spring-boot-devtools忍燥,commons-dbcp拧晕,druid,我感覺都是不需要的梅垄,我完成項目后也沒對這些項目的依賴進行過濾厂捞。
這個時候,你可以測試一下队丝,你的spring boot項目能否正常啟動靡馁,可以,你就進入到下一步的配置當中机久。
3.mybatis的配置
首先臭墨,看看我的源碼的目錄結(jié)構(gòu):


目錄結(jié)構(gòu).PNG

我創(chuàng)建一個entity包(實體包),里面有一個UserInfo的類:

public class UserInfo {
    /**
     *
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database column user_info.user_id
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    private Long userId;

    /**
     *
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database column user_info.user_name
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    private String userName;

    /**
     *
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database column user_info.account
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    private String account;

    /**
     *
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database column user_info.password
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    private String password;

    /**
     * This method was generated by MyBatis Generator.
     * This method returns the value of the database column user_info.user_id
     *
     * @return the value of user_info.user_id
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public Long getUserId() {
        return userId;
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method sets the value of the database column user_info.user_id
     *
     * @param userId the value for user_info.user_id
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public void setUserId(Long userId) {
        this.userId = userId;
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method returns the value of the database column user_info.user_name
     *
     * @return the value of user_info.user_name
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public String getUserName() {
        return userName;
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method sets the value of the database column user_info.user_name
     *
     * @param userName the value for user_info.user_name
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public void setUserName(String userName) {
        this.userName = userName == null ? null : userName.trim();
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method returns the value of the database column user_info.account
     *
     * @return the value of user_info.account
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public String getAccount() {
        return account;
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method sets the value of the database column user_info.account
     *
     * @param account the value for user_info.account
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public void setAccount(String account) {
        this.account = account == null ? null : account.trim();
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method returns the value of the database column user_info.password
     *
     * @return the value of user_info.password
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public String getPassword() {
        return password;
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method sets the value of the database column user_info.password
     *
     * @param password the value for user_info.password
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }
}

然后我有一個mapper包(映射包),里面有一個映射類UserInfoMapper

import com.example.demo.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserInfoMapper {
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table user_info
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    int insert(UserInfo record);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table user_info
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    int insertSelective(UserInfo record);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table user_info
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    UserInfo selectByPrimaryKey(Long userId);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table user_info
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    int updateByPrimaryKeySelective(UserInfo record);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table user_info
     *
     * @mbg.generated Tue Mar 13 23:47:19 CST 2018
     */
    int updateByPrimaryKey(UserInfo record);
}

還有再resources目錄下的mapper目錄中的UserInfoMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserInfoMapper">
  <resultMap id="BaseResultMap" type="com.example.demo.entity.UserInfo">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Mar 13 23:47:19 CST 2018.
    -->
    <id column="user_id" jdbcType="BIGINT" property="userId" />
    <result column="user_name" jdbcType="VARCHAR" property="userName" />
    <result column="account" jdbcType="VARCHAR" property="account" />
    <result column="password" jdbcType="VARCHAR" property="password" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Mar 13 23:47:19 CST 2018.
    -->
    user_id, user_name, account, password
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Mar 13 23:47:19 CST 2018.
    -->
    select 
    <include refid="Base_Column_List" />
    from user_info
    where user_id = #{userId,jdbcType=BIGINT}
  </select>
  <insert id="insert" parameterType="com.example.demo.entity.UserInfo">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Mar 13 23:47:19 CST 2018.
    -->
    insert into user_info (user_id, user_name, account, 
      password)
    values (#{userId,jdbcType=BIGINT}, #{userName,jdbcType=VARCHAR}, #{account,jdbcType=VARCHAR}, 
      #{password,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="com.example.demo.entity.UserInfo">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Mar 13 23:47:19 CST 2018.
    -->
    insert into user_info
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        user_id,
      </if>
      <if test="userName != null">
        user_name,
      </if>
      <if test="account != null">
        account,
      </if>
      <if test="password != null">
        password,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        #{userId,jdbcType=BIGINT},
      </if>
      <if test="userName != null">
        #{userName,jdbcType=VARCHAR},
      </if>
      <if test="account != null">
        #{account,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        #{password,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.example.demo.entity.UserInfo">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Mar 13 23:47:19 CST 2018.
    -->
    update user_info
    <set>
      <if test="userName != null">
        user_name = #{userName,jdbcType=VARCHAR},
      </if>
      <if test="account != null">
        account = #{account,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        password = #{password,jdbcType=VARCHAR},
      </if>
    </set>
    where user_id = #{userId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.example.demo.entity.UserInfo">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Mar 13 23:47:19 CST 2018.
    -->
    update user_info
    set user_name = #{userName,jdbcType=VARCHAR},
      account = #{account,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR}
    where user_id = #{userId,jdbcType=BIGINT}
  </update>
</mapper>

按道理說,這樣的配置應(yīng)該就可以了膘盖,你寫個測試代碼試一試胧弛,能不能進行增刪查改,應(yīng)該是可以的侠畔,可我運氣比較差结缚,居然不行,所以我又加了一個mybatis-config.xml的配置软棺,我不知道你們運行的結(jié)果怎么樣红竭,但是現(xiàn)在如果出問題,一定在mybatis上喘落,所以問題搜索的范圍就相對只有這一個模塊

<?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>
    <!--<environments default="development">-->
    <!--<environment id="development">-->
    <!--<transactionManager type="JDBC"/>-->
    <!--<dataSource type="POOLED">-->
    <!--<property name="driver" value="${driver-class-name}"/>-->
    <!--<property name="url" value="${url}"/>-->
    <!--<property name="username" value="${username}"/>-->
    <!--<property name="password" value="${password}"/>-->
    <!--</dataSource>-->
    <!--</environment>-->
    <!--</environments>-->
    <typeAliases>
        <package name="com.example.demo.entity"/>
    </typeAliases>
    <mappers>
        <mapper resource="mapper/UserInfoMapper.xml"/>
    </mappers>
</configuration>

4.sharding jdbc的配置
當你mybatis調(diào)整好了的時候德崭,這個時候就該加sharding jdbc的配置了,接下如果出問題揖盘,應(yīng)該先朝sharding jdbc的方向去考慮.
從目錄結(jié)構(gòu)中我們可以看到,我有一個config包锌奴,我把我的配置都寫在這里面的兽狭,首先,我們先實現(xiàn)我們的分庫分表的策略
分庫策略的類,DemoDatabaseShardingAlgorithm

package com.example.demo.config;

import com.google.common.collect.Range;
import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;

import java.util.Collection;
import java.util.LinkedHashSet;
public class DemoDatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
        for (String each : collection) {
            if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2+"")) {
                return each;
            }
        }
        throw new IllegalArgumentException();
    }


    //public class DemoDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {
//
//    @Override
//    public String doEqualSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {
//
//        for (String each : databaseNames) {
//            if (each.endsWith(Long.parseLong(shardingValue.getValue().toString()) % 2 + "")) {
//                return each;
//            }
//        }
//        throw new IllegalArgumentException();
//    }
//
//    @Override
//    public Collection<String> doInSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {
//        Collection<String> result = new LinkedHashSet<>(databaseNames.size());
//        for (Long value : shardingValue.getValues()) {
//            for (String tableName : databaseNames) {
//                if (tableName.endsWith(value % 2 + "")) {
//                    result.add(tableName);
//                }
//            }
//        }
//        return result;
//    }
//
//    @Override
//    public Collection<String> doBetweenSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {
//        Collection<String> result = new LinkedHashSet<>(databaseNames.size());
//        Range<Long> range = (Range<Long>) shardingValue.getValueRange();
//        for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
//            for (String each : databaseNames) {
//                if (each.endsWith(i % 2 + "")) {
//                    result.add(each);
//                }
//            }
//        }
//        return result;
//    }
}

使用io.shardingjdbc箕慧,就應(yīng)該實現(xiàn)PreciseShardingAlgorithm接口服球,然后實現(xiàn)doSharding方法,對應(yīng)SQL中的=, IN颠焦,還有RangeShardingAlgorithm接口中斩熊,對應(yīng)SQL中的BETWEEN AND,因為我只需要=伐庭,in操作粉渠,所以只實現(xiàn)了PreciseShardingAlgorithm接口,你如果都需要圾另,你可以都實現(xiàn)(千萬不要忽略了一個類可以實現(xiàn)多個接口)霸株。
如果你使用的當當網(wǎng)的sharding jdbc,那么你需要實現(xiàn)SingleKeyDatabaseShardingAlgorithm這個接口集乔,實現(xiàn)其中的三個方法去件,我注釋到的部分就是原來我用當當網(wǎng)的sharding jdbc的實現(xiàn)。
分表策略的類扰路,DemoTableShardingAlgorithm

package com.example.demo.config;

import com.google.common.collect.Range;
import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;

import java.util.Collection;
import java.util.LinkedHashSet;

//public class DemoTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {
public class DemoTableShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
        for (String each : collection) {
            if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2+"")) {
                return each;
            }
        }
        throw new IllegalArgumentException();
    }


    //    private static Long timeNode1 = 13L;
//
//    /**
//     * select * from t_order where user_id = 11尤溜;類似這個意思
//     * @param tableNames
//     * @param shardingValue
//     * @return
//     */
//    @Override
//    public String doEqualSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
//        for (String each : tableNames) {
//            Long currentTime = shardingValue.getValue()>>23;
//            if (currentTime<=timeNode1){
//                if (each.endsWith(shardingValue.getValue() % 2 + "")) {
//                    return each;
//                }
//            }else {
//                if (each.endsWith(shardingValue.getValue() % 2 + "_1")) {
//                    return each;
//                }
//            }
//        }
//        throw new IllegalArgumentException();
//
//    }
//
//    /**
//     * where user_id in (1,23,7)
//     * @param tableNames
//     * @param shardingValue
//     * @return
//     */
//    @Override
//    public Collection<String> doInSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
//        Collection<String> result = new LinkedHashSet<>(tableNames.size());
//        for (Long value : shardingValue.getValues()) {
//            for (String tableName : tableNames) {
//                if (tableName.endsWith(value % 2 + "")) {
//                    result.add(tableName);
//                }
//            }
//        }
//        return result;
//
//    }
//
//    /**
//     * where user_id between(1, 6)
//     *
//     * @param tableNames
//     * @param shardingValue
//     * @return
//     */
//    @Override
//    public Collection<String> doBetweenSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
//        Collection<String> result = new LinkedHashSet<>(tableNames.size());
//        Range<Long> range = (Range<Long>) shardingValue.getValueRange();
//        for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
//            for (String each : tableNames) {
//                if (each.endsWith(i % 2 + "")) {
//                    result.add(each);
//                }
//            }
//        }
//        return result;
//
//    }
}

與分庫的步驟一致,也是需要實現(xiàn)PreciseShardingAlgorithm和RangeShardingAlgorithm兩個接口的類汗唱。
剩下的就是最重要的部分宫莱,sharding jdbc的配置:
DataSourceConfig:

package com.example.demo.config;


import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration;
import io.shardingjdbc.core.jdbc.core.datasource.ShardingDataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class DataSourceConfig {

    /**
     * 配置分庫分表策略
     * 
     * @return
     * @throws SQLException
     */
    @Bean(name = "shardingDataSource")
    DataSource getShardingDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig;
        shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration());
        shardingRuleConfig.getBindingTableGroups().add("user_info");
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", DemoDatabaseShardingAlgorithm.class.getName()));
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", DemoTableShardingAlgorithm.class.getName()));
        return new ShardingDataSource(shardingRuleConfig.build(createDataSourceMap()));
    }

  
    /**
     * 設(shè)置表的node
     * @return
     */
    @Bean
    TableRuleConfiguration getUserTableRuleConfiguration() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
        orderTableRuleConfig.setLogicTable("user_info");
        orderTableRuleConfig.setActualDataNodes("user_${0..1}.user_info_${0..1}");
        orderTableRuleConfig.setKeyGeneratorColumnName("user_id");
        return orderTableRuleConfig;
    }
    

    /**
     * 需要手動配置事務(wù)管理器
     *
     * @param shardingDataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {
        return new DataSourceTransactionManager(shardingDataSource);
    }

    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory(DataSource shardingDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(shardingDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return bean.getObject();
    }

    @Bean
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    private Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>();
        result.put("user_0", createDataSource("user"));
        result.put("user_1", createDataSource("user_1"));
        return result;
    }

    private DataSource createDataSource(final String dataSourceName) {
        BasicDataSource result = new BasicDataSource();
        result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
        result.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));
        result.setUsername("root");
        result.setPassword("123456");
        return result;
    }
}

當你遇到一個問題:意思差不多是,需要一個數(shù)據(jù)源渡嚣,但是發(fā)現(xiàn)好幾個梢睛,你可以在
getShardingDataSource()這個方法上添加注解:@Primary,設(shè)置默認數(shù)據(jù)源
還有一個重中之重的部分识椰,在Applicatian這個啟動類中:加上注解
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
@EnableTransactionManagement(proxyTargetClass = true)
主要是為了防止代碼的自動配置

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
@EnableTransactionManagement(proxyTargetClass = true)
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

5.測試
我寫了一個測試服務(wù)和一個測試類
測試服務(wù):DemoService

package com.example.demo.service;


import com.example.demo.entity.UserInfo;
import com.example.demo.mapper.UserInfoMapper;
import groovy.util.logging.Slf4j;
import io.shardingjdbc.core.api.HintManager;
import io.shardingjdbc.core.hint.HintManagerHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Slf4j
@Service
public class DemoService {

    @Resource
    UserInfoMapper userInfoMapper;

    public static Long userId = 150L;

    public void demo() {
        System.out.println("Insert--------------");

        for (int i = 1; i <= 10; i++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUserId(userId);
            userInfo.setAccount("Account" + i);
            userInfo.setPassword("pass" + i);
            userInfo.setUserName("name" + i);
            userId++;
            if(i==3){
                HintManagerHolder.clear();
                HintManager hintManager = HintManager.getInstance();
                hintManager.addDatabaseShardingValue("user_info", "user_id", 3L);
                hintManager.addTableShardingValue("user_info", "user_id", 3L);
                System.out.println(userId);
            }
            userInfoMapper.insert(userInfo);
//

//


        }
        System.out.println("over..........");
    }
}

測試類DemoApplicationTests

package com.example.demo;

import com.example.demo.entity.UserInfo;
import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.service.DemoService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @Resource
    UserInfoMapper userInfoMaper;

    @Resource
    DemoService demoService;

    @Test
    public void contextLoads() {

        demoService.demo();


    }

}

三.遇到的問題

1.首先绝葡,我試著用xml的形式來配置,但是一直沒有成功腹鹉,好像是sharding jdbc對xml的支持不是很好藏畅,老點的版本是這樣,但是新版的應(yīng)該有很大的改善功咒;
2.在我進行測試的時候,使用的當當網(wǎng)1.3版本的sharding jdbc愉阎,一直提示我在sql語句的帶上分片鍵的值,我確實是帶上的力奋,我的代碼我確定沒有問題榜旦,網(wǎng)上也沒搜到答案,我就根據(jù)報錯debug sharding jdbc的源碼景殷,經(jīng)過一天的努力溅呢,我發(fā)現(xiàn)問題了
:是因為我使用的sharding jdbc版本不是1.5以上的版本澡屡,所以不是sharding jdbc自帶的SQL解析引擎,所以處理SQL的時候使用druid的parameter屬性進行SQL中的參數(shù)存儲咐旧,而在高版本的druid中驶鹉,將parameter替換成inputParameter,而我使用的sharding jdbc仍是使用的parmeter取出參數(shù)铣墨,所以一直找不到分片鍵值室埋,升級sharding版本或者降低druid版本就可以了
看圖你們就懂了:

sharding-jdbc問題.PNG

sharding-jdbc問題1.PNG

這是兩個新版本和舊版本的druid中setParameters方法的源碼,看出不同了吧伊约,就是因為這個姚淆,我的測試一直報錯。
世上無難事碱妆,只要肯攀登肉盹。
我的愿望是:代碼無bug
我的代碼的github地址:https://github.com/DragonMat/sharding-jdbc-demo.git

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市疹尾,隨后出現(xiàn)的幾起案子上忍,更是在濱河造成了極大的恐慌,老刑警劉巖纳本,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窍蓝,死亡現(xiàn)場離奇詭異,居然都是意外死亡繁成,警方通過查閱死者的電腦和手機吓笙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巾腕,“玉大人面睛,你說我怎么就攤上這事∽鸢幔” “怎么了叁鉴?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長佛寿。 經(jīng)常有香客問我幌墓,道長,這世上最難降的妖魔是什么冀泻? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任常侣,我火速辦了婚禮,結(jié)果婚禮上弹渔,老公的妹妹穿的比我還像新娘胳施。我一直安慰自己,他們只是感情好肢专,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布巾乳。 她就那樣靜靜地躺著您没,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胆绊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天欧募,我揣著相機與錄音压状,去河邊找鬼。 笑死跟继,一個胖子當著我的面吹牛种冬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舔糖,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼娱两,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了金吗?” 一聲冷哼從身側(cè)響起十兢,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎摇庙,沒想到半個月后旱物,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡卫袒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年宵呛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夕凝。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡宝穗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出码秉,到底是詐尸還是另有隱情逮矛,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布泡徙,位于F島的核電站橱鹏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏堪藐。R本人自食惡果不足惜莉兰,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望礁竞。 院中可真熱鬧糖荒,春花似錦、人聲如沸模捂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至综看,卻和暖如春品腹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背红碑。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工舞吭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人析珊。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓羡鸥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忠寻。 傳聞我的和親對象是個殘疾皇子惧浴,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

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

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,848評論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)奕剃,斷路器衷旅,智...
    卡卡羅2017閱讀 134,699評論 18 139
  • 《Spring Boot開發(fā):從0到1》 大綱結(jié)構(gòu)v2.0 第一部分Spring Boot基礎(chǔ) 第1章 Sprin...
    光劍書架上的書閱讀 10,965評論 1 70
  • 你從80樓往下看, 全是美景 祭饭,但你從2樓往下看 芜茵, 全是垃圾 。 人若沒有高度倡蝙, 看到的都是問題九串, 人若沒有格局...
    河北邢臺閱讀 363評論 0 0
  • 親愛的女兒, 最近我們一同參加了頂上托福百人千詞營活動寺鸥≈砼ィ活動第一天,你非常認真的背誦60個單詞胆建,并把生詞逐一抄下來...
    Syneysun閱讀 223評論 0 1