• 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中所注意的类,接口源码。

  • 相关阅读:
    Shiro框架:Failed to deserialize java.lang.Exception: Failed to deserialize问题解决
    Linux系统:CentOS防火墙的各种命令使用
    Linux系统:CentOS下vsftpd的安装配置
    Vue项目启动报错:UnhandledPromiseRejectionWarning: TypeError: loaderContext.getResolve is not a function问题解决
    Minio存储桶:部署存储服务以及设置永久下载链接
    Redis之Bitmaps
    Redis事务与Lua
    Redis Pipeline
    Redis Shell详解
    Redis慢查询
  • 原文地址:https://www.cnblogs.com/itgaofei/p/9229540.html
Copyright © 2020-2023  润新知