学习:JDBC基本使用步骤忘了还好意思学JAVA吗?

先给自己两个小拳拳。。。
整理JDBC,结合代码整理一下JDBC的基本使用


基本思路:

前提条件,Java Database Connectivity 是需要引入相关数据库连接的驱动jar包的,本文以 MySQL 为例。
一、首先要加载数据库驱动 Driver
二、然后,获取链接 Connection
三、获取链接得到 statement 接口的实现类,来执行 SQL 语句
四、获取返回值或查询结果,并遍历、操作结果。
五、关闭资源

代码:

直接上代码吧,最基础的JDBC使用步骤,用的话需要将对应位置的参数更换成自己的,比如域名,端口号,数据库名和 SQL 语句

public static void main(String[] args){
//1.加载驱动类
	Class.forName("com.mysql.jdbc.Driver");
	
//2.获取链接
	//数据库链接地址 url  IP:域名  Port:端口号  Database_Name:数据库名 
	String url = "jdbc:mysql://IP:Port/Database_Name?characterEncoding=utf8&serverTimeZone=GMT";
	String username = "root";//数据库用户名
	String password = "root";//数据库密码
	Connection conn = DriverManager.getConnection(url,username,password);
	
//3.获取statement
	Statement state = conn.createStatement();
	
//4. (1)执行Sql,(2)获取返回值,(3)操作返回值
	String insertSql = "insert into Table_Name(Colum_name) values(value)";//插入语句
	int insertResult = state.updateExcute(insertSql);//返回值为int型,返回执行成功的行数
	//查询语句
	ResultSet qureyResult = state.queryExcute();//返回值为ResultSet结果集,里面包含了从数据库中查询到的结果的集合
	while(queryResult.next()){//通过next()方法,遍历结果集,注意每次调用next()方法都会将指针指向下一个结果,由此机制来遍历结果集直到没有下一个节点
		queryResult.getInt("colum1");//获取列名为colum1的值,返回的是int值,选择的方法一定要与列名保存的数据类型一致
		queryResult.getString("colum2");//返回值是String,方法还有很多,根据实际的表对应的列名调用
	}              
	
	//5.关闭资源,先开启的资源最后关闭
	resultSet.close();
	state.close();
	conn.close();
}

代码分析:

既然要做分析,那就尝试从源码角度跟踪,了解一下这简单的五个步骤,都是干嘛的,干了什么?

  1. 第一步,通过类名加载驱动。这里涉及的知识点较多,包括类加载机制和反射机制。
    简单来说 Class.forName(“类的全限定名”) 的类加载,就是通过类名获得这个类的 类对象 ,在这个过程中需要加载这个类,同时会将类中的静态属性,方法,代码块一并加载。

    获取类对象的源码就不看了,看一眼Driver的源码:

    package com.mysql.jdbc;
    
    import java.sql.DriverManager;
    import java.sql.SQLException;
    public class Driver extends NonRegisteringDriver implements java.sql.Driver {
        public Driver() throws SQLException {
        }
    
        static {
            try {
                DriverManager.registerDriver(new Driver());
           } catch (SQLException var1) {
                throw new RuntimeException("Can't register driver!");
          }
     }
    }
    

    通过源码可以看到在类加载中,执行了静态代码块中的内容:

             将MySQL驱动包中的Driver,通过registerDriver注册给了jdk的java.sql包下的DriverManager中,由于Driver继承了NonRegisteringDriver,那么他就天生的拥有父类中的某些方法,属性等。那么这些属性就可以被java.sql.DriverManager获得到了。即完成了驱动的注册。

    也就是说这个步骤告诉 DriverManager 我要使用 MySQL 的驱动操作 MySQL 数据库了,并给予 DriverManager 操作数据库相关的重写方法。

  2. 第二步,获取链接,通过DriverManager.getConnection()方式获得的。

    @CallerSensitive
    public static Connection getConnection(String url, String user, String password) throws SQLException {
        Properties info = new Properties();
        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }
        return getConnection(url, info, Reflection.getCallerClass());
    }
    

    可以看到,我们调的getConnection(url, username , password)方法中并没有直接返回链接,而是先将用户名和密码放入properties中后,连同url和调用此方法的那个类的类对象,传入它内部的一个私有方法getConnection(url,info,Reflection.getCallerClass())来获取连接的。
    下面是Reflection.getCallerClass()这个静态方法的源码。

    public static native Class<?> getCallerClass();
    

    可以看到这个方法被native修饰了,那么具体的调用是底层有关C或C++函数库的调用。这里是非JAVA代码了。既然咱是学JAVA的,这就不归咱管了。
    回头看那个私有的 getConnection() 源码

    //......省略以上源码
    Connection con = aDriver.driver.connect(url, info);//获取链接
    if (con != null) {//如果获取到了链接
    	//这句是内部封装的打印日志信息
     	println("getConnection returning " + aDriver.driver.getClass().getName());
     	return con;//返回链接
    }
    //......省略以下源码
    

    在这个方法中可以看到获取了链接并返回。

  3. 第三步,获取 Statement 接口的实现类,这个接口中有关于SQL执行的方法(源码或在API中查看),这里通过 Connection 链接来创建一个 statement 接口的实现类。
    想追这个接口是如何获得的,先从 MySQL 驱动包中的 Driver 类开始说。上方 com.mysql.jdbc.Driver 源码,可以看见,这个类继承了 NonRegisteringDriver 父类中的方法 ,又实现了 java.sql.Driver 接口。这个接口中的方法:

    Connection connect(String var1, Properties var2) throws SQLException;
    

    通过调用这个接口的方法实际上执行的是 NonRegisteringDriver 父类中的 connect 方法:

    public Connection connect(String url, Properties info) throws SQLException {
    //......省略源码
        //通过ConnectionImpl的静态方法创建实现类
       	com.mysql.jdbc.Connection newConn = ConnectionImpl.getInstance(this.host(props), this.port(props), props, this.database(props), url);
        return newConn;//返回这个实现类
     //......省略源码
    }
    

    可以获取到一个链接,这个就是上方 getConnection 获取连接的方法,父接口 Connection 引用的是 ConnectionImpl 这个子类对象,这个方法最终返回到我们在第二步调用的 Connection conn = DriverManager.getConnection();

    到这里我们终于得到了 java.sql.Connection 的实现类 java.mysql.jdbc.ConnectionImpl 对象,那么Statement 接口的实现类是如何获得的呢
    继续看 conn 的方法createStatement();

    public java.sql.Statement createStatement() throws SQLException {
        return this.createStatement(1003, 1007);
    }
    
    public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        this.checkClosed();
        StatementImpl stmt = new StatementImpl(this.getMultiHostSafeProxy(), this.database);
        stmt.setResultSetType(resultSetType);
        stmt.setResultSetConcurrency(resultSetConcurrency);
        return stmt;
    }
    

    级联调了两个方法,最终我们获得了 java.mysql.jdbc.StatementImpl 的对象。

    值得注意的是: 自从将 java.mysql.jdbc.Driver 注册给 DriverManager 之后,我们获取到的实现类,子类,执行的方法都是MySQL驱动jar包中提供的。这里充分体现出了 Java 语言的多态。

  4. 第四步,执行sql,操作返回值。
    通过实现类的相关方法执行SQL语句。statement的有关方法举几个例子:

    Statement state = conn.createStatement();
    //插入,更新,删除操作可以通过此方法完成
    int result = state.executeUpdate("sql语句");//返回值是 影响行数
    //查询操作可以通过此方法完成
    ResultSet r = state.executeQuery("select * from t_student");//返回值是 查询结果
    //通过next()方法来遍历ResultSet集合
    while(r.next()){
    	//在这里对当前遍历到的 r 对象进行操作
    }
    

    简单的跟一下 ResultSet ,原本想从 executeQuery() 方法跟进去看一下如何返回的结果,奈何方法封装的太深,直接看的话该方法返回的是一个 ResultSetInternalMethods 接口的实现类,懒得一步步找,就简单判断了一下。
    逻辑为:ResultSet 接口的 next 方法的实现类只有两个,即 ResultSetImpl 和 UpdatableResultSet 那么就可以判断 executeQuery 方法返回的 ResultSetInternalMethods 接口的实现类就是这两只之中的一种。

    而且,UpdatableResultSet 是 ResultSetImpl 的子类,其中 next 方法直接 调用了父类中的 next 方法,见下图: 在这里插入图片描述
    上方是 ResultSet 接口的 next 方法 的实现类,其中只有两个类是 MySQL 驱动包的,即,ResultSetImpl 和 UpdatableResultSet 两个类。观察发现 UpdatableResultSet 是 ReultSetImpl 的子类。
    先看一下 UpdatableResultSet 的 next 方法 源码:

    public synchronized boolean next() throws SQLException {
        return super.next();
    }
    

    可以看到,方法直接就调用了父类 ResultSetImpl 中的 next 方法,然后看一下 ResultSetImpl 中的 next 方法源码:

    public boolean next() throws SQLException {
        synchronized(this.checkClosed().getConnectionMutex()) {
           //......省略源码
           if (!this.reallyResult()) {
           //......省略源码
            } else {
                //.......省略源码
                boolean b;
                if (this.rowData.size() == 0) {
                //如果数据是空的 next方法就返回 false ,
                //那么外部就会结束遍历 resultSet 的 while(resultSet.next()) 循环
                    b = false;
                } else {
                    this.thisRow = this.rowData.next();//将指针指向下一个结点的数据
                    if (this.thisRow == null) {
                    	//如果当前指针的数据是空的,那么就返回 false
                        b = false;
                    } else {
                        this.clearWarnings();
                        b = true;//不是空的就返回true,外部while 循环继续遍历结果集
                    }
                }
                this.setRowPositionValidity();
                return b;
            }
        }
    }
    

    到这里,我们对next方法有了简单的了解。

  5. 第五步,关闭资源,这没啥好说的了,具体怎么关的就随他去吧,close() 方法调就完了。使用时注意关闭资源的顺序,先打开的资源最后关闭。


总结

在一开始的类加载注册,到获取链接,再到获取statement,遍历 resultSet 结果集,最后关闭资源,从源码角度进行了一定的追踪分析,只能说简单的了解了这五个步骤执行JDBC操作的底层原理大致是个什么路子。而不单单停留在告诉你怎么用你就怎么用。
在之后的学习中。要多了解方法和机制的底层原理,多跟进代码内部观察。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值