【问题标题】:Grails Service can't create Sql instanceGrails Service 无法创建 Sql 实例
【发布时间】:2017-09-25 20:13:17
【问题描述】:

如何在不使用数据源或任何与 GORM Hibernate 相关的情况下在 Grails 服务中创建类 groovy.sql.Sql 的实例?在我的 Grails 应用程序中,在我的 Service 类中,当我尝试创建 Sql 实例时出现异常:

Caused by: java.lang.ClassNotFoundException: org.postgresql.Driver
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at java_lang_ClassLoader$loadClass$1.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at com.tav.admin.DbService.copyFileToTable(DbService.groovy:87)

似乎是类加载问题。这是服务方法。

long copyFileToTable(String sql, FileReader fileReader) {

    println("System Class Loader: ${ClassLoader.getSystemClassLoader().getClass().getName()}")
    println("Groovy Class Loader: ${Thread.currentThread().getContextClassLoader().getClass().getName()}")
    Thread.currentThread().getContextClassLoader().loadClass(driverClassName)
    ClassLoader.getSystemClassLoader().loadClass(driverClassName) // Note throws exception here!

    props = new Properties()
    props.setProperty('user', username)
    props.setProperty('password', password)
    PgConnection copyOperationConnection = (PgConnection) Sql.newInstance(url, props, driverClassName)
    CopyManager copyManager = new CopyManager(copyOperationConnection)
    return copyManager.copyIn(sql, fileReader)
}

输出:

System Class Loader: sun.misc.Launcher$AppClassLoader
Groovy Class Loader: groovy.lang.GroovyClassLoader

PostgreSQL 驱动程序已在 DriverManager 中注册。我可以在我的服务中使用注入的数据源,但我需要创建与 GORM Hibernate 无关的数据库的新连接以使用驱动程序特定功能(PostgreSQL CopyManager)。

在 DriverManager.isDriverAllowed() 中

Class.forName(driver.getClass().getName(), true, classLoader);

返回空值。

版本信息:Grails 2.5.1、Java jdk1.7.0_79、Groovy 2.0.6


我已将问题缩小到以下代码行:

ClassLoader.getSystemClassLoader().loadClass(driverClassName) // Note throws exception here!

似乎 Groovy/Grails 使用 GroovyClassLoader 加载 JDBC 驱动程序,但是当您尝试从 DriverManager.getConnection() 获取连接时,驱动程序管理器会尝试使用 System Class Loader 加载 JDBC 驱动程序。系统类加载器似乎不知道 Groovy 类加载器已经加载了该类。就像 DriverManager 和系统类加载器被沙箱化,远离 Groovy 加载的类。

【问题讨论】:

  • 与您的问题无关,但仅供参考...grailsApplication.config.dataSource.username 和类似的问题有几个原因。有更好的方法可以做到这一点,其中一些在objectcomputing.com/products/grails/… 中进行了描述。
  • @JeffScottBrown 我看了你的视频并修改了代码以使用 Value 注释。
  • 我在 Grails 应用程序中做了类似的事情,即在 Gorm 之外使用 groovy.sql.Sql,如果删除 ClassLoader.getSystemClassLoader().loadClass... 部分并让它运行到 Sql.newInstance...,会在哪里发生故障?在我的应用程序中,我不关心 ClassLoader 的东西,不确定您是出于演示目的展示它还是实际在代码中使用它。

标签: grails groovy


【解决方案1】:

在我的情况下,我有同样的情况(使用没有 GORM 的 Sql)但是使用 MySQL。

我在 resources.groovy 声明了一个 dataSource bean:

beans{
   "otherDataSource"(BasicDataSource) {
      driverClassName = 'com.mysql.Driver'
      username = 'xxx'
      password = 'xxxx'
      url = 'jdbc:blablabla'
   }
}

并在服务中注入它

class MyService{
    def otherDataSource
    void doIt(){
        def sql = new Sql(otherDataSource)
    }
}

【讨论】:

  • 很高兴为您提供帮助
【解决方案2】:

如堆栈跟踪所示,为了与 PostgreSQL 交互,您需要在类路径中有一个合适的驱动程序。

您需要包含一个依赖项或将一个外部库捆绑在一个 jar 中,这将为您提供一个实现 `javax.sql.*' 接口的包。

【讨论】:

  • PostgreSQL 驱动程序包含在我的依赖项中。我可以通过 GORM Hibernate 和 def dataSource 访问我的 PostgreSQL 数据库。这似乎与正在使用的类加载器有关。 DriverManager 使用 sun.misc.Launcher$AppClassLoader,但看起来 Grails/Groovy 使用 groovy.lang.GroovyClassLoader。 sun 类加载器返回 null,Groovy 类加载器返回驱动程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 2020-01-16
  • 2019-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多