探索JDBC:Java数据库连接的艺术与魅力

文章目录

    • 一.JDBC概述
      • [1. 基本介绍](#1. 基本介绍)
        • [1.1 JDBC的目标](#1.1 JDBC的目标)
        • [1.2 JDBC架构](#1.2 JDBC架构)
        • [1.3 JDBC驱动类型](#1.3 JDBC驱动类型)
        • [1.4 JDBC基本使用步骤](#1.4 JDBC基本使用步骤)
      • [2. 示例代码](#2. 示例代码)
      • [3. JDBC 带来的好处](#3. JDBC 带来的好处)
    • [二.获取数据库连接 5 种方式](#二.获取数据库连接 5 种方式)
    • 三.ResultSet
      • [3.1 ResultSet 的作用](#3.1 ResultSet 的作用)
      • [3.2 ResultSet 的类型](#3.2 ResultSet 的类型)
      • [3.3 ResultSet 的并发性](#3.3 ResultSet 的并发性)
      • [3.4 常用的 ResultSet 方法](#3.4 常用的 ResultSet 方法)
      • [3.5 示例代码](#3.5 示例代码)
    • 四.Sql注入
      • [4.1 SQL注入的原理](#4.1 SQL注入的原理)
      • [4.2 SQL注入的类型](#4.2 SQL注入的类型)
      • [4.3 防御SQL注入的方法](#4.3 防御SQL注入的方法)
    • 五.Statement
      • [5.1 Statement 接口的主要方法](#5.1 Statement 接口的主要方法)
      • [5.2 创建 Statement 对象](#5.2 创建 Statement 对象)
      • [5.3 使用 Statement 执行SQL语句](#5.3 使用 Statement 执行SQL语句)
      • [5.4 安全性和性能问题](#5.4 安全性和性能问题)
      • [5.5 资源管理](#5.5 资源管理)
    • [六. PreparedStatement](#六. PreparedStatement)
      • [6.1 PreparedStatement 的优势](#6.1 PreparedStatement 的优势)
      • [6.2 创建 PreparedStatement 对象](#6.2 创建 PreparedStatement 对象)
      • [6.3 设置参数](#6.3 设置参数)
      • [6.4 执行 PreparedStatement](#6.4 执行 PreparedStatement)
      • [6.5 使用示例](#6.5 使用示例)
      • [6.6 资源管理](#6.6 资源管理)
      • [6.7 总结](#6.7 总结)

🙋‍♂️ 作者:@whisperrr. 🙋‍♂️

👀 专栏:JDBC 👀

💥 标题:探索JDBC:Java数据库连接的艺术与魅力💥

❣️ 寄语:比较是偷走幸福的小偷❣️

一.JDBC概述

1. 基本介绍

JDBC(Java Database Connectivity)是Java语言中用于数据库连接和操作的一组标准API,它定义了一套标准的数据库访问接口,为Java程序提供了与数据库连接和执行SQL语句的能力。以下是JDBC的基本介绍:

1.1 JDBC的目标
  • 提供一种标准的数据库访问接口,使Java应用程序可以方便地访问各种数据库。
  • 实现数据库访问的跨平台性。
  • 简化数据库编程。
1.2 JDBC架构

JDBC架构包括以下四个层次:

  1. JDBC API:这是提供给Java程序员的接口,他们可以使用这些接口来连接数据库,执行SQL语句,处理结果集等。
  2. JDBC Driver Manager:负责管理不同数据库的JDBC驱动程序,根据请求为应用程序加载合适的驱动程序。
  3. JDBC Driver API:由数据库厂商提供,实现了JDBC API中定义的接口,用于与特定的数据库进行交互。
  4. 数据库:存储数据的系统,JDBC API通过驱动程序与之通信。
1.3 JDBC驱动类型

根据实现方式和功能,JDBC驱动程序可以分为以下四类:

  1. Type 1:JDBC-ODBC Bridge Driver
    • 将JDBC调用转换为ODBC调用。
    • 需要本地ODBC驱动程序。
    • 适用于实验和小型应用。
  2. Type 2:Native API Driver
    • 将JDBC调用转换为特定数据库的本地API调用。
    • 依赖于特定数据库的本地库。
    • 性能较好,但跨平台性较差。
  3. Type 3:Network Protocol Driver
    • 将JDBC调用转换为网络协议,通常由中间服务器转换为特定数据库的命令。
    • 跨平台性好,但可能存在性能瓶颈。
  4. Type 4:Thin Driver(纯Java驱动)
    • 直接将JDBC调用转换为数据库的网络协议。
    • 不需要额外的中间件或客户端软件。
    • 性能好,跨平台性好。
1.4 JDBC基本使用步骤
  1. 注册驱动 :通过Class.forName()加载驱动类。
  2. 建立连接 :通过DriverManager.getConnection()获取Connection对象。
  3. 创建语句 :通过Connection对象创建StatementPreparedStatement
  4. 执行查询 :通过StatementPreparedStatement执行SQL语句。
  5. 处理结果 :处理ResultSet对象中的查询结果。
  6. 关闭连接 :操作完成后,关闭ResultSetStatementConnection对象。

2. 示例代码

java 复制代码
import java.sql.*;
public class JDBCDemo {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try {
            // 1. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2. 建立连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb", "username", "password");
            // 3. 创建语句
            stmt = conn.createStatement();
            // 4. 执行查询
            ResultSet rs = stmt.executeQuery("SELECT * FROM example_table");
            // 5. 处理结果
            while (rs.next()) {
                System.out.println(rs.getString("column1") + ", " + rs.getString("column2"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭连接
            try {
                if (stmt != null) stmt.close();
                if (conn != null) conn.close();
            } catch (SQLException se) {
                se.printStackTrace();
            }
        }
    }
}

通过以上步骤,Java程序就可以使用JDBC与数据库进行交互了。在实际开发中,通常会使用更高级的数据库访问框架,如Hibernate、MyBatis等,来简化数据库操作和提高开发效率。

3. JDBC 带来的好处

JDBC带来的好处(示意图)

说明:JDBC是Java提供一套用于数据库操作的接口API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。

二.获取数据库连接 5 种方式

1.方式1

获取 Driver 实现类对象,属于静态加载,灵活性不够高,依赖性强。

java 复制代码
   public void connect01() throws SQLException {
        Driver driver = new Driver();

        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        Properties properties = new Properties();
        properties.setProperty("user","root");
        properties.setProperty("password","lrx");

        Connection connect = driver.connect(url, properties);
        System.out.println(connect);

    }

2.方式2

使用反射机制 Class<?> aClass = Class.forName("com.mysql.jdbc.Driver"),动态加载,可以更加灵活

java 复制代码
    public void connect02() throws Exception {
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) aClass.newInstance();

        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        Properties properties = new Properties();
        properties.setProperty("user","root");
        properties.setProperty("password","lrx");

        Connection connect = driver.connect(url, properties);
        System.out.println(connect);
    }

3.方式3

使用 DriverManager 替换 Driver,DriverManager 用于管理一组 JDBC 驱动程序的基本服务。

java 复制代码
public void connect03() throws Exception {
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) aClass.newInstance();

        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        String user = "root";
        String password = "lrx";

        DriverManager.registerDriver(driver);
        Connection connection 
        = DriverManager.getConnection(url, user, password);
        System.out.println(connection);
    }

4.方式4

使用反射加载 Driver 类,使用 DriverManager 替换 Driver ,注册加载的工作在加载 Driver 类时,底层已经完成,相比较方式3,更加简洁

java 复制代码
  public void connect04() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        String user = "root";
        String password = "lrx";

        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println(connection);
    }

5.方式5

使用配置文件,连接数据库

java 复制代码
 public void connect05() throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));

        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");

        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println(connection);

        String sql = "insert into news1 values (1,'111'),(2,'222')";
        Statement statement = connection.createStatement();
        int rows = statement.executeUpdate(sql);
        System.out.println(rows > 0 ? "成功" : "失败");
        statement.close();
        connection.close();
    }

三.ResultSet

1.ResultSet 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。

2.ResultSet 对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。

  1. next 方法将光标移动到下一行,并且由于在 ResultSet 对象中没有更多行时返回 false, 因此可以在while循环中使用循环来遍历结果集。

以下是对 ResultSet 的详细介绍:

3.1 ResultSet 的作用

  • 存储 SQL 查询返回的数据。
  • 允许应用程序通过移动光标来遍历结果集中的行。
  • 提供了访问当前行中不同列的数据的方法。

3.2 ResultSet 的类型

ResultSet 有几种类型,这些类型定义了结果集的滚动能力和更新能力:

  • TYPE_FORWARD_ONLY:光标只能向前移动。
  • TYPE_SCROLL_INSENSITIVE:光标可以向前和向后移动,但不反映对数据库的更改。
  • TYPE_SCROLL_SENSITIVE:光标可以向前和向后移动,并反映对数据库的更改。

3.3 ResultSet 的并发性

ResultSet 的并发性定义了其他线程或进程对结果集所做的更改是否可见:

  • CONCUR_READ_ONLY:结果集是只读的,不能更新。
  • CONCUR_UPDATABLE:结果集是可更新的。

3.4 常用的 ResultSet 方法

以下是一些常用的 ResultSet 方法:

  • next():将光标从当前位置向前移动一行。
  • previous():将光标从当前位置向后移动一行(仅在可滚动的结果集中可用)。
  • absolute(int row):将光标移动到指定的行号。
  • relative(int rows):相对于当前位置移动光标。
  • getRow():返回当前行的行号。
  • getString(String columnLabel):以 String 形式获取当前行指定列的值。
  • getInt(int columnIndex):以 int 形式获取当前行指定列的值。
  • getObject(int columnIndex):以 Object 形式获取当前行指定列的值。
  • updateString(int columnIndex, String x):更新当前行指定列的值为 String。
  • updateRow():更新结果集中的当前行。
  • insertRow():在结果集中插入一行。
  • deleteRow():从结果集中删除当前行。
  • close():关闭 ResultSet 对象并释放与之关联的资源。

3.5 示例代码

以下是如何使用 ResultSet 的一个简单示例:

java 复制代码
import java.sql.*;
public class ResultSetExample {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 加载驱动、建立连接、创建语句等步骤省略
            // 执行查询
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT id, name, age FROM users");
            // 遍历结果集
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                
                System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (rs != null) rs.close();
                if (stmt != null) stmt.close();
                if (conn != null) conn.close();
            } catch (SQLException se) {
                se.printStackTrace();
            }
        }
    }
}

底层存储:

在处理 ResultSet 时,需要注意资源的正确关闭,以避免潜在的资源泄漏问题。在实际应用中,通常会使用 try-with-resources 语句来自动关闭实现了 AutoCloseable 接口的资源。

四.Sql注入

SQL注入(SQL Injection)是一种常见的网络攻击技术,它主要针对基于SQL语言的数据库系统。攻击者通过在Web应用的输入字段中插入恶意的SQL代码,从而欺骗服务器执行非预期的SQL命令。以下是关于SQL注入的详细介绍:

4.1 SQL注入的原理

当应用程序直接将用户输入的数据拼接到SQL查询语句中,而没有进行适当的验证或转义时,就可能出现SQL注入漏洞。例如:

java 复制代码
String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";

如果用户输入的数据包含SQL关键字或特殊字符,如单引号('),它们可能会改变原始SQL语句的结构,导致安全漏洞。例如,如果用户输入的username' OR '1'='1,那么拼接后的SQL语句将变成:

sql 复制代码
SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = '...'

这里的--是SQL中的注释符号,它将忽略后面的内容,从而使攻击者绕过身份验证。

4.2 SQL注入的类型

  • 基于错误的SQL注入:攻击者故意输入错误的SQL语法,通过错误信息了解数据库结构。
  • 基于布尔的SQL注入:攻击者通过真或假的布尔表达式来推断数据库信息。
  • 基于时间的SQL注入:攻击者利用SQL语句的执行时间来推断数据库信息。
  • 盲注:攻击者无法直接从服务器获取信息,只能通过服务器响应的差异来推断信息。

4.3 防御SQL注入的方法

  • 使用预编译的SQL语句(PreparedStatement):这是预防SQL注入最有效的方法。预编译的语句会先对SQL模板进行编译,然后再将用户输入作为参数绑定到模板中,从而避免了直接拼接SQL语句。

    java 复制代码
    String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setString(1, username);
    pstmt.setString(2, password);
    ResultSet rs = pstmt.executeQuery();
  • 输入验证:对所有用户输入进行严格的验证,只接受符合预期格式的数据。

  • 使用存储过程:在数据库层面使用存储过程可以减少SQL注入的风险,因为存储过程可以限制输入类型和执行权限。

  • 最小权限原则:数据库连接应该使用最小权限的账户,以减少攻击者成功注入SQL后的潜在损害。

  • 错误处理:不要在应用程序中显示数据库错误信息给用户,以免泄露数据库结构信息。

  • 使用ORM框架 :对象关系映射(ORM)框架通常会处理SQL语句的构造,减少SQL注入的风险。

    通过采取上述措施,可以有效地减少应用程序遭受SQL注入攻击的风险。

五.Statement

在Java的JDBC API中,Statement是一个接口,它用于执行不带参数的SQL语句并返回执行结果。以下是对Statement的详细介绍:

5.1 Statement 接口的主要方法

  • execute(String sql): 执行给定的SQL语句,该语句可能返回多个结果集,也可能不返回结果集,例如DDL语句。
  • executeQuery(String sql): 执行查询数据库的SQL语句,并返回单个ResultSet对象。
  • executeUpdate(String sql): 执行更新数据库的SQL语句(INSERT、UPDATE、DELETE等),并返回一个整数,表示受影响的行数。
  • addBatch(String sql): 将给定的SQL命令添加到当前的命令批次中。
  • executeBatch(): 执行当前批次中的所有命令,并返回一个整数数组,表示每条命令受影响的行数。
  • close(): 立即释放此Statement对象的数据库和JDBC资源,而不是等待它们自动释放。

5.2 创建 Statement 对象

要使用Statement,首先需要通过Connection对象创建它:

java 复制代码
Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement();

5.3 使用 Statement 执行SQL语句

以下是如何使用Statement执行不同类型的SQL语句的示例:

java 复制代码
// 执行查询
String query = "SELECT * FROM employees";
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
    // 处理结果集
}
// 执行更新
String update = "UPDATE employees SET salary = salary * 1.1 WHERE department = 'Engineering'";
int rowsUpdated = stmt.executeUpdate(update);
// 执行多个更新
stmt.addBatch("INSERT INTO employees (name, department) VALUES ('Alice', 'HR')");
stmt.addBatch("INSERT INTO employees (name, department) VALUES ('Bob', 'Finance')");
int[] rowsAffected = stmt.executeBatch();

5.4 安全性和性能问题

虽然Statement接口使用方便,但它存在一些安全和性能问题:

  • SQL注入风险 :由于Statement直接将用户输入拼接到SQL语句中,因此容易受到SQL注入攻击。
  • 性能问题 :每次执行SQL语句时,Statement都需要重新编译SQL语句,这可能会降低性能。
    为了解决这些问题,推荐使用PreparedStatement,它是Statement的子接口,提供了更好的安全性和性能:
  • 防止SQL注入PreparedStatement使用参数化查询,可以有效地防止SQL注入。
  • 性能提升PreparedStatement可以预编译SQL语句,多次执行时不需要重新编译。

5.5 资源管理

在使用完Statement对象后,应该及时关闭它以释放数据库资源:

java 复制代码
stmt.close();

在实际应用中,通常使用try-with-resources语句来自动关闭实现了AutoCloseable接口的资源:

java 复制代码
try (Statement stmt = conn.createStatement()) {
    // 使用stmt执行SQL语句
}
// stmt会在try块结束时自动关闭

六. PreparedStatement

PreparedStatement 是 Java JDBC API 中的一个接口,它是 Statement 的子接口,提供了比 Statement 更强大的功能,尤其是在安全性、性能和灵活性方面。以下是 PreparedStatement 的详细介绍:

6.1 PreparedStatement 的优势

  1. 参数化查询PreparedStatement 允许你使用占位符(?)来代替直接在 SQL 语句中插入值,这样可以有效地防止 SQL 注入攻击。
  2. 性能提升 :使用 PreparedStatement 可以预编译 SQL 语句,多次执行相同的 SQL 语句时,不需要再次编译,从而提高性能。
  3. 类型安全PreparedStatement 提供了设置参数类型的方法,确保了数据类型的一致性。

6.2 创建 PreparedStatement 对象

要创建 PreparedStatement 对象,需要通过 Connection 对象的 prepareStatement 方法:

java 复制代码
Connection conn = DriverManager.getConnection(url, username, password);
String sql = "SELECT * FROM employees WHERE department = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);

6.3 设置参数

在执行 SQL 语句之前,需要为 PreparedStatement 中的占位符设置值:

java 复制代码
pstmt.setString(1, "Engineering"); // 设置第一个占位符的值为 "Engineering"

参数的索引从 1 开始。

6.4 执行 PreparedStatement

PreparedStatement 提供了与 Statement 类似的方法来执行 SQL 语句:

  • execute(): 执行任何类型的 SQL 语句。
  • executeQuery(): 执行查询语句并返回 ResultSet
  • executeUpdate(): 执行 INSERT、UPDATE 或 DELETE 语句,并返回受影响的行数。
java 复制代码
ResultSet rs = pstmt.executeQuery(); // 执行查询
int rowsUpdated = pstmt.executeUpdate(); // 执行更新

6.5 使用示例

以下是一个使用 PreparedStatement 的完整示例:

java 复制代码
try (Connection conn = DriverManager.getConnection(url, username, password);
     PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM employees WHERE department = ?")) {
    
    pstmt.setString(1, "Engineering");
    ResultSet rs = pstmt.executeQuery();
    
    while (rs.next()) {
        // 处理结果集
    }
} catch (SQLException e) {
    e.printStackTrace();
}

6.6 资源管理

Statement 一样,使用 PreparedStatement 后,应该关闭它以释放数据库资源。使用 try-with-resources 语句可以自动关闭资源:

java 复制代码
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
    // 使用 pstmt 执行 SQL 语句
}
// pstmt 在 try 块结束时自动关闭

6.7 总结

PreparedStatement 是处理数据库操作时推荐使用的接口,因为它提供了更好的安全性、性能和易用性。在处理用户输入和执行多次相同的 SQL 语句时,它尤其有用。

相关推荐
m0_748255023 分钟前
基于python对网页进行爬虫简单教程
开发语言·爬虫·python
A XMan.4 分钟前
JSON结构快捷转XML结构API集成指南
xml·java·前端·json·php
小林爱10 分钟前
【Compose multiplatform教程06】用IDEA编译Compose Multiplatform常见问题
android·java·前端·kotlin·intellij-idea·compose·多平台
hacker7072 小时前
探索数据的艺术:R语言与Origin的完美结合
开发语言·r语言·origin
计算机毕设定制辅导-无忧学长2 小时前
Redis 在小型项目中的实战运用
数据库·redis·mybatis
小冯的编程学习之路2 小时前
【Redis】:初识Redis
数据库·redis·缓存
炸鸡配泡面3 小时前
Qt 12.28 day3
java·开发语言
get_money_3 小时前
代码随想录Day37 动态规划:完全背包理论基础,518.零钱兑换II,本周小结动态规划,377. 组合总和 Ⅳ,70. 爬楼梯(进阶版)。
java·笔记·算法·动态规划
get_money_3 小时前
代码随想录38 322. 零钱兑换,279.完全平方数,本周小结动态规划,139.单词拆分,动态规划:关于多重背包,你该了解这些!背包问题总结篇。
java·开发语言·笔记·算法·动态规划
Maiko Star4 小时前
MySQL管理
数据库·mysql·oracle