DriverManager 在 gradle 自定义插件的任务中看不到依赖关系
DriverManager doesn't see dependency in gradle custom plugin's task
我正在编写一些 gradle 插件,现在我遇到了一个问题,即 DriverManager 没有看到 JDBC 在构建脚本依赖项中定义的驱动程序:
我有下一个 build.gradle 文件:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("com.h2database:h2:1.4.196")
}
}
plugins {
id "someplugin"
}
apply plugin: 'groovy'
当我调用任务中定义的扩展 DefaultTask 的命令时
DriverManager.getConnection("jdbc:h2:mem:", "sa", "")
我收到异常
No suitable driver found for jdbc:h2:mem:
当我对这些 类 进行单元测试时没有问题 - 只有当我调用插件的任务时才会发生 DriverManager.getConnection.
我在这里错过了什么?谢谢!
好吧,关于为什么 DriverManager
不像您使用它那样工作以及如何使它工作有一个答案,并且有一个关于如何使用 SQL 的答案来自 Groovy(这是 Gradle 脚本的基础)。我将从 Groovy:
中使用 SQL 的正确方法开始
正确使用SQL from Gradle / Groovy:
将驱动添加到buildscript class路径中是不够的,不幸的是要使用Groovy Sql class,您需要将驱动添加到正确的class改为loader,否则将无法正常工作。
除了将驱动程序添加到 JVM 的 ext 目录之外,您还可以像这样动态地执行此操作:
configurations { jdbc }
jdbc 'com.h2database:h2:1.4.196'
def sqlClassLoader = Sql.classLoader
configurations.jdbc.each { sqlClassLoader.addURL it.toURI().toURL() }
Sql.withInstance('jdbc:h2:mem:', 'sa', '', 'org.h2.Driver') {
it.execute 'your sql here'
}
正确使用DriverManager
:
由于 Groovy 的动态性,您不能像以前那样正确使用 DriverManager
。在您使用的方法中,DriverManager
尝试从调用堆栈中动态找到调用者 class,然后使用 class 的 class 加载程序来查找数据库驱动程序。对于 Groovy,这是找到的一些动态代理 class,因此在其 class 加载程序中找不到数据库驱动程序。
如果您改为像 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, getClass())
一样明确地给 DriverManager
正确的调用者 class,它会正常工作。或者它也可以不给任何调用者 class 像 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, null)
在这种情况下使用当前线程上下文 class 加载器,这也很好。
Gradle 中的自动驱动程序查找问题:
加载class DriverManager
时,它会扫描系统属性 jdbc.drivers
和提供java.sql.Driver
服务的所有服务。它遍历那些找到的 classes 并实例化它们。驱动程序本身通常会对在 DriverManager
上注册自己做出响应,他们可以在此时及时进行注册,以便稍后像我上面建议的那样进行自动查找。
现在的问题是,如果您正在使用 Gradle 守护进程(这是现在的默认设置),并且 运行 该守护进程中加载 DriverManager
(例如,在您之前的尝试中),那么 class 已经加载。如果您稍后将 buildscript 依赖项添加到 H2(或 运行 一个它存在的构建在它不存在但 DriverManager
已加载的构建之后),那么 class 已经加载并且不查找现在位于 class 路径中的驱动程序。
这里有一些没有明确命名驱动程序的可能解决方法 class,从可能最差到可能最好的解决方案:
- 为你的构建禁用守护进程,并确保没有人使用你的构建启用守护进程。这很难控制和执行,并且会降低构建性能。
- 在调用
getConnection
之前使用私有方法 DriverManager.loadInitialDrivers()
以确保再次完成查找并自动添加 H2 驱动程序。更好,但使用私有方法。
- 自己使用
ServiceLoader
加载 class 路径中的所有 Driver
class 使它们像 ServiceLoader.load(Driver.class).collect()
在 getConnection
调用之前。可能是最优雅的解决方案。
这里有一些可能的解决方法,明确命名驱动程序 class:
- 只需在使用
getConnection()
之前加载class,使其自动注册到org.h2.Driver.toString()
- 在使用
getConnection()
之前简单地加载class,使其自动注册到Class.forName 'org.h2.Driver'
我正在编写一些 gradle 插件,现在我遇到了一个问题,即 DriverManager 没有看到 JDBC 在构建脚本依赖项中定义的驱动程序:
我有下一个 build.gradle 文件:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("com.h2database:h2:1.4.196")
}
}
plugins {
id "someplugin"
}
apply plugin: 'groovy'
当我调用任务中定义的扩展 DefaultTask 的命令时
DriverManager.getConnection("jdbc:h2:mem:", "sa", "")
我收到异常
No suitable driver found for jdbc:h2:mem:
当我对这些 类 进行单元测试时没有问题 - 只有当我调用插件的任务时才会发生 DriverManager.getConnection.
我在这里错过了什么?谢谢!
好吧,关于为什么 DriverManager
不像您使用它那样工作以及如何使它工作有一个答案,并且有一个关于如何使用 SQL 的答案来自 Groovy(这是 Gradle 脚本的基础)。我将从 Groovy:
正确使用SQL from Gradle / Groovy:
将驱动添加到buildscript class路径中是不够的,不幸的是要使用Groovy Sql class,您需要将驱动添加到正确的class改为loader,否则将无法正常工作。
除了将驱动程序添加到 JVM 的 ext 目录之外,您还可以像这样动态地执行此操作:
configurations { jdbc }
jdbc 'com.h2database:h2:1.4.196'
def sqlClassLoader = Sql.classLoader
configurations.jdbc.each { sqlClassLoader.addURL it.toURI().toURL() }
Sql.withInstance('jdbc:h2:mem:', 'sa', '', 'org.h2.Driver') {
it.execute 'your sql here'
}
正确使用DriverManager
:
由于 Groovy 的动态性,您不能像以前那样正确使用 DriverManager
。在您使用的方法中,DriverManager
尝试从调用堆栈中动态找到调用者 class,然后使用 class 的 class 加载程序来查找数据库驱动程序。对于 Groovy,这是找到的一些动态代理 class,因此在其 class 加载程序中找不到数据库驱动程序。
如果您改为像 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, getClass())
一样明确地给 DriverManager
正确的调用者 class,它会正常工作。或者它也可以不给任何调用者 class 像 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, null)
在这种情况下使用当前线程上下文 class 加载器,这也很好。
Gradle 中的自动驱动程序查找问题:
加载class DriverManager
时,它会扫描系统属性 jdbc.drivers
和提供java.sql.Driver
服务的所有服务。它遍历那些找到的 classes 并实例化它们。驱动程序本身通常会对在 DriverManager
上注册自己做出响应,他们可以在此时及时进行注册,以便稍后像我上面建议的那样进行自动查找。
现在的问题是,如果您正在使用 Gradle 守护进程(这是现在的默认设置),并且 运行 该守护进程中加载 DriverManager
(例如,在您之前的尝试中),那么 class 已经加载。如果您稍后将 buildscript 依赖项添加到 H2(或 运行 一个它存在的构建在它不存在但 DriverManager
已加载的构建之后),那么 class 已经加载并且不查找现在位于 class 路径中的驱动程序。
这里有一些没有明确命名驱动程序的可能解决方法 class,从可能最差到可能最好的解决方案:
- 为你的构建禁用守护进程,并确保没有人使用你的构建启用守护进程。这很难控制和执行,并且会降低构建性能。
- 在调用
getConnection
之前使用私有方法DriverManager.loadInitialDrivers()
以确保再次完成查找并自动添加 H2 驱动程序。更好,但使用私有方法。 - 自己使用
ServiceLoader
加载 class 路径中的所有Driver
class 使它们像ServiceLoader.load(Driver.class).collect()
在getConnection
调用之前。可能是最优雅的解决方案。
这里有一些可能的解决方法,明确命名驱动程序 class:
- 只需在使用
getConnection()
之前加载class,使其自动注册到org.h2.Driver.toString()
- 在使用
getConnection()
之前简单地加载class,使其自动注册到Class.forName 'org.h2.Driver'