为什么 Class.forName 似乎没有向 DriverManager 注册驱动程序

Why does Class.forName not seem to register driver with DriverManager

作为 JDBC 的新手,我被教导使用 Class.forName 将驱动程序注册到 DriverManager, 这似乎不适用于我的代码:

public static void main(String[] args)
{
    System.out.println(DriverManager.drivers().count());

    // clear all loaded drivers
    Iterator<Driver> it = DriverManager.drivers().iterator();

    while (it.hasNext())
    {
        try { DriverManager.deregisterDriver(it.next()); }
        catch (SQLException e) { e.printStackTrace(); }
    }

    System.out.println(DriverManager.drivers().count());

    // register mysql driver with forName
    try { Class.forName("com.mysql.cj.jdbc.Driver"); }
    catch (ClassNotFoundException e) { e.printStackTrace(); }

    System.out.println(DriverManager.drivers().count());

    // register with constructor - works fine
    try { DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver()); }
    catch (SQLException e) { e.printStackTrace(); }

    System.out.println(DriverManager.drivers().count());
}

每当我编译和 运行 时,结果总是 1、0、0、1。 我哪里做错了?

JDBC 驱动程序在现代自动注册 Java

你说:

I was taught to use Class.forName

您的老师非常out-of-date。

在现代 Java 中,您不再需要调用 Class.forName。 JDBC 驱动程序现在通过 Java Service Provider Interface (SPI). If curious about how this works, see .

自动加载

如果您的 JDBC 驱动程序无法自动注册,请验证其包含在您的项目中,或者如果在 Jakarta EE (Java EE) 服务器中,请验证该环境中的正确位置。您没有发布足够的关于您的开发和部署方案的详细信息以供我们诊断。并验证您的版本是否正确,通常应该支持 JDBC 4.x(当前为 4.3)。

如果您仍然遇到问题,请编写一个一次性的控制台应用程序,它 什么都不做 除了连接到您的数据库。尽可能消除复杂性和干扰。

关于您显示的代码,我不知道您为什么想成为 de-registering 任何驱动程序。基本上,您在问题中显示的所有代码都应该是不必要的。

DataSource

此外,通常最好使用 DataSource 作为获取数据库连接的方式。 DataSource 对象包含连接所需的信息:服务器地址、端口号、用户名、密码以及特定于您的数据库服务器产品的各种专有设置。

我已经为 MySQL、Postgres 和 H2 写了一些关于 Stack Overflow 的答案以及完整的工作示例。喜欢。我建议你从我和 Stack Overflow 上的许多其他优秀作者那里搜索这样的例子。

这是一个这样的例子。此处显示的代码应该足够了,因为 MySQL 驱动程序应该会自动加载。

private DataSource configureDataSource ( )
{
    System.out.println( "INFO - `configureDataSource` method. " + Instant.now() );

    com.mysql.cj.jdbc.MysqlDataSource dataSource = Objects.requireNonNull( new com.mysql.cj.jdbc.MysqlDataSource() );  // Implementation of `DataSource`.
    dataSource.setServerName( "db-mysql-lon2-722-do-user-89973-1.x.db.ondigitalocean.com" );
    dataSource.setPortNumber( 24_090 );
    dataSource.setDatabaseName( "defaultdb" );
    dataSource.setUser( "scott" );
    dataSource.setPassword( "tiger" );
    return dataSource;
}

用法:

Connection conn = myDataSource.getConnection() ;

当然,您应该获得满足您需求的 DataSource 实现。例如,一些提供全新的连接,而另一些提供连接池。有些是由您的数据库供应商提供的,有些是 third-party.

自 Java 6(假设初始 class 路径上有 JDBC 4.0 或更高版本的驱动程序),JDBC 驱动程序将自动加载。这意味着驱动程序在 DriverManager 被 class 加载和初始化时自动加载。

驱动程序的静态初始化程序class 是向驱动程序管理器注册驱动程序的。当加载 class 时,此静态初始值设定项为 运行。由于驱动程序 class 已经加载,随后的 Class.forName returns 已经加载 class,并且静态初始化程序不会再次 运行,所以没有注册DriverManager.

换句话说:

  1. DriverManager 已 class 加载并使用 ServiceLoader 加载驱动程序
    • com.mysql.cj.jdbc.Driver 通过 ServiceLoader 加载,其静态初始化程序使用 DriverManager
    • 注册一个实例
  2. 您删除了 DriverManager
  3. 中的所有驱动程序
  4. 你打电话给Class.forName("com.mysql.cj.jdbc.Driver")
    • 鉴于 class 已经 class 加载,Java returns 已经加载 class
    • 未注册任何内容,因为静态初始化程序不会再次 运行
  5. 您使用 DriverManager 手动注册一个实例(顺便说一句,这不是您通常应该做的事情,DriverManager.registerDriver 是供 JDBC 驱动程序调用的)。