我正在编写一些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", "")

我收到异常(exception)
No suitable driver found for jdbc:h2:mem:

当我对这些类进行单元测试时没有问题-仅当我调用调用DriverManager.getConnection的插件任务时才会发生。

我在这里想念什么?谢谢!

最佳答案

好吧,有一个答案说明为什么DriverManager不能像您使用它那样工作,以及如何使其工作,还有一个答案,如何正确使用Groovy中的SQL(这是Gradle脚本的基础)。我将从使用Groovy使用SQL的正确方法开始:

从Gradle / Groovy正确使用SQL:

不幸的是,将驱动程序添加到buildscript类路径不足以使用Groovy Sql类,您需要将驱动程序添加到正确的类加载器中,否则它将无法正常工作。

除了将驱动程序添加到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尝试从调用堆栈中动态查找调用者类,然后使用该类的类加载器来查找数据库驱动程序。使用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/55262588/

    10-11 08:32