0

我有一个场景,我需要在 SQL Server 上使用 Windows 身份验证两次使用 Java11 执行数据库连接。最初,为第一次调用加载 sqljdbc_auth.dll 并且连接成功。但是,为了第二次在不同的地方建立连接,它会抛出一个 SQLException 说“sqljdbc_auth.dll 已经被另一个类加载器加载”。所以,我需要在两个调用之间卸载 dll。

在 Java8 版本之前,有一个选项可以通过使用反射机制在类加载器上调用 finalize() 来执行相同操作,但在 Java11 中找不到替代方法

示例代码:

在这里,我将 sqljdbc_auth.dll 放在 PATH 中,并将名为 sql_jdbc.jar 的 jar 放在 urls 列表中,这些与 Java11 兼容

private static void loadFile(){
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();;
ClassLoader loader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); // here, urls is an array and contains only a single sql_jdbc.jar path in it
Thread.currentThread().setContextClassLoader(loader);
Driver driver = (Driver)loader.loadClass("com.microsoft.sqlserver.jdbc.SQLServerDriver").newInstance();
Connection connection = driver.connect("jdbc:sqlserver://IP-addr:1433;DatabaseName=db_name;SelectMethod=cursor;integratedSecurity=true", props);
//perform db actions here
Thread.currentThread().setContextClassLoader(currentClassLoader);
unloadDLL("sqljdbc_auth.dll",loader);
}
private synchronized static void unloadDllFile(String dllName, ClassLoader classLoader) throws Throwable {
try {
    Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
    field.setAccessible(true);
    Map<Object, Object> lib = (ConcurrentHashMap<Object, Object>) field.get(classLoader);
    Set<Object> keyset = lib.keySet();
    for (Object dllpath : keyset) {
        if (dllpath.toString().contains(dllName)) {                    
            Object o = lib.get(dllpath);                    
            classLoader = null;
            field = null;
            lib.remove(dllpath);                     
            keyset.remove(dllpath);                   
            o = null;
            System.gc();                   
        }
     } 
  } catch (Exception e) {
        System.out.println("Exception in dll is "+e.getMessage());
         
  }
}

第二个组件中有类似的代码,但它在那里引发异常。加载第二个组件时的异常堆栈跟踪是:

com.microsoft.sqlserver.jdbc.SQLServerException: This driver is not configured for integrated authentication. ClientConnectionId:d19de7a1-d099-477c-9c18-0c4cd5807f5e
at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:2892)
at com.microsoft.sqlserver.jdbc.AuthenticationJNI.<init>(AuthenticationJNI.java:72)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3636)
at com.microsoft.sqlserver.jdbc.SQLServerConnection$LogonCommand.doExecute(SQLServerConnection.java:3627)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7194)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2935)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:2456)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:2103)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1950)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:1162)
at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:735)
at sample.java_samples.Sample2.loadFile(Sample2.java:66)
at sample.java_samples.Sample2.main(Sample2.java:23)
Caused by: java.lang.UnsatisfiedLinkError: Native Library C:\Windows\System32\sqljdbc_auth.dll already loaded in another classloader
at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2456)
at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2684)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2649)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:829)
at java.base/java.lang.System.loadLibrary(System.java:1867)
at com.microsoft.sqlserver.jdbc.AuthenticationJNI.<clinit>(AuthenticationJNI.java:52)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3635)
... 10 more

谢谢

4

0 回答 0