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 的逻辑库与逻辑表。

相关推荐
蜜桃小阿雯8 小时前
JAVA开源项目 校园美食分享平台 计算机毕业设计
java·jvm·spring boot·spring cloud·intellij-idea·美食
程序猿进阶12 小时前
如何在 Visual Studio Code 中反编译具有正确行号的 Java 类?
java·ide·vscode·算法·面试·职场和发展·架构
猿饵块12 小时前
vscode
ide·vscode·编辑器
大数据界Olu15 小时前
bug | pycharm社区版无sciview解决办法
ide·python·pycharm
千寻简17 小时前
Cursor免费 GPT-4 IDE 工具的保姆级使用教程
java·运维·ide·ai
晚枫200017 小时前
kafka发送事件的几种方式
spring boot·分布式·docker·容器·kafka·intellij-idea·linq
我是一颗小小的螺丝钉21 小时前
idea插件推荐之Cool Request
java·ide·intellij-idea
尘浮生1 天前
Java项目实战II基于Spring Boot的宠物商城网站设计与实现
java·开发语言·spring boot·后端·spring·maven·intellij-idea
我命由我123451 天前
2.使用 VSCode 过程中的英语积累 - Edit 菜单(每一次重点积累 5 个单词)
前端·javascript·ide·vscode·学习·编辑器·学习方法
Chase-Hart1 天前
【每日一题】LeetCode 1184.公交站间的距离问题(数组)
java·算法·leetcode·eclipse·intellij-idea