Java中如何完全卸载JDBC驱动程序

3

我有一个使用Java、mybatis和SQL Anywhere的应用程序。由于某些原因,我必须在运行时手动加载SQLA驱动程序jar文件。我使用以下代码实现了这个功能:

    public static URLClassLoader ucl;
    public static Driver saDriver;


    public static final void loadSqlAnywhere(SqlAnywhereVersions version)
    {
        if (saDriver == null)
        {
            try
            {
                File jarPath = "path\\to\\sajdbc4.jar";
                URL jarUrl = new URL("jar:file:" + jarPath.getAbsolutePath() + "!/");

                String driverClass = "sap.jdbc4.sqlanywhere.IDriver";

                ucl = URLClassLoader.newInstance(new URL[]{ jarUrl });
                saDriver = new DriverShim((Driver) Class.forName(driverClass, true, ucl).newInstance());
                DriverManager.registerDriver(saDriver);

            }
            catch (Exception ex)
            {
                // Could not load SQL Anywhere driver
            }
        }
    }

当应用程序运行时,SQL Anywhere 可以进行升级。因此,我需要完全卸载旧驱动程序,让更新发生,然后使用最新的 jar 文件进行初始化。我尝试过以下操作:

    private static List<SqlSession> sessions = new ArrayList<>();
    private static SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder();
    public static final void unloadSqlAnywhere()
    {
        if (saDriver != null)
        {

            // Close out any existing sessions
            for (SqlSession session : sessions)
            {
                session.close();
            }
            sessions.clear();

             // Kill the session factory
            sessionFactory = null;

            try
            {
                DriverManager.deregisterDriver(saDriver);
                saDriver = null;

                ucl.close();
                ucl = null;
            }
            catch (Exception ex)
            {
                // Failed to unregister SQL Anywhere driver
            }
        }
    }

但是使用jprofiler,我可以看到DriverManager仍然在引用sap.jdbc4.sqlanywhere.IDriver,这导致SQL Anywhere的升级终止了我的应用程序。

是否有更多的方法可以确保所有驱动程序引用都被删除?或者有更好的方式处理这个问题吗?


你确定注销时没有抛出异常吗?规范说明:如果存在安全管理器并且其checkPermission拒绝权限,则会抛出SecurityException异常。 - mangusta
我确定。我的实际代码中有记录,包括在catch语句中,但为了简洁起见,我将其删除了。 - Troncoso
这是否也会发生在SQLA或其他驱动程序中? - mangusta
3
更新库时实时生效非常不寻常(我从未听说过有人这样做,也从未考虑过这样做)。为什么不直接部署新的库并重新启动应用程序呢?毕竟新版本不是每天甚至每月都会发布,而重新启动应用程序应该是例行操作,因为每次发布代码的新版本时都要这样做。 - Bohemian
该应用程序实际上负责安装最新的SQLA版本,以及其他应用程序更新。然而,在此升级过程中,它必须能够连接到一个SQLA数据库,因此驱动程序必须重新加载才能正常工作。 - Troncoso
1个回答

0

大多数JDBC驱动程序会自动注册,例如,您以前只需调用Class.forName()来加载驱动程序类,这将自动注册它。

现在,您甚至不必再这样做了,因为它们使用服务框架通过简单地存在于类路径上来自动注册,但由于您的JDBC jar文件不在类路径上,因此不适用于此处。

由于您还注册了驱动程序,因此它被注册了两次,当您注销时,您只删除了您创建的注册,而没有删除自动注册的注册。

要删除自动注册的实例,您必须枚举所有已注册的驱动程序以查找要注销的实例:

// Any Java version
for (Enumeration<Driver> e = DriverManager.getDrivers(); e.hasMoreElements(); ) {
    Driver driver = e.nextElement();
    if (driver.getClass().getName().equals("sap.jdbc4.sqlanywhere.IDriver"))
        DriverManager.deregisterDriver(driver);
}

// Java 9+
Optional<Driver> driver = DriverManager.drivers()
        .filter(d -> d.getClass().getName().equals("sap.jdbc4.sqlanywhere.IDriver"))
        .findAny();
if (driver.isPresent())
    DriverManager.deregisterDriver(driver.get());

谢谢您的回答。然而,我尝试了这个for循环并打印出每个找到的驱动程序。在我的初始DriverManager.deregisterDriver()调用之后,唯一剩下的是"org.sqlit.JDBC",因此我不认为我的驱动程序被自动注册了。 - Troncoso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接