DriverManager在gradle自定义插件的任务中看不到依赖项

Evg*_*pov 4 groovy jdbc gradle gradle-plugin

我正在编写一些gradle插件,现在我遇到了一个问题,即DriverManager没有看到在buildscript依赖项中定义的JDBC驱动程序:

我有下一个build.gradle文件:

buildscript {
   repositories {
      mavenCentral()
   }
   dependencies {
      classpath("com.h2database:h2:1.4.196")
   }
}
plugins {
   id "someplugin"
}
apply plugin: 'groovy'
Run Code Online (Sandbox Code Playgroud)

当我调用在任务中定义的扩展DefaultTask的命令时

DriverManager.getConnection("jdbc:h2:mem:", "sa", "")
Run Code Online (Sandbox Code Playgroud)

我收到例外

No suitable driver found for jdbc:h2:mem:
Run Code Online (Sandbox Code Playgroud)

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

我在这里错过了什么?谢谢!

Vam*_*ire 7

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

正确使用Gradle/Groovy中的SQL:

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

除了将驱动程序添加到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'
}
Run Code Online (Sandbox Code Playgroud)

正确使用DriverManager:

DriverManager由于Groovy的动态性,你不能像你一样正确使用.在您使用的方法中,DriverManager尝试从callstack动态查找调用者类,然后使用该类的类加载器来查找数据库驱动程序.使用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驱动程序.更好,但使用私人方法.
  • 使用ServiceLoader自己加载所有Driver类的类路径,使他们的自我与注册DriverManager类似ServiceLoader.load(Driver.class).collect()的前getConnection调用.可能是最优雅的解决方案.

这里有一些明确命名驱动程序类的可能的解决方法:

  • 只需在使用之前加载类getConnection(),使其自行注册org.h2.Driver.toString()
  • 只需在使用之前加载类getConnection(),使其自行注册Class.forName 'org.h2.Driver'