我正在编写一些 gradle 插件,现在我遇到了一个问题,即 DriverManager 没有看到在 buildscript 依赖项中定义的 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
不能像您使用它那样工作以及如何使其工作的答案,并且有一个关于如何正确使用来自 Groovy(这是 Gradle 脚本的基础)的 SQL 的答案。我将从使用 Groovy 的 SQL 的正确方法开始:
从 Gradle/Groovy 正确使用 SQL:
不幸的是,将驱动程序添加到 buildscript 类路径不足以使用 Groovy Sql 类,您需要将驱动程序添加到正确的类加载器中,否则将无法正常工作。
除了将驱动程序添加到 JVMs 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
尝试从调用堆栈中动态查找调用者类,然后使用该类的类加载器来查找数据库驱动程序。使用 Groovy,这是找到的一些动态代理类,因此在其类加载器中找不到数据库驱动程序。如果您改为像
DriverManager
一样显式地为 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, getClass())
提供正确的调用者类,则它可以正常工作。或者,它也可以不提供任何调用者类,如 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, null)
在这种情况下使用当前线程上下文类加载器,这也很好。Gradle 中的驱动程序自动查找问题:
当类
DriverManager
被加载时,它会扫描系统属性 jdbc.drivers
和所有提供 java.sql.Driver
服务的服务。它遍历那些找到的类并实例化它们。驱动程序本身通常会响应向 DriverManager
注册自己,此时他们可以这样做,以便以后可以像我上面建议的那样进行自动查找。现在的问题是,如果您正在使用 Gradle 守护程序(现在是默认设置),并在该守护程序中运行加载
DriverManager
的任何构建(例如,在您之前的尝试中),则该类已经加载。如果您稍后将 buildscript 依赖项添加到 H2(或在它不存在但 DriverManager
被加载之后运行它存在的构建),那么该类已经加载并且不会查找现在在类路径中的驱动程序.这里有一些没有明确命名驱动程序类的可能解决方法,从可能最差到可能最好的解决方案:
DriverManager.loadInitialDrivers()
之前使用私有(private)方法 getConnection
以确保再次完成查找并自动添加 H2 驱动程序。更好,但使用私有(private)方法。 ServiceLoader
加载类路径中的所有 Driver
类,使它们在 DriverManager
调用之前像 ServiceLoader.load(Driver.class).collect()
一样自行注册到 getConnection
中。可能是最优雅的解决方案。 这里有一些可能的解决方法,显式命名驱动程序类:
getConnection()
之前加载类,使其自注册 org.h2.Driver.toString()
getConnection()
之前加载类,使其自注册 Class.forName 'org.h2.Driver'
关于jdbc - DriverManager 在 gradle 自定义插件的任务中看不到依赖项,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44740416/