Java 编程之 JDBC

0x01 前言

JDBC(Java DataBase Connectivity,java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

JDBC架构

JDBC API 支持两层和三层处理模型进行数据库访问,但在一般的 JDBC 体系结构由两层组成:

JDBC API:提供了应用程序对 JDBC 的管理连接。

JDBC Driver API:支持 JDBC 管理到驱动器连接。

JDBC API 的使用驱动程序管理器和数据库特定的驱动程序提供透明的连接到异构数据库。

JDBC 驱动程序管理器可确保正确的驱动程序来访问每个数据源。该驱动程序管理器能够支持连接到多个异构数据库的多个并发的驱动程序。

以下是JDBC结构图,它显示了驱动程序管理器方面的 JDBC 驱动程序和 Java 应用程序的位置。

JDBC 结构图.jpg

常见的JDBC组件

  • DriverManager:这个类管理数据库驱动程序的列表。确定内容是否符合从 Java 应用程序使用的通信子协议正确的数据库驱动程序的连接请求。识别 JDBC 在一定子协议的第一个驱动器将被用来建立数据库连接。

  • Driver: 此接口处理与数据库服务器通信。很少直接直接使用驱动程序(Driver)对象,一般使用 DriverManager 中的对象,它用于管理此类型的对象。它也抽象与驱动程序对象工作相关的详细信息。

  • Connection:此接口与接触数据库的所有方法。连接对象表示通信上下文,即,与数据库中的所有的通信是通过此唯一的连接对象。

  • Statement:可以使用这个接口创建的对象的SQL语句提交到数据库。一些派生的接口接受除执行存储过程的参数。

  • ResultSet:这些对象保存从数据库后,执行使用 Statement 对象的 SQL 查询中检索数据。它作为一个迭代器,可以通过移动它来检索下一个数据。

  • SQLException:这个类用于处理发生在数据库应用程序中的任何错误。

0x02 创建简单的一个 JDBC

构建 JDBC 应用程序涉及以下六个步骤:

  1. 导入包:需要包含包含数据库编程所需的 JDBC 类的包。大多数情况下,使用 import java.sql.* 就足够了。

  2. 注册 JDBC 驱动程序:需要初始化驱动程序,以便可以打开与数据库的通信通道。

  3. 打开一个连接:需要使用 DriverManager.getConnection() 方法创建一个 Connection 对象,它表示与数据库的物理连接。

  4. 执行查询:需要使用类型为 Statement 的对象来构建和提交 SQL 语句到数据库。

  5. 从结果集中提取数据:需要使用相应的 ResultSet.getXXX() 方法从结果集中检索数据。

  6. 清理环境:需要明确地关闭所有数据库资源,而不依赖于 JVM 的垃圾收集。

  7. 口诀:(贾琏欲执事)。

具体实现如下:

1. 导入包

在程序中包含数据库编程所需的 JDBC 类。大多数情况下,使用 import java.sql.* 就足够了,如下所示。

//STEP 1. Import required packages
import java.sql.*;

2. 注册JDBC驱动程序

需要初始化驱动程序,这样就可以打开与数据库的通信。以下是代码片段实现这一目标。

//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");

3. 打开一个连接

使用 DriverManager.getConnection() 方法来创建一个 Connection 对象,它代表一个数据库的物理连接,如下所示。

//STEP 3: Open a connection
//Database credentials
static final String USER = "root";
static final String PASS = "pwd123456";
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);

4. 执行一个查询

需要使用一个类型为 StatementPreparedStatement 的对象,并提交一个 SQL 语句到数据库执行查询。如下:

//STEP 4: Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);

如果要执行一个SQL语句:UPDATEINSERTDELETE 语句,那么需要下面的代码片段:

//STEP 4: Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "DELETE FROM Employees";
ResultSet rs = stmt.executeUpdate(sql);

5. 从结果集中提取数据

这一步中演示如何从数据库中获取查询结果的数据。可以使用适当的 ResultSet.getXXX() 方法来检索的数据结果如下。

//STEP 5: Extract data from result set
while(rs.next()){
    //Retrieve by column name
    int id= rs.getInt("id");
    int age = rs.getInt("age");
    String first = rs.getString("first");
    String last = rs.getString("last");
    //Display values
    System.out.print("ID: " + id);
    System.out.print(", Age: " + age);
    System.out.print(", First: " + first);
    System.out.println(", Last: " + last);
}

6. 清理环境资源

在使用 JDBC 与数据交互操作数据库中的数据后,应该明确地关闭所有的数据库资源以减少资源的浪费,对依赖于 JVM 的垃圾收集如下。

//STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();

0x03 JDBC Statements, PreparedStatement 和 CallableStatement 语句

接口 推荐使用
Statement 用于对数据库进行通用访问,在运行时使用静态SQL语句时很有用。Statement 接口不能接受参数。
PreparedStatement 当计划要多次使用SQL语句时使用。PreparedStatement 接口在运行时接受输入参数。
CallableStatement 当想要访问数据库存储过程时使用。CallableStatement 接口也可以接受运行时输入参数。

处理 Statement 对象

创建 Statement 对象

在使用 Statement 对象执行 SQL 语句之前,需要使用 Connection 对象的 createStatement() 方法创建一个 Statement 对象。

在创建 Statement 对象后,可以使用它来执行一个SQL语句,它有三个执行方法可以执行。

  • boolean execute (String SQL):如果可以检索到 ResultSet 对象,则返回一个布尔值 true ; 否则返回 false 。使用此方法执行 SQL DDL 语句或需要使用真正的动态 SQL,可使用于执行创建数据库,创建表的 SQL 语句等等。

  • int executeUpdate (String SQL):返回受 SQL 语句执行影响的行数。使用此方法执行预期会影响多行的 SQL 语句,例如:INSERTUPDATEDELETE 语句。

  • ResultSet executeQuery(String SQL):返回一个 ResultSet 对象。 当您希望获得结果集时,请使用此方法,就像使用 SELECT 语句一样。

关闭 Statement 对象

操作结束后,应关闭 Statement 对象。就像关闭一个 Connection 对象一样,以保存数据库资源一样,由于同样的原因,还应该关闭 Statement 对象。

一个简单的调用 close() 方法将执行该作业(工作)。 如果先关闭 Connection 对象,它也会关闭 Statement 对象。但是,应该始终显式关闭 Statement 对象,以确保正确的清理顺序。

Statement stmt = null;
try {
    stmt = conn.createStatement( );
    // TODO
}
catch (SQLException e) {
    // TODO
}
finally {
    stmt.close();
}

Statement 对象具体实现

在使用 Statement 对象执行 SQL 语句之前,需要使用 Connection 对象的 createStatement() 方法创建一个 Statement 对象,然后调用上述处理 Statement 类方法提交执行 SQL 。

Statement 操作 DDL

使用 Satemtnt 类创建数据库表。

@Test
public void testDDL() {
    String sql = "CREATE TABLE student(id BIGINT AUTO_INCREMENT, name VARCHAR(20), age INT, PRIMARY KEY(id), intro VARCHAR(64))";
    Connection connection = null; // 数据库连接对象
    Statement statement = null; // 用于执行静态 SQL 语句并返回它所生成结果的对象
    try {
        Class.forName("com.mysql.jdbc.Driver"); // 加载注册驱动
        connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root"); // 创建 MySQL 连接对象
        statement = connection.createStatement(); // 创建 Statement 对象,用于提交和接收 MySQL
        int row = statement.executeUpdate(sql); // 提交 SQL 语句,并接收执行结果
        System.out.println("数据库修改行数:" + row); // 执行创建表 SQL 影响行数为 0
    } catch (Exception e) { // 释放资源
        e.printStackTrace();
    } finally {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
Java7 新特性创建数据库表
@Test
public void testDDL_JAVA7() {
    String sql = "CREATE TABLE student(id BIGINT AUTO_INCREMENT, name VARCHAR(20), age INT, PRIMARY KEY(id))";
    try {
        Class.forName("com.mysql.jdbc.Driver");
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root"); Statement st = conn.createStatement();) {
            st.executeUpdate(sql);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Statement 操作 DML
@Test
public void testInsert() {
    String sql = "INSERT INTO t_student (id, name, age, intro) VALUES (null, '王五', 30, '王五是个好孩子')";
    Connection connection = null;
    Statement statement = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root");
        statement = connection.createStatement();
        statement.executeUpdate(sql);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
Statement 操作 DQL
@Test
public void testQuery() {
    String sql = "SELECT * FROM t_student";
    Connection connection = null;
    Statement statement = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root");
        statement = connection.createStatement();
        ResultSet query = statement.executeQuery(sql); // 接收查询返回的结果
        while (query.next()) { // 遍历返回结果集
            Long id = query.getLong("id"); // 根据列名获取结果
            String name = query.getString("name");
            Integer age = query.getInt("age");
            String intro = query.getString("intro");
            System.out.println(id + " " + name + " " + age + " " + intro);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

PreparedStatement 对象

PreparedStatement 接口扩展了 Statement 接口,它添加了比 Statement 对象更好一些优点的功能。此语句可以动态地提供 / 接受参数。(实例后面提供,将抽取重复代码,统一处理结果集和赋值)

创建 PreparedStatement 对象

JDBC 中的所有参数都由 ? 符号作为占位符,这被称为参数标记。在执行SQL语句之前,必须为每个参数(占位符)提供值。

setXXX() 方法将值绑定到参数,其中 XXX 表示要绑定到输入参数的值的 Java 数据类型。 如果忘记提供绑定值,则将会抛出一个 SQLException

每个参数标记是它其顺序位置引用。第一个标记表示位置 1,下一个位置 2 等等。该方法与 Java 数组索引不同(它不从 0 开始)。

所有 Statement 对象与数据库交互的方法 execute()executeQuery()executeUpdate() 也可以用于 PreparedStatement 对象。但是,这些方法被修改为可以使用输入参数的 SQL 语句。

关闭PreparedStatement对象

就像关闭 Statement 对象一样,由于同样的原因 (节省数据库系统资源),也应该关闭 PreparedStatement 对象。

简单的调用 close() 方法将执行关闭。 如果先关闭 Connection 对象,它也会关闭 PreparedStatement 对象。 但是,应该始终显式关闭 PreparedStatement 对象,以确保以正确顺序清理资源。

PreparedStatement pstmt = null;
try {
    String SQL = "Update Employees SET age = ? WHERE id = ?";
    pstmt = conn.prepareStatement(SQL);
    // TODO
}
catch (SQLException e) {
    // TODO
}
finally {
    pstmt.close();
}

CallableStatement 对象

创建 CallableStatement 对象

类似 Connection 对象创建 StatementPreparedStatement 对象一样,它还可以使用同样的方式创建 CallableStatement 对象,该对象将用于执行对数据库存储过程的调用。

留坑。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • JDBC概述 在Java中,数据库存取技术可分为如下几类:JDBC直接访问数据库、JDO技术、第三方O/R工具,如...
    usopp阅读 3,533评论 3 75
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,457评论 0 4
  • 本节介绍Statement接口及其子类PreparedStatement和CallableStatement。 它...
    zlb阅读 1,143评论 0 0
  • 在中国,有一群可爱的人 战争年代,用生命抵挡敌人的战火 和平年代,用身躯与天灾相搏 他们的名字叫:中国军人 他们的...
    静听若言阅读 329评论 0 3