程序员

JAVA加载数据库驱动(JDBC)

JAVA加载数据库驱动(JDBC)

前言

之前,对Class.forName("com.mysql.jdbc.Driver");这条动态加载JDBC驱动感觉很疑惑,故有了这篇短文。

一、使用JDBC连接MySQL

首先,来看一下正常使用Java操纵MySql的简单代码逻辑。

    public static boolean connectionMySqlDemo() {
        Connection conn = null;
        try {
            // 1、动态加载mysql驱动
            Class.forName("com.mysql.jdbc.Driver"); 
            // 2、连接数据库
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?"
                    + "user=root&password=1234&useUnicode=true&characterEncoding=UTF8");
            // 3、声明一个Statement 用来执行sql语句
            Statement stmt = conn.createStatement();

            // 4、执行sql语句
            stmt.executeUpdate("create table student(no_id char(20),name varchar(20),primary key(no_id))");
            int result = stmt.executeUpdate("insert into student(no_id,name) values('1','fxleyu')");
            if (result > 0) {
                ResultSet rs = stmt.executeQuery("select * from student");
                while (rs.next()) {
                    System.out.println(rs.getString(1));
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 5、关闭数据库
            if (conn != null) {
                try {
                    conn.close();
                    return true;
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        return false;
    }

在上述代码中,动态加载数据库驱动那条语句感觉独立于其余代码逻辑。感觉缺少其并无关系(当然缺少了会在链接数据库时报java.sql.SQLException: No suitable driver found for)。

二、疑惑

上述代码很容易理解,除了如下Class.forName("com.mysql.jdbc.Driver");。正常理解,该语句只是加载把com.mysql.jdbc.Driver加载到JVM中,没不会产生特殊作用。
阅读com.mysql.jdbc.Driver代码,可以发现其中的隐含逻辑。

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

原来该类中有静态代码库,其加载到JVM时,会执行该静态代码库。而该代码块会把该类的对象实例自注册到DriverManager中。如此第一部分中的第二步就很容易理解了。

// 2、连接数据库
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?"
        + "user=root&password=1234&useUnicode=true&characterEncoding=UTF8");

其就是可以根据String来获得上述加载的驱动,从而可以正常访问数据库。

三、余以为

代码的逻辑很重要,而Class.forName("com.mysql.jdbc.Driver");单独来看只是一条孤立的语句。没有和上下文代码产生显示的关联,这就导致了余在前言中的疑惑。故,使用Class.forName("com.mysql.jdbc.Driver");来动态注册驱动,会对当前代码逻辑产生不利影响。

正常的逻辑,Class.forName方法就是加载一个指定类,并对该类做一些初始化工作(使用静态代码库),其不应该做一些其它逻辑,例如动态注册驱动。如果需要注册驱动时,应该让用户自己使用DriverManager.registerDriver(new com.mysql.jdbc.Driver());来显示注册。这样代码逻辑会很清晰。

但在这里使用显示注册并不合适。因为当new com.mysql.jdbc.Driver()的动作中,就有类的加载过程。在该过程中,已经把该驱动加载到了DriverManager的列表中。而有显示的注册了一次,故DriverManager的列表会有两个com.mysql.jdbc.Driver实例。也就是说,MySql的jdbc并不适合用来进行显示加载。

当然,也许使用Class.forName("com.mysql.jdbc.Driver");有MySql团队自己的考虑,而我当前视野并没有看到其好处。

四、结束

Talk is cheap, show me the code. 当对某些代码逻辑有疑问时,不妨查看一下相关源码,就会豁然开朗。自勉。P.S. 第三部分的余以为灵感借鉴于“余晟以为”微信公众号,很喜欢他那句“我是这么以为的,当然你也可以那么以为”。

参考

1、使用源码下载路径 http://101.96.8.142/dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.40.tar.gz