正常情况下,按照【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,结果还算理想,希望给有同样问题的小伙伴带来帮助。
如果还有空余时间可以改造下最新稳定版本,这个版本的着实不太新






