jdbc源码分析

今天看了jdbc的源码,感觉特别有意思,下面是jdbc的传统步骤:

 Connection connection=null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/myshool?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong","root","root");
            String sql="select * from student where name= ? ";
            preparedStatement=connection.prepareStatement(sql);
            preparedStatement.setString(1,"小明");
            resultSet=preparedStatement.executeQuery();
            while (resultSet.next()){
                System.out.println(resultSet.getString("id")+" " +resultSet.getString("name"));
            }


        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
       } catch (SQLException e) {
            e.printStackTrace();
        }

这里我用的是最新的mysql驱动包

第一点:加载驱动。

 Class.forName("com.mysql.cj.jdbc.Driver");
在最开始用的时候很不明白,首先为什么要加载一个驱动类,之后就可以取得
connection,也非常好奇DriverManager是怎么来拿到驱动类的信息,另外还有为什么要用Class.forName加载。
后来点进Driver里,看到:

所以也就是说,在Class.forName加载完驱动类后,开始执行静态代码块时,会new一个Driver,并调用DriverManager的registerDriver把Driver给注册到自己的驱动程序管理器(DriverManager)中去。

在这里说一下ClassLoader.loadClass和Class.forName的区别:

  • Classloder.loaderClass(String name)

    其实该方法内部调用的是:Classloder. loadClass(name, false)

    方法:Classloder. loadClass(String name, boolean resolve)

        1:参数name代表类的全限定类名

        2:参数resolve代表是否解析,resolve为true是解析该类

  • Class.forName(String name)

    其实该方法内部调用的是:Class.forName(className, true, ClassLoader.getClassLoader(caller))

    方法:Class.forName0(String name, boolean initialize, ClassLoader loader)

      参数name代表全限定类名

      参数initialize表示是否初始化该类,为true是初始化该类

      参数loader 对应的类加载器

  • 两者最大的区别

    Class.forName得到的class是已经初始化完成的

    Classloder.loaderClass得到的class是没有初始化的

这里Driver类也继承NonRegisteringDriver并实现java.sql.Driver接口

我们点进NonRegisteringDriver类会发现,它也是实现java.sql.Driver接口:

最主要看一下Driver接口:

public interface Driver {

    //获取Connection 方法。数据库的url,及info至少得包含user,password key
    Connection connect(String url, java.util.Properties info)
        throws SQLException;

    //判断是否是一个正确的url字符串。
    boolean acceptsURL(String url) throws SQLException;

    //得到驱动的属性(user,password,port等)。
    DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException;

    //得到主要版本
    int getMajorVersion();

    //得到次要版本
    int getMinorVersion();

    //判断是否是一个正确的driver
    boolean jdbcCompliant();

    //------------------------- JDBC 4.1 -----------------------------------
    //返回父日志
    public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}

其实Driver接口是每个数据库驱动都必须继承的接口。

言归正传,我们可以看到在Driver类中的静态代码块有DriverManager.registerDriver(这是把Driver给注册到自己的驱动程序管理器(DriverManager)中)的方法,我们点进去可以看到DriverManager类,此类没有继承和实现任何接口,它是管理一组 JDBC 驱动程序的基本服务。

这里显示的是一些重要的方法

public class DriverManager {

    //已经注册的驱动列表
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    private static volatile int loginTimeout = 0;
    private static volatile java.io.PrintWriter logWriter = null;
    private static volatile java.io.PrintStream logStream = null;
    // Used in println() to synchronize logWriter
    private final static  Object logSync = new Object();
    //阻止被初始化,DriverManager里面都是静态的方法。
    private DriverManager(){}
    //初始化加载驱动,其中用到了ServiceLoader机制
    static {
        //初始化加载驱动
        loadInitialDrivers();
        //打印日志
        println("JDBC DriverManager initialized");
    }
    /*因为源代码过长,这里我们只讲重要的一些方法*/

    //初始化加载驱动
    private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        //这里涉及到一个ServiceLoader概念。上面我推荐了一篇文章,想了解的可以去看看。通过ServiceLoader去加载所有的driver。
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                //通过ServiceLoader.load()方法加载所有驱动
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {

                }
                return null;
            }
        });

        println("DriverManager.initialize: jdbc.drivers = " + drivers);

        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

    //3个获取connection方法,对外提供的方法。
    @CallerSensitive
    public static Connection getConnection(String url, java.util.Properties info) throws SQLException {

        return (getConnection(url, info, Reflection.getCallerClass()));
    }   
    //这个方法中可以看到,properties中至少配置参数其实就是user和password
    @CallerSensitive
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }

    @CallerSensitive
    public static Connection getConnection(String url)
        throws SQLException {

        java.util.Properties info = new java.util.Properties();
        return (getConnection(url, info, Reflection.getCallerClass()));
    }
     // 内部真正工作的方法(私有)
    private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        //检查callerCL是否为空,如果为空则通过Thread.currentThread().getContextClassLoader()去加载
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }
        //这里判断url
        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");
        SQLException reason = null;
        //遍历registeredDrivers去获得正确的connection
        for(DriverInfo aDriver : registeredDrivers) {
            // 如果callerCL不允许读取驱动,就会跳过。
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    //真正的获取connection的方法,其实还是通过driver接口中的connect方法。
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }

    //通过url获取driver。
    @CallerSensitive
    public static Driver getDriver(String url)
        throws SQLException {

        println("DriverManager.getDriver(\"" + url + "\")");

        Class<?> callerClass = Reflection.getCallerClass();

        // 通过遍历registeredDrivers中每个驱动
        for (DriverInfo aDriver : registeredDrivers) {
            // acceptsURL()方法判断url是否符合driver
            if(isDriverAllowed(aDriver.driver, callerClass)) {
                try {
                    if(aDriver.driver.acceptsURL(url)) {
                        println("getDriver returning " + aDriver.driver.getClass().getName());
                    return (aDriver.driver);
                    }

                } catch(SQLException sqe) {

                }
            } else {
                println("    skipping: " + aDriver.driver.getClass().getName());
            }

        }

        println("getDriver: no suitable driver");
        throw new SQLException("No suitable driver", "08001");
    }

    //注册驱动的方法
    public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {
        //调用下面的方法
        registerDriver(driver, null);
    }
    //
    public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {
        //判断driver是否已经被加载到registeredDrivers,没有就加进去
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            throw new NullPointerException();
        }
        println("registerDriver: " + driver);
    }

    //注销driver方法。
    @CallerSensitive
    public static synchronized void deregisterDriver(Driver driver)
        throws SQLException {
        if (driver == null) {
            return;
        }
        SecurityManager sec = System.getSecurityManager();
        if (sec != null) {
            sec.checkPermission(DEREGISTER_DRIVER_PERMISSION);
        }
        println("DriverManager.deregisterDriver: " + driver);
        DriverInfo aDriver = new DriverInfo(driver, null);
        if(registeredDrivers.contains(aDriver)) {
            if (isDriverAllowed(driver, Reflection.getCallerClass())) {
                DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver));
                 if(di.action() != null) {
                     di.action().deregister();
                 }
                 //通过remove()注销。
                 registeredDrivers.remove(aDriver);
            } else {
                throw new SecurityException();
            }
        } else {
            println("    couldn't find driver to unload");
        }
    }   
}
getConnection这个方法中的properties类注意一下,它是以后能用到的以.properties为后缀名的属性文件

第二点:获取数据库链接Connection

代表与数据库的链接,并拥有创建SQL语句的方法,以完成基本的SQL操作,同时为数据库事务提供提交和回滚方法

这里说一下Connection接口的重要方法:

public interface Connection  extends Wrapper, AutoCloseable {
    //创建statement
    Statement createStatement() throws SQLException;
    //创建prepareStatement
    PreparedStatement prepareStatement(String sql)
        throws SQLException;
    //创建CallableStatement
    CallableStatement prepareCall(String sql) throws SQLException;
    //转换sql为本机执行sql
    String nativeSQL(String sql) throws SQLException;
    //设置是否自动提交 状态
    void setAutoCommit(boolean autoCommit) throws SQLException;
    //判断是否是自动提交
    boolean getAutoCommit() throws SQLException;
    //提交
    void commit() throws SQLException;
    //回滚
    void rollback() throws SQLException;
    //关闭连接
    void close() throws SQLException;
    //判断是否关闭
    boolean isClosed() throws SQLException;
    ...//都是一些规范的接口,太多就不一一列举了。
}

第三点:获取statement、preparedstatement

statement和preparedStatement的作用是一样的,都是用来执行sql。区别:

1.代码的可读性和可维护性. 
虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次: 
stmt.executeUpdate(“insert into tb_name (col1,col2,col2,col4) values (‘”+var1+”’,’”+var2+”’,”+var3+”,’”+var4+”’)”);//stmt是Statement对象实例

perstmt = con.prepareStatement(“insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)”); 
perstmt.setString(1,var1); 
perstmt.setString(2,var2); 
perstmt.setString(3,var3); 
perstmt.setString(4,var4); 
perstmt.executeUpdate(); //prestmt是 PreparedStatement 对象实例

2.PreparedStatement尽最大可能提高性能. 

3.最重要的一点是极大地提高了安全性.

由此可见:开发的时候尽量用preparedStatement,少用statement。 

preparedStatement中一些重要的方法:

public interface PreparedStatement extends Statement {
    //用于产生单个结果集的语句,例如 SELECT 语句
    ResultSet executeQuery() throws SQLException;
    //用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句
    int executeUpdate() throws SQLException;
    //设置空值,必须穿入type,不然可能报空指针异常
    void setNull(int parameterIndex, int sqlType) throws SQLException;
    ...(同理有很多set的方法)
    //清空属性
    void clearParameters() throws SQLException;
    //用于执行返回多个结果集、多个更新计数或二者组合的语句
    boolean execute() throws SQLException;
    ...//都是一些规范的接口,太多就不一一列举了。
}

还有一个接口CallableStatement 提供了一种以标准形式调用已储存过程的方法。

第四点:ResultSet

结果集(ResultSet)是数据中查询结果返回的一种对象,可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功能,可能完成对数据的更新等

ResultSet中一些重要的方法:

public interface ResultSet extends Wrapper, AutoCloseable {
    //是否有下一个值
    boolean next() throws SQLException;
    //关闭
    void close() throws SQLException;
    //是否为空
    boolean wasNull() throws SQLException;
    //得到第几列的String类型数据
    String getString(int columnIndex) throws SQLException;
    boolean getBoolean(int columnIndex) throws SQLException;
    ...(太多get方法不一一列举)
    //得到列名为columnLabel的值
    String getString(String columnLabel) throws SQLException;

    //更新第几列为空(各种update方法)
    void updateNull(int columnIndex) throws SQLException;
    //插入(updateRow、deleteRow、refreshRow 等)
    void insertRow() throws SQLException;
}

以上就是jdbc中所注意的类,接口源码。

posted @ 2018-06-26 15:56  it飞  阅读(5147)  评论(0编辑  收藏  举报