JDBC——概述与JDBC的使用

引言

一直希望深入学习一下数据库持久化技术,接触过Hibernate、Mybatis,也使用过Spring事务管理来控制回滚操作,但是越发觉得底层知识有一定的知识盲区和空洞。

很多ORM框架都是基于JDBC规范来进行构建的,因此,学习JDBC的基础知识势在必行。虽然不建议在实际开发中使用 JDBC API,但了解其技术背景和使用过程无疑会更好的理解构建于其上的高级框架。

本篇博客总结自尚硅谷宋红康老师的视频教程,旨在记录和总结JDBC API的使用步骤和常见问题,方便未来面试和深入理解其他框架。

一、JDBC概述

1.1 JDBC 介绍

JDBC,Java Database Connectivity,Java数据库连接技术。这是JDK原生的独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)。

这套API包含在 java.sql. 和 javax.sql 包下。使用这套API 可以以一种标准的方法访问数据库资源。

JDBC产生的原因是由于各个数据库厂商,如MySQL、Oracle、SQL Server、DB2(IBM)等,都有属于自己数据库特有的连接和访问方式,从连接驱动到增删改查,都存在各种差异。

JDBC的目标是使Java程序员无需对特定的数据库系统的特点有过多了解,也能够快速完成数据库访问等开发工作。

1.2 JDBC体系结构

JDBC接口包括两个层次:

  • 面向应用的API:Java API,抽象接口,供应用开发者使用,包括连接数据库、执行SQL语句、获得结果等。
  • 面向数据库的API:Java Driver API,供数据库服务商实现特定于自家数据库的驱动程序。MySQL的驱动就是我们经常需要引入的mysql-connector-java.jar 。

1.3 JDBC的编程步骤

jdbc编程步骤有以下几步固定操作:

1、添加数据库驱动依赖,例如:MySQL的 mysql-connector-java.jar,这是一种纯Java实现的驱动程序,除此之外,微软的SQL Server,需要JDBC-ODBC桥方式。

2、加载并注册驱动程序

3、创建 Connection 对象

4、创建 Statement 对象

5、执行 SQL 语句

6、若为查询操作,需要额外处理 ResultSet 结果集

7、关闭 Statement 对象

8、关闭 Connection 对象

补充:ODBC(Open Database Connectivity 开放式数据库连接),是微软在 Windows 平台下推出的。

二、JDBC编程实践

在使用 JDBC 进行编程前,有一些不可或缺的要素:

1、Driver 接口实现:是JDBC API中对数据库驱动程序的接口定义,不同的数据库厂商会自定义实现,但都需要符合Driver接口标准。一般使用MySQL,就需要引入mysql-connector-java驱动包。

包中提供的 com.mysql.jdbc.Driver(高版本已经废弃,改为com.mysql.cj.jdbc.Driver) 就是java.sql.Driver接口的实现类。

2、数据库连接信息:URL、用户名、密码等。

JDBC URL的标准由三部分组成,各部分用冒号分隔,例如:

jdbc:mysql://localhost:3306/test?serverTimezone=UTC

jdbc是主协议名,mysql是子协议,JDBC URL的主协议始终是 jdbc。

以下代码为 jdbc 实现入库操作的最终版本,后面的章节将会进一步探讨可以优化或需要注意的点,但是以下代码作为学习范例,已经足够展现JDBC 编程的绝大部分内容。

import java.sql.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

public class PreparedStatementUpdTest {

    public static void main(String[] args) {
        jdbcTest();
    }

    public static void jdbcTest() {
        // 创建驱动
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 声明连接信息
            String url = "jdbc:mysql://localhost:3306/learn_mysql?serverTimezone=UTC";
            String username = "root";
            String password = "123456";
            // 创建连接
            connection = DriverManager.getConnection(url, username, password);
            String sql = "INSERT INTO user(name, birth_day) VALUES(?, ?)";
            // SQL预编译
            ps = connection.prepareStatement(sql);
            ps.setObject(1, "Lisa");
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            java.util.Date birthday = format.parse("2021-02-12");
            // util.Date和sql.Date的共同点是毫秒数一样
            ps.setObject(2, new Date(birthday.getTime()));
            // 执行SQL语句,executeUpdate()方法可以返回 int 受影响行数
            ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (ps != null)
                    ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (connection != null)
                    connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

三、JDBC编程实践的优化与思考

3.1 创建驱动实例

在程序编写之初,我们已经通过依赖管理,将mysql-connector-java.jar 包引入类路径下,那么 JDBC 的 Driver 接口就有了针对于MySQL的驱动器实现——com.mysql.cj.jdbc.Driver。

实际上,我们可以通过最原始的方式 new 一个出来

// 直接 new 创建驱动实例,不要这么做!
Driver driver = new com.mysql.cj.jdbc.Driver();
// 声明连接信息(略)
// 从 Driver 中获取 Connection
Connection connection = driver.connect(连接信息);

但是通过 Class.forName(..) 获得驱动对象的好处就是可以将类名作为配置放到程序外,方便替换其他的数据库驱动,有更好的移植性:

// 通过反射获取驱动,有更强的可移植性
Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
// 声明连接信息(略)
// 从 Driver 中获取 Connection
Connection connection = driver.connect(连接信息);

3.2 DriverManager与Connection获取方式的

Driver 接口有获取Connection的方法,这是最初获取Connection的方式。

Connection connection = driver.connect(连接信息);

JDBC API提供了一个名为 DriverManager 的基础服务类,可以管理 Driver 驱动程序,并创建 Connection 对象,于是有了通过 DriverManager 获取 Connection的方式,但前提是需要将驱动对象注册到 DriverManager 中

// 创建驱动
Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
// 注册驱动
DriverManager.registerDriver(driver);
// 连接信息(略)
// 从驱动管理器中获取连接
Connection connection = DriverManager.getConnection(url, username, password);

其实,“注册驱动”的操作并不需要应用开发者来完成,在 Driver 类加载时,就会自动注册,原因就是以下静态代码块:

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!");
        }
    }
}

因此,在 Class.forName(..) 完成后,mysql Driver 类已经成功加载,即已经完成了驱动注册步骤,因此我们可以更加简化我们获取Connection的代码:

Class.forName("com.mysql.cj.jdbc.Driver");
// 声明数据库连接信息(略)
Connection connection = DriverManager.getConnection(url, user, password);

另外,值得一提的是,Class.forName("com.mysql.cj.jdbc.Driver") 加载驱动这一步也可以省略,

这是因为当 mysql-connector-java.jar 引入类路径后,会默认自动加载 META-INF/services/java.sql.Driver中配置的驱动实现类,但是建议不要省略,因为如果迁移了其他数据库驱动,可能不会有这样的默认操作。

3.3 配置化数据库连接信息

在前面的例子中,其实已经可以轻松的获取到数据库连接:

Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/learn_mysql?serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);

我们可以进一步将驱动类信息、连接信息转移到程序之外,以配置的形式存在:

然后通过 Properties 获取配置数据:

InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties props = new Properties();
props.load(is);
String url = props.getProperty("url");
String username = props.getProperty("username");
String password = props.getProperty("password");
String driverName = props.getProperty("driverName");
// 加载驱动类
Class.forName(driverName);
// 获取连接
Connection connection = DriverManager.getConnection(url, username, password);

通过系统类加载器获取配置文件数据,默认读取 src 目录下的文件,以 properties 结尾的文件可以直接被 load 进 Properties 对象中。

3.4 Statement与PreparedStatement

数据库连接被用于向数据库服务器发送命令和SQL语句,并接收数据库返回的结果。其实,一个数据库连接就是一个Socket连接。

在java.sql包中有 3 个接口分别定义了对数据库的调用方式:

  • Statement:用于执行静态SQL语句并返回它所生成结果的对象。
  • PreparedStatement:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效地执行SQL语句。
  • CallableStatement:用于执行SQL存储过程。

 原始的Statement存在一定的弊端,由于只能处理静态SQL语句,因此只能将变量直接拼接到SQL中才可以使用,除此之外,更严重的问题还是SQL注入

例如下面这条拼接的SQL语句:

String sql = "SELECT user, password FROM user_table WHERE user='" + username + "' AND password='" + password + "'";

如果username和password分别是:

String username = "1' OR ";
String password = " = 1 OR '1' = '1";

那么sql 的WHERE条件就是一个恒成立的情况,这就是SQL注入:

SELECT user, password FROM user_table WHERE user='1' OR ' AND password=' = 1 OR '1' = '1'

PreparedStatement表示一个可以预编译SQL语句的对象,使用“?”占位符来明确区分 SQL语法与参数,可以有效防止SQL注入问题。

因此,Statement 已不再使用了,取而代之的是可以进行预编译SQL的PreparedStatement。 

总结

JDBC是数据库访问的公共规范,它的编程步骤主要分为几点:

1、引入对应数据库厂商的驱动包

2、加载、注册驱动(类加载的同时即完成注册)

3、从驱动管理器 DriverManager 中直接通过连接信息创建一个 Connection 对象

4、使用 Connection 对象以预编译方式创建 PreparedStatement 对象

5、填充 PreparedStatement 属性值

6、使用PreparedStatement 执行SQL 操作(查询操作需要处理结果集)

7、以资源创建的先后顺序,逆向关闭资源

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页