解决Springboot2.x + shardingSphere数据库名带下划线问题

正常情况下,按照【shardingSphere4.1.1】版本配置,会特别特别简单,真正的0侵入框架!超级好用!!
但是如果有正如标题的问题……
出现这个问题,网上居然没有解决方案!!无奈只能自己撸了。

1. 抛出问题

如果你的数据库名带下划线的,而且还想接入shardingSphere,当你兴奋的配置完后,一启动 (๑•̀ㅂ•́)و✧ !!!报错了!!!

image.png

错误信息很明显,长话短说就是:数据库不能使用下划线命名

2.问题追踪

看到报错信息,脑袋瞬间大了好几圈!对于一个c端服务来说,修改数据库名带来的影响不可估量,比如:除了考虑停服,或者线上数据冷冻手段外,你还要考虑是否存在服务启动后,多服务联调异常,数据同步等问题!

2.1 起初以为是shariding的问题

我就纳闷了,这么大一个顶级开源项目,怎么兼容性这么差!
接下来做的第一件事就是:撸源码!

2.1.1 从jar包开始

sharding依赖包核心就一个(版本号根据项目需要自行引用):

        <dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>3.0.0.M3</version>
        </dependency>

我们看到start的时候,应该会对下面这个文件很敏感!

image.png

没错,它就是SPI!打开对应的factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.shardingsphere.jdbc.spring.boot.SpringBootConfiguration

找到入口配置文件 SpringBootConfiguration 点它!

private final Map<String, DataSource> dataSourceMap = new LinkedHashMap<>();
    
    /**
     * Get data source bean.
     * 
     * @return data source bean
     * @throws SQLException SQL exception
     */
    @Bean
    public DataSource dataSource() throws SQLException {
        return null == masterSlaveProperties.getMasterDataSourceName() 
                ? ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingProperties.getShardingRuleConfiguration(), shardingProperties.getConfigMap(), shardingProperties.getProps())
                : MasterSlaveDataSourceFactory.createDataSource(
                        dataSourceMap, masterSlaveProperties.getMasterSlaveRuleConfiguration(), masterSlaveProperties.getConfigMap(), masterSlaveProperties.getProps());
    }
    
    @Override
    public final void setEnvironment(final Environment environment) {
        setDataSourceMap(environment);
    }
    
    @SuppressWarnings("unchecked")
    private void setDataSourceMap(final Environment environment) {
        String prefix = "sharding.jdbc.datasource.";
        String dataSources = environment.getProperty(prefix + "names");
        for (String each : dataSources.split(",")) {
            try {
                Map<String, Object> dataSourceProps = PropertyUtil.handle(environment, prefix + each, Map.class);
                Preconditions.checkState(!dataSourceProps.isEmpty(), "Wrong datasource properties!");
                DataSource dataSource = DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(), dataSourceProps);
                dataSourceMap.put(each, dataSource);
            } catch (final ReflectiveOperationException ex) {
                throw new ShardingException("Can't find datasource type!", ex);
            }
        }
    }

看到 dataSourceMap、setDataSourceMap、dataSource等,我的兴奋细胞被激活了,直觉告诉我,就是它!
这里只有一个@Bean,肯定是注入数据源么,sharding毕竟是个中间件,它可以分库,分表,主从等等,数据源就在这里维护了!
重点撸下面,看看怎么把数据源放进去的!
String prefix = "sharding.jdbc.datasource.";这个就是yml文件里对应关系,好的,开始吧!从这里开始打断点!

2.2 狐狸的尾巴,终究会露出来

当我们断点到如何返回map的DataSource里面后,看到这段:

 public static <T> T handle(final Environment environment, final String prefix, final Class<T> targetClass) {
        switch (springBootVersion) {
            case 1:
                return (T) v1(environment, prefix);
            default:
                return (T) v2(environment, prefix, targetClass);
        }
    }

对应的v1、v2,断点一看就明白,它是判断当前用的boot版本号,走不通的方法!
我现在用的是2的版本,自然进入v2的方法里,具体代码大家自己看源码,里边有一句最关键的是这个:

Map<String, Object> dataSourceProps = (Map<String, Object>) getSubPropertiesMethod.invoke(resolverObject, prefixParam);

再点进去看一堆堆堆东西,对这次bug来说意义不大了,大概知道了,是因为boot版本导致的,但是接下来要进入验证环节!

长话短说!我把boot版本切到1.x后,数据库名还是带下划线的,启动项目,它居然没报错,正常启动!

2.3 真像大白

看来是冤枉sharding团队了,我说呢,一个阿帕奇顶级项目,还在快速发展中,兼容性居然这么弱!原来是spring的坑!
继续验证!
把sharding依赖去掉,单独在配置文件中配置带下划线的属性,它xx确实会报错!!!!

3. 改造

思路:
既然找到了根本原因,我们从这里出发,datasource尝试手动封装,然后继续使用sharding的分表功能。

过程很痛苦,由于sharding的版本差异很大,期间尝试了api开发、版本切换、还有各种依赖的干扰,导致这个过程有点漫长,下面直接上干货!

3.1 自定义配置类,排除sharding自己的初始化配置

启动类中排除两个配置类:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, SpringBootConfiguration.class})

3.12 配置文件
sharding.jdbc:
  datasource:
    names: sunac-wxapp-order #注意这里,我们配置的是横杠,实际中是下划线
    sunac_wxapp_order:
      url: jdbc:mysql://xxxx:3306/sunac_wxapp_order?useTimezone=true&serverTimezone=GMT%2B8&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      username: xxx
      password: xxx
      initial-size: 80
  #分表的配置不受影响,照常
  config:
    sharding:
      tables:
        order_product_info:
          actual-data-nodes: sunac_wxapp_order.order_product_info_$->{0..3}
          table-strategy:
            standard:
              sharding-column: member_no
              precise-algorithm-class-name: com.skh.config.CustomShardingTable

3.12 配置类(最重要的)

下面贴下重点代码:

/**
* @description:
* @author: Liangyb (liangyb5@sunac.com.cn)
* @date: 2021/7/28 2:12 下午
*/
@Configuration
@EnableConfigurationProperties({SpringBootShardingRuleConfigurationProperties.class, SpringBootMasterSlaveRuleConfigurationProperties.class})
public class ShardingDataSourceConfig implements EnvironmentAware {

    private final static Logger log = LoggerFactory.getLogger (ShardingDataSourceConfig.class);

    @Autowired
    private SpringBootShardingRuleConfigurationProperties shardingProperties;
    @Autowired
    private SpringBootMasterSlaveRuleConfigurationProperties masterSlaveProperties;

    private final Map<String, DataSource> dataSourceMap = new LinkedHashMap<> ();

    /**
     * Get data source bean.
     *
     * @return data source bean
     * @throws SQLException SQL exception
     */
    @Bean
    public DataSource dataSource() throws SQLException {
        return null == masterSlaveProperties.getMasterDataSourceName()
                ? ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingProperties.getShardingRuleConfiguration(), shardingProperties.getConfigMap(), shardingProperties.getProps())
                : MasterSlaveDataSourceFactory.createDataSource(
                dataSourceMap, masterSlaveProperties.getMasterSlaveRuleConfiguration(), masterSlaveProperties.getConfigMap(), masterSlaveProperties.getProps());
    }

    @Override
    public final void setEnvironment(final Environment environment) {
        setDataSourceMap(environment);
    }

    @SuppressWarnings("unchecked")
    private void setDataSourceMap(final Environment environment) {
        String prefix = "sharding.jdbc.datasource.";
        String dataSources = environment.getProperty(prefix + "names");
        for (String each : dataSources.split(",")) {
            try {
                Map<String, Object> dataSourceProps = PropertyUtil.handle(environment, prefix + each, Map.class);
                Preconditions.checkState(!dataSourceProps.isEmpty(), "Wrong datasource properties!");
                DruidDataSource dataSource = (DruidDataSource) DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(), dataSourceProps);
//                if(encrypt){
                // 如果数据库密码加密,这里可以做解密操作
//                    dataSource.setPassword (EncryptUtil.getInstance ().Base64Decode (dataSource.getPassword ()));
//                }
                dataSource.setInitialSize (initialSize);
                dataSource.setMaxActive (maxActive);
//                dataSourceMap.put(each, dataSource);
                // each是从配置文件读取过来的,sunac-wxapp-order,我们强制改为自己需要的库名
                dataSourceMap.put("sunac_wxapp_order", dataSource);
            } catch (final ReflectiveOperationException ex) {
                throw new ShardingException ("Can't find datasource type!", ex);
            }
        }
    }
}




如果上述工程引入自己的项目没问题,无需向下看:

4. 又有bug来捣乱

demo搞完了,本以为大功告成,一步步挪到自己的项目中,我x,又报错了!!

4.1 无语1

自定义配置直接失效了!十万个为什么头顶飘过……
查了又查,看了又看,实在看不出哪里问题,一点点试,终于,初露端倪!

我尝试着把启动类的@ComponentScan注释掉,它是可以进入自定义类的,加上就失效!

莫非这个注解有问题,简单看了下源码也没啥问题啊!然后把引入的包一个个尝试,发现只有引入

image.png

这个包才会失效,心想,那肯定这个包有问题!

找到这个工程,一点点看,全局搜了下,也没发现有啥sharding配置的东西,这就奇怪了,只能一点点来了!

然后一点点把依赖注释掉,打包,重试,再注释再打包再重试!
我是真快吐了,到底哪里的问题!

此时,觉得脑袋太乱了,肯定是有个地方忽略了…… 打开qq音乐,搜了一首【go west】,把声音开到大大大,闭上眼,尝试然自己冷静下来……

冷静了大约5分钟后,重新审视这个依赖工程

果然发现了端倪!

这就是个依赖工程,怎么还有启动类,要这个有啥用??和同事确认了下,这个没用,然后尝试着把这个类全部注释掉,然后打包,重新引用,启动项目。
我xxxxx,它tm成功进入了自定义配置类!!
心中顿时涌现出了好多问候的句子!这是情感真情流露,氛围都到这里了,没有针对任何人的意思。

4.2 无语2

那个错误解决了,然后……,是的,又有报错信息了!!!然后我真想放弃了

image.png

还真和我较上劲了!又听了一首【Uptown Funk】,还是很大大声音,让自己平复下烦躁的心情!

继续开鲁!

en???莫非,,,配置文件不对?随后和demo工程对了好几遍,确保没问题!再启动,,,还是这个错!
头大了,百度吧!,,搜了一堆解决办法,nnd一个都没有解决!

无语!咋整!太奇怪了!

源码打断点!看看到底哪里报的错,我tm就不信邪了!!!
随后一路断点,一遍又一遍……

终于找到报错的地方了:::

image.png

这个方法,点进去,会找对应的分表信息:

image.png

这里有个返回的封装类ResultSet,获取resultSet.next()返回,没错!就这里报的错!!

立即找到demo对应源码这里的代码块,启动,断点,到这里点击下一步……奇迹出现了, demo源码居然没报错,直接返回了!!!

这就好办了,看下这两个的对象类是否一样!一看果然又真相大白了!
demo源码中的是这样的:

image.png

它俩张的太像了!但是真不一样!一下就知道咋回事了,看了下项目引用的版本号,果然是6.x的,降版本!引入5.x,问题迎刃而解!

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.26</version>
        </dependency>

5. 回味

上述只是几个查问题解决问题重要的环节,实际比这个要曲折很多,虽然过程很fk,结果还算理想,希望给有同样问题的小伙伴带来帮助。

如果还有空余时间可以改造下最新稳定版本,这个版本的着实不太新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容