DriverManager在Gradle自定义插件的任务中看不到依赖 [英] DriverManager doesn't see dependency in gradle custom plugin's task

查看:155
本文介绍了DriverManager在Gradle自定义插件的任务中看不到依赖的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

build.gradle文件:

  buildscript {
repositories {
mavenCentral()
}
依赖关系{
classpath(com.h2database:h2:1.4.196)
}
}
插件{
idsomeplugin
}
apply plugin:'groovy'

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

  DriverManager.getConnection(jdbc:h2:mem:,sa,)

$ b

 c>没有找到适合jdbc的驱动程序:h2:mem:

测试这些类 - 只有当我调用调用DriverManager.getConnection的插件任务时才会发生。



我在这里丢失了什么?谢谢!

解决方案

那么,为什么 DriverManager 不像你使用它,以及如何使它工作,并且如何正确使用Groovy中的SQL(这是Gradle脚本的基础)。我将从正确的方式开始使用Groovy中的SQL:



正确使用Gradle / Groovy中的SQL:



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



除了将驱动程序添加到JVMs ext dir,您可以像这样动态地执行此操作:

 配置{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'你的sql在这里'
}

正确使用 DriverManager



您不能正确使用 DriverManager ,因为动力Groovy的强烈。在您使用的方法中, DriverManager 尝试从callstack动态查找调用方类,然后使用该类的类加载器来查找数据库驱动程序。使用Groovy,这是一些动态代理类,因此在其类加载器中找不到数据库驱动程序。



如果您改为使用 DriverManager 正确的调用者类明确地像 DriverManager.getConnection(jdbc:h2:mem:,[user:sa,password:]作为Properties,getClass ()),它工作正常。或者,它也可以不给任何类似 DriverManager.getConnection(jdbc:h2:mem:,[user:sa,password:]作为属性的调用者类,null) code>在这种情况下,使用当前的线程上下文类加载器,这也很好。



Gradle中自动查找驱动程序的问题:当加载类 DriverManager 时,它会扫描系统属性 jdbc .drivers 以及提供 java.sql.Driver 服务的所有服务。它遍历那些找到的类并实例化它们。驱动程序本身通常会响应自己向 DriverManager 注册自己,他们现在可以在此时进行自动查找,就像我之前建议的一样。



现在问题在于,如果您正在使用Gradle Daemon(现在是默认值),并且运行该守护进程中的任何构建,该构建会加载 DriverManager (例如在你以前的尝试中),那么这个类已经被加载了。如果你稍后将构建脚本依赖项添加到H2中(或者运行一个构建文件,在它不存在但是 DriverManager 被载入)之后运行,那么该类是已经加载并且不会查找现在在类路径中的驱动程序。



这里有一些可能的解决方法,没有明确命名驱动程序类,可能是最糟糕的,也可能是最好的解决方案: p>


  • 禁用构建的守护进程,并确保没有人使用构建启用守护进程。在调用<$ c之前,使用私有方法 DriverManager.loadInitialDrivers(),这是很难控制和执行和降低构建性能的。
  • $ c> getConnection
    以确保查找完成并自动添加H2驱动程序。更好,但使用私有方法。
  • 自己使用 ServiceLoader 来加载所有 Driver ClassLoader.load(Driver.class).collect()中的类自动注册到 DriverManager $ c>在 getConnection 调用之前。可能是最优雅的解决方案。


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


  • 在使用 getConnection()之前简单地加载类,使其自注册为 org.h2 .Driver.toString()

  • 在使用 getConnection()之前简单地加载类以使其自己 Class.forName'org.h2.Driver'


I'm writing some gradle plugin and now I've faced with a problem that DriverManager doesn't see JDBC driver defined in buildscript dependency:

I have next build.gradle file:

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

And when I invoke command defined in task that extends DefaultTask

DriverManager.getConnection("jdbc:h2:mem:", "sa", "")

I receive exception

No suitable driver found for jdbc:h2:mem:

There is no problem when I unit-test these classes - it happens only when I invoke plugin's task that invokes DriverManager.getConnection.

What am I missing here? Thanks!

解决方案

Well, there is an answer on why DriverManager does not work like you use it and how to make it work and there is an answer on how to use SQL from Groovy (which is the base for Gradle scripts) properly. I'll start with the proper way to use SQL from Groovy:

Properly use SQL from Gradle / Groovy:

Adding the driver to the buildscript classpath is not enough unfortunately to use the Groovy Sql class, you need to add the driver to the right classloader instead, otherwise it will not work properly.

Besides adding the driver to the JVMs ext dir, you can do this dynamically like this:

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'
}

Properly using the DriverManager:

You cannot use the DriverManager properly like you did, because of the dynamicness of Groovy. In the method you used, the DriverManager tries to dynamically find the caller class from the callstack and then uses the classloader of that class for finding the database driver. With Groovy, this is some dynamic proxy class that is found and thus the database driver is not found in its class loader.

If you instead give the DriverManager the proper caller class explicitly like DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, getClass()), it works properly. Alternatively it would also work to not give any caller class like DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, null) in which case the current threads context class loader is used, which is also fine.

Problem with automatic driver lookup in Gradle:

When the class DriverManager is loaded, it scans through the system property jdbc.drivers and all services that provide the java.sql.Driver service. It iterates through those found classes and instantiates them. The drivers themselves are usually responsive for registering themselves with the DriverManager which they can do at this point in time for being available for automatic lookup later on like I advised above.

Now the problem is, if you are using the Gradle Daemon (which is the default by now), and run any build in that daemon that loads the DriverManager (e. g. in your former tries), then the class is already loaded. If you then later on add the buildscript dependency to H2 (or run a build where it is present after one where it not was present but DriverManager got loaded), then the class is already loaded and does not lookup drivers that are now in the classpath.

Here some possible workarounds without explicitly naming the driver class, from probably worst to probably best solution:

  • Disable the daemon for your build and make sure noone using your build enables the daemon. This is hard to control and enforce and degrades build performance.
  • Use the private method DriverManager.loadInitialDrivers() before calling getConnection to make sure the lookup is done again and the H2 driver added automatically. Better but uses a private method.
  • Use the ServiceLoader yourself to load all Driver classes in the classpath to make them self register with the DriverManager like ServiceLoader.load(Driver.class).collect() before the getConnection call. Probably the most elegant solution.

Here some possible workarounds with explicitly naming the driver class:

  • Simply load the class before using getConnection() to make it self-register with org.h2.Driver.toString()
  • Simply load the class before using getConnection() to make it self-register with Class.forName 'org.h2.Driver'

这篇关于DriverManager在Gradle自定义插件的任务中看不到依赖的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆