【死磕Sharding-jdbc】---基于ssm

本篇文章讲解如何在ssm(spring、springmvc、mybatis)结构的程序上集成sharding-jdbc(版本为1.5.4.1)进行分库分表; 假设分库分表行为如下:

  • 将authuser表分到4个库(user0~user_3)中;
  • 其他表不进行分库分表,保留在default_db库中;

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,那么需要将坐标 <groupId>com.dangdang</groupId>修改为 <groupId>io.shardingjdbc</groupId>;另外,如果是yaml配置,那么需要将坐标 <artifactId>sharding-jdbc-config-spring</artifactId>修改为 <artifactId>sharding-jdbc-config-yaml</artifactId>

2. 配置数据源

spring-datasource.xml配置所有需要的数据源如下--authuser分库分表后需要的4个库user0~user3,以及不分库分表的默认库defaultdb:

<?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">    <!-- 配置数据源 -->    <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配置优化可以放在这里-->    </bean>    <!-- 配置数据源 -->    <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配置优化可以放在这里-->    </bean>    <!-- 配置数据源 -->    <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配置优化可以放在这里-->    </bean>    <!-- 配置数据源 -->    <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配置优化可以放在这里-->    </bean>    <!-- 配置数据源 -->    <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配置优化可以放在这里-->    </bean></beans>

properties配置文件内容如下:

sj_user_0.driver=com.mysql.jdbc.Driversj_user_0.url=jdbc:mysql://localhost:3306/user_0sj_user_0.username=rootsj_user_0.password=RootAfei_1sj_user_1.driver=com.mysql.jdbc.Driversj_user_1.url=jdbc:mysql://localhost:3306/user_1sj_user_1.username=rootsj_user_1.password=RootAfei_1sj_user_2.driver=com.mysql.jdbc.Driversj_user_2.url=jdbc:mysql://localhost:3306/user_2sj_user_2.username=rootsj_user_2.password=RootAfei_1sj_user_3.driver=com.mysql.jdbc.Driversj_user_3.url=jdbc:mysql://localhost:3306/user_3sj_user_3.username=rootsj_user_3.password=RootAfei_1sj_default.driver=com.mysql.jdbc.Driversj_default.url=jdbc:mysql://localhost:3306/default_dbsj_default.username=rootsj_default.password=RootAfei_1

3. 集成sharding数据源

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">    <!--数据库sharding策略:以id列进行sharding,sharding逻辑在AuthUserDatabaseShardingAlgorithm中-->    <rdb:strategy id="databaseStrategy" sharding-columns="id"                  algorithm-class="com.crt.fin.ospsso.service.shardingjdbc.AuthUserDatabaseShardingAlgorithm" />    <!--auth_user表sharding策略:无 -->    <!--定义sharding数据源-->    <rdb:data-source id="shardingDataSource">        <!--default-data-source指定默认数据源, 即没有在<rdb:table-rules>申明的logic-table表,        即不需要分库分表的表, 全部走默认数据源-->        <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只分库不分表, actual-tables的值一定要加上:sj_ds_${0..3}.,                否则会遍历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>

说明:spring-sharding.xml配置的分库分表规则:authuser表分到id为sjds${0..3}的四个库中,表名保持不变;其他表在id为sjds_default库中,不分库也不分表;集成sharding-jdbc的核心就是将SqlSessionFactoryBean需要的dataSource属性修改为 shardingDataSource,把数据源交给sharding-jdbc处理;

分库逻辑 AuthUserDatabaseShardingAlgorithm的代码很简单,源码如下:

/** * @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接口的实现即可,例如 ModuloDatabaseShardingAlgorithm.java

4. 注意事项

无法识别sharding-jdbc分库分表规则inline-expression问题,例如: <rdb:table-rulelogic-table="auth_user"actual-tables="sj_ds_${0..3}.auth_user"database-strategy="databaseStrategy"/>

  1. 根本原因: 根本原因是spring把 ${}当做占位符, ${0..3}这种表达式,spring会尝试去properties文件中找key为 0..3的属性。但是这里是sharding-jdbc分库分表规则的inline表达式,需要spring忽略这种行为。否则会抛出异常: java.lang.IllegalArgumentException: Could not resolve placeholder '0..3' in value "sjds${0..3}.auth_user"
  2. 解决办法: 配置: <propertyname="ignoreUnresolvablePlaceholders"value="true"/> 或者: <context:property-placeholder****** ignore-unresolvable="true"/>

5. Main测试

Main.java用来测试分库分表是否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有进行分库,        AuthUserMapper authUserMapper = context.getBean(AuthUserMapper.class);        AuthUser authUser = authUserMapper.selectByPrimaryKey(7L);        System.out.println("-----> The auth user: "+JSON.toJSONString(authUser));        // user_permission没有分库分表        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()的代码和没有分库分表的代码完全一样;

6. 遗留问题

Main方法测试,或者启动服务后的调用测试都没有问题,但是通过junit测试用例访问就会抛出异常,作为一个待解决的遗留问题:

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

END

原文发布于微信公众号 - Java技术驿站(chenssy89)

原文发表时间:2018-06-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Linux驱动

41.Linux应用调试-修改内核来打印用户态的oops

1.在之前第36章里,我们学习了通过驱动的oops定位错误代码行 第36章的oops代码如下所示: Unable to handle kernel paging...

2885
来自专栏一个会写诗的程序员的博客

Springboot Application 集成 OSGI 框架开发

是 Java 类加载层次中最顶层的类加载器,负责加载 JDK 中的核心类库,如:rt.jar、resources.jar、charsets.jar 等

1362
来自专栏测试开发架构之路

【总结】梳理下接口功能测试

1403
来自专栏JackieZheng

Spring实战——缓存

缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis…… 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓...

20310
来自专栏linux驱动个人学习

Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)

我们前面提到了, fork, vfork等复制出来的进程是父进程的一个副本, 那么如何我们想加载新的程序, 可以通过execve来加载和启动新的程序。

3543
来自专栏从零开始学自动化测试

python测试开发django-3.url配置

我们在浏览器访问一个网页是通过url地址去访问的,django管理url配置是在urls.py文件。当一个页面数据很多时候,通过会有翻页的情况,那么页数是不固定...

1123
来自专栏ImportSource

学会一个JVM插件:使用HSDIS反汇编JIT生成的代码

HSDIS是一个Java官方推荐 HotSpot虚拟机JIT编译代码的反汇编插件。我们有了这个插件后,通过JVM参数-XX:+PrintAssembly就可以加...

80211
来自专栏Android 研究

Retrofit解析2之使用简介

前面介绍完RESTful之后,我们先来初步认识下Retrofit的使用"姿势"。本文的主要内容如下:

5553
来自专栏奔跑的蛙牛技术博客

javaBean 简单理解JavaBean简单及使用

PO:persistant object持久对象,可以看成是与数据库中的表相映射的java对象。最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用P...

1504
来自专栏区块链

GoAHEAD嵌入式WEB SERVER漏洞概览

GOAHEAD由于开源、简单、轻巧、功能强大、多平台等特点,主要运用于嵌入式设备上。考虑到如路由器等等的使用场景等加上出现的漏洞情况等等,使得其漏洞利用难度也会...

2446

扫码关注云+社区

领取腾讯云代金券