JDBC使用与理解

什么是JDBC

Java数据库连接,(Java Database
Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。
那就是是说Java定义了一套接口,而对应的数据库厂商需要实现这个接口(也就是所谓的驱动)。

简单理解:Java规定了访问数据库的API,数据库厂商去实现它
这个API就是java.sql路径下的一系列接口,比如说

以mysql为例

  • java.sql.Driver接口,实现就是com.mysql.jdbc.Driver
  • java.sql.Connection接口,实现就是com.mysql.cj.jdbc.ConnectionImpl
  • java.sql.Statement接口,实现就是com.mysql.cj.jdbc.StatementImpl
  • …等等还有很多

下述来源于wiki百科

JDBC API主要位于JDK中的java.sql包中(之后扩展的内容位于javax.sql包中),主要包括(斜体代表接口,需驱动程序提供者来具体实现):

  • DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。
  • Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。
  • Connection:数据库连接,负责进行与数据库间的通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。
  • Statement:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。
  • PreparedStatement:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。
  • CallableStatement:用以调用数据库中的存储过程。
  • SQLException:代表在数据库连接的建立和关闭和SQL语句的执行过程中发生了例外情况(即错误

使用方式

使用方式其实还是比较简单

  1. 加载对应数据库的驱动(Driver)
  2. 通过DriverManager来获取连接(Connection)
  3. 创建对应语句(StatementPreparedStatement)
  4. 接收执行结果(ResultSet)
通过DriverManager
创建语句
创建语句
执行语句
执行语句
class加载Driver
获取Connection
Statement
PreparedStatement
ResultSet
ResultSet

对应代码:

        Connection connection = null;
        Statement statement = null;
        try {
        	//步骤1:加载Driver
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&nullCatalogMeansCurrent=true";
            String username = "root";
            String password = "123456";
            String sql = "select * from `test_info`";
            //步骤2:通过`DriverManager`来获取连接(`Connection`)
            connection = DriverManager.getConnection(url, username, password);
            //步骤3:创建对应语句(`Statement`,`PreparedStatement`)
            statement = connection.createStatement();
            //步骤4:接收执行结果(`ResultSet`)
            ResultSet resultSet = statement.executeQuery(sql);
        } finally {
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }

首先,步骤1中将com.mysql.jdbc.Driver加载到虚拟机中后,会执行一个静态代码块:

注意: 这个Driver是com.mysql.cj.jdbc.Driver,上面的com.mysql.jdbc.Driver继承于它。com.mysql.cj.jdbc.Driver作用就是当它被加载的时候自动将自己注册到驱动管理器中。

public class Driver extends NonRegisteringDriver implements java.sql.Driver {

    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    public Driver() throws SQLException {
    }
}

步骤二中,通过DriverManager.getConnection(url, username, password)来获取到连接Connection

问: 如果我同时加载Mysql的驱动和Oracle的驱动,那么DriverManager如何去区分使用哪一个驱动
答:通过url中的协议,比如说 jdbc:mysql://localhost:3306 ,在mysql中com.mysql.cj.conf.ConnectionUrl来解析url判断是否是驱动可以处理。

轮训所有的Driver,如果Driver返回了Connection就跳出循环返回,这就需要驱动厂商自己根据url来判断自己的驱动是否能支持该url对应的连接。

private static Connection getConnection(
       ...
        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

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

        }

		...
		
    }

以mysql为例,算了代码太长了,感兴趣的可以看下com.mysql.cj.jdbc.NonRegisteringDriver

第三步,需要创建一个StatementPreparedStatement 来执行sql,它们的区别在于,Statement执行的就是纯纯的sql语句,而PreparedStatement代表着未完成的sql,也就是需要后续额外的数据来补充sql。

例子:
select * from test_info,使用 Statement
select * from test_info where id = ?,使用PreparedStatement

String sql = "select * from `test_info` where id=? and name=?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setLong(1, 1L);
preparedStatement.setString(2, "hahaha");
ResultSet resultSet = preparedStatement.executeQuery();

如果想看PreparedStatement处理后的sql可以直接进行打印:

System.out.println("sql:" + preparedStatement);

mysql对于PreparedStatement方法的实现是ClientPreparedStatement,其中的toString方法被重写了

    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(this.getClass().getName());
        buf.append(": ");
        buf.append(((PreparedQuery) this.query).asSql());
        return buf.toString();
    }

第四步,接收数据库返回的结果并解析ResultSet

    private static void printResultSet(ResultSet resultSet) throws SQLException {
        while (resultSet.next()) {
            ResultSetMetaData metaData = resultSet.getMetaData();
            printMetaData(metaData, resultSet);
        }
    }
    
        private static void printMetaData(ResultSetMetaData metaData, ResultSet resultSet) throws SQLException {
        StringBuilder builder = new StringBuilder();
        int columnCount = metaData.getColumnCount();
        for (int i = 1; i <= columnCount; i++) {
            String type = metaData.getColumnTypeName(i);
            builder.append("列名称:"+metaData.getColumnName(i)+" ");
            builder.append("列类型:"+metaData.getColumnTypeName(i)+" ");
            builder.append("数值:");
            if (type.equals("BIGINT")) {
                builder.append(resultSet.getLong(i));
            } else if (type.equals("INT")) {
                builder.append(resultSet.getInt(i));
            } else if (type.equals("DATETIME")) {
                builder.append(DateUtil.formatDateTime(resultSet.getDate(i)));
            }
            builder.append(" ");
            builder.append("\n");
        }
        System.out.println(builder);
    }

控制台的输出结果:

列名称:id 列类型:BIGINT 数值:1 
列名称:test_enum 列类型:INT 数值:1 
列名称:tenant_id 列类型:BIGINT 数值:4 
列名称:test_two_enum 列类型:INT 数值:2 
列名称:created_time 列类型:DATETIME 数值:2022-12-27 00:00:00 
列名称:time 列类型:DATETIME 数值:null 

对于事务的提交和级别的设置是在Connection实例中

try {	
			//设置事务为不自动提交
            connection.setAutoCommit(false);
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setLong(1, 1L);
            ResultSet resultSet = preparedStatement.executeQuery();
            //提交事务
            connection.commit();
        } catch (Exception ex){
            if(connection!=null){
            	//回滚事务
                connection.rollback();
            }
        }

隔离级别connection.setTransactionIsolation();方法
mysql的使用枚举为org.apache.ibatis.session.TransactionIsolationLevel

ok,本次分享到这里就结束了,本章比较简单。
分享的原因是后续想在最近过年比较清闲的时间里面,将mybatis的前置知识先铺垫完,后续在好好整理下mybatis

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐芬奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值