IntelliJ IDEA 集成 ShardingSphere-JDBC 访问分库分表

背景

众所周知,IntelliJ IDEA 是 Java 领域常用的开发工具之一,IDEA Ultimate(旗舰版)或其他例如 DataGrip 等 Intellij 平台的工具都集成了对数据库的访问能力。

但是,对于做了分库分表的项目,直接使用 IDEA 连接数据库只能访问具体的库、表。假如开发测试过程中,需要执行一些不带分片条件的查询语句,直连数据库的话只能手动逐个库表查询。

要解决分库分表数据访问的问题,Apache ShardingSphere 还有 ShardingSphere-Proxy,可以通过 Proxy 实现开发测试过程中的分库分表数据访问。但 Proxy 毕竟是独立进程,部署需要额外的资源与操作。考虑到:

  • 部分项目只通过 ShardingSphere-JDBC 访问分库分表,没有引入 ShardingSphere-Proxy 的条件。
  • 测试环境没有可供 Proxy 部署的资源,每次开发测试使用 IDEA 时,还要再单独本地启动一个 Proxy,相对麻烦。

所以,我们能否直接把 ShardingSphere-JDBC 集成到 IDEA 的数据库工具中,在 IDEA 内直接用逻辑 SQL 访问数据?

步骤

打包 ShardingSphere-JDBC 并在 IDEA 新建自定义数据库驱动

本文 JAR 的打包可以参考:https://github.com/TeslaCN/shardingsphere-all-in-one

对于使用依赖管理的项目,JAR 数量的多少对于开发者来说相对透明,引入依赖改改 POM 文件即可。但如果需要集成 ShardingSphere 的项目没有 Maven 或 Gradle 依赖管理(甚至没有源码),引入 ShardingSphere 可能是一项麻烦的事情。

Apache ShardingSphere 是一个模块拆分非常细的项目。当你只把基本的 ShardingSphere-JDBC 引入项目,你会发现项目依赖 JAR 包瞬间多了 100+ org.apache.shardingsphere 开头的 JAR,且该数量不包含 ShardingSphere 的第三方依赖。

xml 复制代码
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc</artifactId>
            <version>5.5.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shardingsphere</groupId>
                    <artifactId>shardingsphere-test-util</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

本质就是引入 ShardingSphere-JDBC 的所有依赖,通过 maven-shade-plugin 打包为单个 JAR,以便引入 IDEA。

Apache ShardingSphere 官方提供了 ShardingSphere-JDBC 二进制包 的下载,但该包内 仅包含了 ShardingSphere-JDBC 的 JAR,不包括所有第三方依赖 ,所以这个包的实际应用场景比较受限。

踩坑:报错 SPI 找不到实现类

当然,事情不会那么顺利。当我们把配置文件准备好,填写了正确的 ShardingSphere JDBC URL 后,测试连接发现报错找不到 SPI 实现类。

此处使用的是绝对路径指定配置文件,参考文档:

jdbc:shardingsphere:absolutepath:/home/wuweijie/dev/shardingsphere/config-sharding.yaml

打开 IDEA 日志,发现如下报错。

2024-08-11 21:50:38,554 [22072396]   WARN - #c.i.d.d.BaseDatabaseErrorHandler$UnknownErrorInfo - SPI-00001: No implementation class load from SPI 'org.apache.shardingsphere.infra.url.spi.ShardingSphereURLLoader' with type 'absolutepath:'.
org.apache.shardingsphere.infra.spi.exception.ServiceProviderNotFoundException: SPI-00001: No implementation class load from SPI 'org.apache.shardingsphere.infra.url.spi.ShardingSphereURLLoader' with type 'absolutepath:'.
	at org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader.lambda$getService$1(TypedSPILoader.java:110)
	at java.base/java.util.Optional.orElseThrow(Optional.java:403)
	at org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader.lambda$getService$2(TypedSPILoader.java:110)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader.getService(TypedSPILoader.java:110)
	at org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader.getService(TypedSPILoader.java:97)
	at org.apache.shardingsphere.infra.url.core.ShardingSphereURLLoadEngine.<init>(ShardingSphereURLLoadEngine.java:39)
	at org.apache.shardingsphere.driver.jdbc.core.driver.DriverDataSourceCache.createDataSource(DriverDataSourceCache.java:56)
	at org.apache.shardingsphere.driver.jdbc.core.driver.DriverDataSourceCache.lambda$get$0(DriverDataSourceCache.java:50)
	at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
	at org.apache.shardingsphere.driver.jdbc.core.driver.DriverDataSourceCache.get(DriverDataSourceCache.java:50)
	at org.apache.shardingsphere.driver.ShardingSphereDriver.connect(ShardingSphereDriver.java:71)
	at com.intellij.database.remote.jdbc.helpers.JdbcHelperImpl.connect(JdbcHelperImpl.java:790)
	at com.intellij.database.remote.jdbc.impl.RemoteDriverImpl.connect(RemoteDriverImpl.java:153)

难道是我打的 shade 包出问题了?

检查 JAR 包发现,SPI 的 services 文件和 absolutepath 的 class 文件都在。

于是搜了下 IDEA 加载 SPI 的问题,发现 IDEA 的插件也有无法加载 SPI 实现类的问题:ServiceLoader issues and locating folders inside the plugin

大致意思:SPI 不生效是因为 IDEA 使用了独立的类加载器加载插件导致的。

那这个问题也不是完全无解,数据库插件的 Expert options 中可以去除勾选 Isolate class path

去除勾选后 SPI 找不到实现类的问题就解决了。

小结:当项目集成 ShardingSphere 后报错找不到 SPI 时,关注一下与类加载器是否有关系。

再踩坑:报错 StackOverflowError

查 IDEA 日志发现,爆栈的错误信息如下,明显是日志实现发生了问题。

Caused by: java.lang.StackOverflowError
	at org.slf4j.jul.JDK14LoggerAdapter.log(JDK14LoggerAdapter.java:167)
	at org.slf4j.bridge.SLF4JBridgeHandler.callLocationAwareLogger(SLF4JBridgeHandler.java:221)
	at org.slf4j.bridge.SLF4JBridgeHandler.publish(SLF4JBridgeHandler.java:303)
	at java.logging/java.util.logging.Logger.log(Logger.java:983)
	at org.slf4j.jul.JDK14LoggerAdapter.innerNormalizedLoggingCallHandler(JDK14LoggerAdapter.java:156)
	at org.slf4j.jul.JDK14LoggerAdapter.log(JDK14LoggerAdapter.java:172)
	at org.slf4j.bridge.SLF4JBridgeHandler.callLocationAwareLogger(SLF4JBridgeHandler.java:221)
	at org.slf4j.bridge.SLF4JBridgeHandler.publish(SLF4JBridgeHandler.java:303)
	at java.logging/java.util.logging.Logger.log(Logger.java:983)
	at org.slf4j.jul.JDK14LoggerAdapter.innerNormalizedLoggingCallHandler(JDK14LoggerAdapter.java:156)
	at org.slf4j.jul.JDK14LoggerAdapter.log(JDK14LoggerAdapter.java:172)
	at org.slf4j.bridge.SLF4JBridgeHandler.callLocationAwareLogger(SLF4JBridgeHandler.java:221)
	at org.slf4j.bridge.SLF4JBridgeHandler.publish(SLF4JBridgeHandler.java:303)
	at java.logging/java.util.logging.Logger.log(Logger.java:983)

此时笔者想起,IDEA 自从 2022.1 版本起使用 java.util.logging 代替第三方日志实现:Removing log4j from the IntelliJ Platform

ShardingSphere 内部使用的 Guava EventBus 默认以 java.util.logging 作为日志输出框架,为了将 EventBus 日志与 ShardingSphere-Proxy 的日志输出统一,ShardingSphere 通过 jul-to-slf4j 将日志输出统一到 slf4j 了。由于笔者当时没有考虑到与 ShardingSphere-JDBC 集成的项目可能会把 java.util.logging 作为日志输出框架,jul-to-slf4j 直接应用到了 infra 类上面(对 JDBC 与 Proxy 都生效)。

Bridges error logs which printed by ShardingSphereEventBus to slf4j #17541

由此引发的问题:假如集成 ShardingSphere 的项目本身使用 java.util.logging 作为日志框架,集成就会发生日志实现冲突,可能发生的问题包括但不限于本文中的 StackOverflowError

笔者针对该问题提交的 PR 已被合并,将随 ShardingSphere 下一版本发布:
Make jul-to-slf4j work in ShardingSphere-Proxy only #32347

集成初步完成

Test Connection 操作成功后,就可以在 Console 中执行逻辑 SQL 了。

打开 IDEA 的日志,可以在日志中找到逻辑 SQL 与实际执行的物理 SQL。

之所以说集成是"初步"完成的原因,是 ShardingSphere-JDBC 对元数据查询的支持有限,可能没有办法像直连数据库一样在 IDEA 中正确展示已有的数据库和表。

后续笔者会进一步研究 IDEA 加载数据源的库表的实现,尝试让 IDEA 能够加载 ShardingSphere-JDBC 的逻辑库与逻辑表。

相关推荐
wrx繁星点点9 小时前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
Rverdoser15 小时前
Android Studio 多工程公用module引用
android·ide·android studio
尘浮生16 小时前
Java项目实战II基于Spring Boot的问卷调查系统的设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
free_girl_fang17 小时前
高效作业之Mybatis缓存
java·ide·缓存·mybatis
星寂樱易李18 小时前
pycharm 使用
ide·python·pycharm
心安成长19 小时前
IntelliJ IDEA快速接入LLMs大模型API
java·ide·intellij-idea
37_oC1 天前
Idea自动生成mysql表DML语句
mysql·intellij-idea·dml
tekin1 天前
vscode php Launch built-in server and debug, PHP内置服务xdebug调试,自定义启动参数配置使用示例
ide·vscode·php·launch.json·runtimeargs·php内置服务自定义参数
Tp_jh1 天前
推荐一款非常好用的C/C++在线编译器
linux·c语言·c++·ide·单片机·unity·云原生
曹申阳1 天前
vscode 创建 vue 项目时,配置文件为什么收缩到一起展示了?
ide·vue.js·vscode