sharding jdbc + mybatis +spring boot的分库分表实现

作者:1994_老叶

链接:https://www.jianshu.com/p/3b2ab87b0de7

一.sharding jdbc简介(这里你可以不看)

首先,我要在这里先介绍一下sharding jdbc:\ Sharding-JDBC定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,未使用中间层,无需额外部署,无其他依赖,DBA也无需改变原有的运维方式,可理解为增强版的JDBC驱动,旧代码迁移成本几乎为零。

它主要的功能:分库分表;读写分离;柔性事务;分布式主键;兼容性;灵活多样的配置;分布式治理能力 (2.0新功能);

前两个功能无需多说,柔性事务主要表现在:最大努力送达型事务,TCC型事务(TBD);分布式主键也不需要多说,兼容性主要体现在:可适用于任何基于java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC,可基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid等,理论上可支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL;灵活多样的配置主要支持这些配置Java,YAML,Inline表达式,Spring命名空间,Spring boot starter;分布式治理能力 :配置集中化与动态化,可支持数据源、表与分片策略的动态切换(2.0.0.M1), 客户端的数据库治理,数据源失效自动切换(2.0.0.M2), 基于Open Tracing协议的APM信息输出(2.0.0.M3),分布式治理能力我也没有尝试过。

从sharding jdbc的官网文档中下载的demo,基本上都不能跑起来,而它的基本文档大部分都粘贴的是代码片段,虽然能理解它的意思,但是很难将这些代码拼接起来,我自己做的时候,也踩了很多坑,在网上搜索的例子很多都是基于当当网的sharding jdbc,基本上版本是到了1.5(在我使用的时候),我当时在使用的时候遇到一些问题,想加官网群,发现加不进去,就去找群主,加群主,并且询问为什么不让加讨论群,后来我才知道那个群主是张亮(当当网架构师,负责sharding jdbc的大牛),还好别人没有理我,太唐突了,后面自己也都把这些问题解决了。不过据说当当网的sharding jdbc抽离出来了,由Apache负责,所以:我的依赖从dangdang的


        com.dangdang
        sharding-jdbc-core
        1.3.3

变为了:


        io.shardingjdbc
        sharding-jdbc-core
        2.0.3

我当当网的sharding使用的版本比较低,遇到了一些问题,文章末尾会分享出来。

二.开始动手(这是正文)

废话已经说了那么多了,现在进入正题吧,如何进行sharding jdbc的分库分表,如有错误的地方,欢迎指正。

我使用的工具:编译器:IntelliJ IDEA; mysql管理工具:workbench;

1.我们先建立数据库和表(分别建了两个库两张表):

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这个网址去创建一个spring boot项目,也可以从idea编译器上:File->new->project

sharding jdbc + mybatis +spring boot的分库分表实现_第1张图片

创建项目.PNG\ 最后生成的pom文件:



    4.0.0
    com.example
    demo
    0.0.1-SNAPSHOT
    jar
    demo
    Demo project for Spring Boot
    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.0.RELEASE
         
    
    
        UTF-8
        UTF-8
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            1.3.1
        
        
            mysql
            mysql-connector-java
            runtime
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
        
            
            
            
        
        
            io.shardingjdbc
            sharding-jdbc-core
            2.0.3
        
        
            org.springframework.boot
            spring-boot-devtools
            true
        
        
            com.alibaba
            druid
            1.1.3
        
        
            commons-dbcp
            commons-dbcp
            1.4
        
        
            mysql
            mysql-connector-java
            5.1.44
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

其中,spring-boot-devtools,commons-dbcp,druid,我感觉都是不需要的,我完成项目后也没对这些项目的依赖进行过滤。

 这个时候,你可以测试一下,你的spring boot项目能否正常启动,可以,你就进入到下一步的配置当中。

3.mybatis的配置

首先,看看我的源码的目录结构:

sharding jdbc + mybatis +spring boot的分库分表实现_第2张图片

目录结构.PNG

我创建一个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




  
    
    
    
    
    
  
  
    
    user_id, user_name, account, password
  
  
  
    
    insert into user_info (user_id, user_name, account,
      password)
    values (#{userId,jdbcType=BIGINT}, #{userName,jdbcType=VARCHAR}, #{account,jdbcType=VARCHAR},
      #{password,jdbcType=VARCHAR})
  
  
    
    insert into user_info
    
      
        user_id,
      
      
        user_name,
      
      
        account,
      
      
        password,
      
    
    
      
        #{userId,jdbcType=BIGINT},
      
      
        #{userName,jdbcType=VARCHAR},
      
      
        #{account,jdbcType=VARCHAR},
      
      
        #{password,jdbcType=VARCHAR},
      
    
  
  
    
    update user_info
    
      
        user_name = #{userName,jdbcType=VARCHAR},
      
      
        account = #{account,jdbcType=VARCHAR},
      
      
        password = #{password,jdbcType=VARCHAR},
      
    
    where user_id = #{userId,jdbcType=BIGINT}
  
  
    
    update user_info
    set user_name = #{userName,jdbcType=VARCHAR},
      account = #{account,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR}
    where user_id = #{userId,jdbcType=BIGINT}
  

按道理说,这样的配置应该就可以了,你写个测试代码试一试,能不能进行增删查改,应该是可以的,可我运气比较差,居然不行,所以我又加了一个mybatis-config.xml的配置,我不知道你们运行的结果怎么样,但是现在如果出问题,一定在mybatis上,所以问题搜索的范围就相对只有这一个模块




    
    
    
    
    
    
    
    
    
    
    
    
        
    
    
        
    

4.sharding jdbc的配置\ 当你mybatis调整好了的时候,这个时候就该加sharding jdbc的配置了,接下如果出问题,应该先朝sharding jdbc的方向去考虑.\ 从目录结构中我们可以看到,我有一个config包,我把我的配置都写在这里面的,首先,我们先实现我们的分库分表的策略\ 分库策略的类,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 {
    @Override
    public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
        for (String each : collection) {
            if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2+"")) {
                return each;
            }
        }
        throw new IllegalArgumentException();
    }
    //public class DemoDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm {
//
//    @Override
//    public String doEqualSharding(Collection databaseNames, ShardingValue shardingValue) {
//
//        for (String each : databaseNames) {
//            if (each.endsWith(Long.parseLong(shardingValue.getValue().toString()) % 2 + "")) {
//                return each;
//            }
//        }
//        throw new IllegalArgumentException();
//    }
//
//    @Override
//    public Collection doInSharding(Collection databaseNames, ShardingValue shardingValue) {
//        Collection 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 doBetweenSharding(Collection databaseNames, ShardingValue shardingValue) {
//        Collection result = new LinkedHashSet<>(databaseNames.size());
//        Range range = (Range) 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,就应该实现PreciseShardingAlgorithm接口,然后实现doSharding方法,对应SQL中的=, IN,还有RangeShardingAlgorithm接口中,对应SQL中的BETWEEN AND,因为我只需要=,in操作,所以只实现了PreciseShardingAlgorithm接口,你如果都需要,你可以都实现(千万不要忽略了一个类可以实现多个接口)。\ 如果你使用的当当网的sharding jdbc,那么你需要实现SingleKeyDatabaseShardingAlgorithm这个接口,实现其中的三个方法,我注释到的部分就是原来我用当当网的sharding jdbc的实现。\ 分表策略的类,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 {
public class DemoTableShardingAlgorithm implements PreciseShardingAlgorithm {
    @Override
    public String doSharding(Collection collection, PreciseShardingValue 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 tableNames, ShardingValue 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 doInSharding(Collection tableNames, ShardingValue shardingValue) {
//        Collection 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 doBetweenSharding(Collection tableNames, ShardingValue shardingValue) {
//        Collection result = new LinkedHashSet<>(tableNames.size());
//        Range range = (Range) 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;
//
//    }
}

与分库的步骤一致,也是需要实现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()));
    }
    /**
     * 设置表的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;
    }
    /**
     * 需要手动配置事务管理器
     *
     * @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 createDataSourceMap() {
        Map 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;
    }
}

当你遇到一个问题:意思差不多是,需要一个数据源,但是发现好几个,你可以在\ getShardingDataSource()这个方法上添加注解:@Primary,设置默认数据源\ 还有一个重中之重的部分,在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.测试

我写了一个测试服务和一个测试类

测试服务: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的支持不是很好,老点的版本是这样,但是新版的应该有很大的改善;

 2.在我进行测试的时候,使用的当当网1.3版本的sharding jdbc,一直提示我在sql语句的带上分片键的值,我确实是带上的,我的代码我确定没有问题,网上也没搜到答案,我就根据报错debug sharding jdbc的源码,经过一天的努力,我发现问题了

是因为我使用的sharding jdbc版本不是1.5以上的版本,所以不是sharding jdbc自带的SQL解析引擎,所以处理SQL的时候使用druid的parameter属性进行SQL中的参数存储,而在高版本的druid中,将parameter替换成inputParameter,而我使用的sharding jdbc仍是使用的parmeter取出参数,所以一直找不到分片键值,升级sharding版本或者降低druid版本就可以了

看图你们就懂了:

sharding jdbc + mybatis +spring boot的分库分表实现_第3张图片

sharding-jdbc问题.PNG

sharding-jdbc问题1.PNG

这是两个新版本和旧版本的druid中setParameters方法的源码,看出不同了吧,就是因为这个,我的测试一直报错。

世上无难事,只要肯攀登。

我的愿望是:代码无bug

我的代码的github地址:https://github.com/DragonMat/sharding-jdbc-demo.git

(完)

MarkerHub文章索引:(点击阅读原文直达)

https://github.com/MarkerHub/JavaIndex

【推荐阅读】

我才懂!SpringBoot的StringRedisTemplate与RedisTemplate的序列化策略有啥不同~

Spring Boot 2 + Spring Security 5 + JWT 的单页应用 Restful 解决方案

大公司为什么都有API网关?什么是API网关?

Springboot整合多数据源以及多数据源中的事务处理(附源码)


好文!必须点赞

你可能感兴趣的:(sharding jdbc + mybatis +spring boot的分库分表实现)