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

[复制链接]
发表于 2025-11-18 10:19:09 | 显示全部楼层 |阅读模式

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

👀 专栏:JDBC 👀

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

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

  一.JDBC概述

1. 根本先容

JDBC(Java Database Connectivity)是Java语言中用于数据库毗连和操纵的一组尺度API,它界说了一套尺度的数据库访问接口,为Java步调提供了与数据库毗连和实行SQL语句的本事。以下是JDBC的根本先容:
1.1 JDBC的目的



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

JDBC架构包罗以下四个条理:

  • JDBC API:这是提供给Java步调员的接口,他们可以利用这些接口来毗连数据库,实行SQL语句,处置惩罚结果集等。
  • JDBC Driver Manager:负责管理差别数据库的JDBC驱动步调,根据哀求为应用步调加载符合的驱动步调。
  • JDBC Driver API:由数据库厂商提供,实现了JDBC API中界说的接口,用于与特定的数据库举行交互。
  • 数据库存储数据的体系,JDBC API通过驱动步调与之通讯。
1.3 JDBC驱动范例

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

  • Type 1:JDBC-ODBC Bridge Driver

    • 将JDBC调用转换为ODBC调用。
    • 须要当地ODBC驱动步调。
    • 实用于实行和小型应用。

  • Type 2:Native API Driver

    • 将JDBC调用转换为特定命据库的当地API调用。
    • 依靠于特定命据库的当地库。
    • 性能较好,但跨平台性较差。

  • Type 3:Network Protocol Driver

    • 将JDBC调用转换为网络协议,通常由中央服务器转换为特定命据库的下令。
    • 跨平台性好,但大概存在性能瓶颈。

  • Type 4:Thin Driver(纯Java驱动)

    • 直接将JDBC调用转换为数据库的网络协议。
    • 不须要额外的中央件或客户端软件。
    • 性能好,跨平台性好。

1.4 JDBC根本利用步调


  • 注册驱动:通过Class.forName()加载驱动类。
  • 创建毗连:通过DriverManager.getConnection()获取Connection对象。
  • 创建语句:通过Connection对象创建Statement或PreparedStatement。
  • 实行查询:通过Statement或PreparedStatement实行SQL语句。
  • 处置惩罚结果:处置惩罚ResultSet对象中的查询结果。
  • 关闭毗连:操纵完成后,关闭ResultSet、Statement和Connection对象。
2. 示例代码

  1. import java.sql.*;
  2. public class JDBCDemo {
  3.     public static void main(String[] args) {
  4.         Connection conn = null;
  5.         Statement stmt = null;
  6.         try {
  7.             // 1. 加载驱动
  8.             Class.forName("com.mysql.cj.jdbc.Driver");
  9.             // 2. 建立连接
  10.             conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb", "username", "password");
  11.             // 3. 创建语句
  12.             stmt = conn.createStatement();
  13.             // 4. 执行查询
  14.             ResultSet rs = stmt.executeQuery("SELECT * FROM example_table");
  15.             // 5. 处理结果
  16.             while (rs.next()) {
  17.                 System.out.println(rs.getString("column1") + ", " + rs.getString("column2"));
  18.             }
  19.         } catch (Exception e) {
  20.             e.printStackTrace();
  21.         } finally {
  22.             // 6. 关闭连接
  23.             try {
  24.                 if (stmt != null) stmt.close();
  25.                 if (conn != null) conn.close();
  26.             } catch (SQLException se) {
  27.                 se.printStackTrace();
  28.             }
  29.         }
  30.     }
  31. }
复制代码
通过以上步调,Java步调就可以利用JDBC与数据库举行交互了。在实际开发中,通常会利用更高级的数据库访问框架,如Hibernate、MyBatis等,来简化数据库操纵和进步开发服从。
3. JDBC 带来的长处

JDBC带来的长处(表示图)

   阐明:JDBC是Java提供一套用于数据库操纵的接口API,Java步调员只须要面向这套接口编程即可。差别的数据库厂商,须要针对这套接口,提供差别实现。
  二.获取数据库毗连 5 种方式

1.方式1

获取 Driver 实现类对象,属于静态加载,机动性不敷高,依靠性强。
  1.    public void connect01() throws SQLException {
  2.         Driver driver = new Driver();
  3.         String url = "jdbc:mysql://localhost:3306/hsp_db02";
  4.         Properties properties = new Properties();
  5.         properties.setProperty("user","root");
  6.         properties.setProperty("password","lrx");
  7.         Connection connect = driver.connect(url, properties);
  8.         System.out.println(connect);
  9.     }
复制代码
2.方式2

利用反射机制 Class<?> aClass = Class.forName(“com.mysql.jdbc.Driver”),动态加载,可以更加机动
  1.     public void connect02() throws Exception {
  2.         Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
  3.         Driver driver = (Driver) aClass.newInstance();
  4.         String url = "jdbc:mysql://localhost:3306/hsp_db02";
  5.         Properties properties = new Properties();
  6.         properties.setProperty("user","root");
  7.         properties.setProperty("password","lrx");
  8.         Connection connect = driver.connect(url, properties);
  9.         System.out.println(connect);
  10.     }
复制代码
3.方式3

利用 DriverManager 更换 Driver,DriverManager 用于管理一组 JDBC 驱动步调的根本服务。
  1. public void connect03() throws Exception {
  2.         Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
  3.         Driver driver = (Driver) aClass.newInstance();
  4.         String url = "jdbc:mysql://localhost:3306/hsp_db02";
  5.         String user = "root";
  6.         String password = "lrx";
  7.         DriverManager.registerDriver(driver);
  8.         Connection connection
  9.         = DriverManager.getConnection(url, user, password);
  10.         System.out.println(connection);
  11.     }
复制代码
4.方式4

利用反射加载 Driver 类,利用 DriverManager 更换 Driver ,注册加载的工作在加载 Driver 类时,底层已经完成,相比力方式3,更加轻便
  1.   public void connect04() throws Exception {
  2.         Class.forName("com.mysql.jdbc.Driver");
  3.         String url = "jdbc:mysql://localhost:3306/hsp_db02";
  4.         String user = "root";
  5.         String password = "lrx";
  6.         Connection connection = DriverManager.getConnection(url, user, password);
  7.         System.out.println(connection);
  8.     }
复制代码
5.方式5

利用设置文件,毗连数据库
  1. public void connect05() throws Exception {
  2.         Properties properties = new Properties();
  3.         properties.load(new FileInputStream("src\\mysql.properties"));
  4.         String url = properties.getProperty("url");
  5.         String user = properties.getProperty("user");
  6.         String password = properties.getProperty("password");
  7.         String driver = properties.getProperty("driver");
  8.         Class.forName("com.mysql.jdbc.Driver");
  9.         Connection connection = DriverManager.getConnection(url, user, password);
  10.         System.out.println(connection);
  11.         String sql = "insert into news1 values (1,'111'),(2,'222')";
  12.         Statement statement = connection.createStatement();
  13.         int rows = statement.executeUpdate(sql);
  14.         System.out.println(rows > 0 ? "成功" : "失败");
  15.         statement.close();
  16.         connection.close();
  17.     }
复制代码
三.ResultSet

   1.ResultSet 表现数据库结果集的数据表,通常通过实行查询数据库的语句天生。
2.ResultSet 对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。
3. 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 的一个简朴示例:
  1. import java.sql.*;
  2. public class ResultSetExample {
  3.     public static void main(String[] args) {
  4.         Connection conn = null;
  5.         Statement stmt = null;
  6.         ResultSet rs = null;
  7.         try {
  8.             // 加载驱动、建立连接、创建语句等步骤省略
  9.             // 执行查询
  10.             stmt = conn.createStatement();
  11.             rs = stmt.executeQuery("SELECT id, name, age FROM users");
  12.             // 遍历结果集
  13.             while (rs.next()) {
  14.                 int id = rs.getInt("id");
  15.                 String name = rs.getString("name");
  16.                 int age = rs.getInt("age");
  17.                
  18.                 System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
  19.             }
  20.         } catch (SQLException e) {
  21.             e.printStackTrace();
  22.         } finally {
  23.             // 关闭资源
  24.             try {
  25.                 if (rs != null) rs.close();
  26.                 if (stmt != null) stmt.close();
  27.                 if (conn != null) conn.close();
  28.             } catch (SQLException se) {
  29.                 se.printStackTrace();
  30.             }
  31.         }
  32.     }
  33. }
复制代码
底层存储

在处置惩罚 ResultSet 时,须要留意资源的精确关闭,以制止埋伏的资源走漏标题。在实际应用中,通常会利用 try-with-resources 语句来主动关闭实现了 AutoCloseable 接口的资源。
四.Sql注入

SQL注入(SQL Injection)是一种常见的网络攻击技能,它重要针对基于SQL语言的数据库体系。攻击者通过在Web应用的输入字段中插入恶意的SQL代码,从而诱骗服务器实行非预期的SQL下令。以下是关于SQL注入的具体先容:
4.1 SQL注入的原理

当应用步调直接将用户输入的数据拼接到SQL查询语句中,而没有举行得当的验证或转义时,就大概出现SQL注入毛病。比方:
  1. String username = request.getParameter("username");
  2. String password = request.getParameter("password");
  3. String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
复制代码
假如用户输入的数据包罗SQL关键字或特别字符,如单引号('),它们大概会改变原始SQL语句的布局,导致安全毛病。比方,假如用户输入的username是' OR '1'='1,那么拼接后的SQL语句将变成:
  1. 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语句。
    1. String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
    2. PreparedStatement pstmt = connection.prepareStatement(sql);
    3. pstmt.setString(1, username);
    4. pstmt.setString(2, password);
    5. 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对象创建它:
  1. Connection conn = DriverManager.getConnection(url, username, password);
  2. Statement stmt = conn.createStatement();
复制代码
5.3 利用 Statement 实行SQL语句

以下是怎样利用Statement实行差别范例的SQL语句的示例:
  1. // 执行查询
  2. String query = "SELECT * FROM employees";
  3. ResultSet rs = stmt.executeQuery(query);
  4. while (rs.next()) {
  5.     // 处理结果集
  6. }
  7. // 执行更新
  8. String update = "UPDATE employees SET salary = salary * 1.1 WHERE department = 'Engineering'";
  9. int rowsUpdated = stmt.executeUpdate(update);
  10. // 执行多个更新
  11. stmt.addBatch("INSERT INTO employees (name, department) VALUES ('Alice', 'HR')");
  12. stmt.addBatch("INSERT INTO employees (name, department) VALUES ('Bob', 'Finance')");
  13. int[] rowsAffected = stmt.executeBatch();
复制代码
5.4 安全性和性能标题

固然Statement接口利用方便,但它存在一些安全和性能标题:


  • SQL注入风险:由于Statement直接将用户输入拼接到SQL语句中,因此容易受到SQL注入攻击。
  • 性能标题:每次实行SQL语句时,Statement都须要重新编译SQL语句,这大概会低落性能。
    为了办理这些标题,保举利用PreparedStatement,它是Statement的子接口,提供了更好的安全性和性能:
  • 防止SQL注入:PreparedStatement利用参数化查询,可以有用地防止SQL注入。
  • 性能提升:PreparedStatement可以预编译SQL语句,多次实行时不须要重新编译。
5.5 资源管理

在利用完Statement对象后,应该及时关闭它以开释数据库资源:
  1. stmt.close();
复制代码
在实际应用中,通常利用try-with-resources语句来主动关闭实现了AutoCloseable接口的资源:
  1. try (Statement stmt = conn.createStatement()) {
  2.     // 使用stmt执行SQL语句
  3. }
  4. // stmt会在try块结束时自动关闭
复制代码
六. PreparedStatement

PreparedStatement 是 Java JDBC API 中的一个接口,它是 Statement 的子接口,提供了比 Statement 更强大的功能,尤其是在安全性、性能和机动性方面。以下是 PreparedStatement 的具体先容:
6.1 PreparedStatement 的上风


  • 参数化查询:PreparedStatement 允许你利用占位符(?)来代替直接在 SQL 语句中插入值,如允许以有用地防止 SQL 注入攻击。
  • 性能提升:利用 PreparedStatement 可以预编译 SQL 语句,多次实行雷同的 SQL 语句时,不须要再次编译,从而进步性能。
  • 范例安全:PreparedStatement 提供了设置参数范例的方法,确保了数据范例的同等性。
6.2 创建 PreparedStatement 对象

要创建 PreparedStatement 对象,须要通过 Connection 对象的 prepareStatement 方法:
  1. Connection conn = DriverManager.getConnection(url, username, password);
  2. String sql = "SELECT * FROM employees WHERE department = ?";
  3. PreparedStatement pstmt = conn.prepareStatement(sql);
复制代码
6.3 设置参数

在实行 SQL 语句之前,须要为 PreparedStatement 中的占位符设置值:
  1. pstmt.setString(1, "Engineering"); // 设置第一个占位符的值为 "Engineering"
复制代码
参数的索引从 1 开始。
6.4 实行 PreparedStatement

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


  • execute(): 实行任何范例的 SQL 语句。
  • executeQuery(): 实行查询语句并返回 ResultSet。
  • executeUpdate(): 实行 INSERT、UPDATE 或 DELETE 语句,并返回受影响的行数。
  1. ResultSet rs = pstmt.executeQuery(); // 执行查询
  2. int rowsUpdated = pstmt.executeUpdate(); // 执行更新
复制代码
6.5 利用示例

以下是一个利用 PreparedStatement 的完备示例:
  1. try (Connection conn = DriverManager.getConnection(url, username, password);
  2.      PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM employees WHERE department = ?")) {
  3.    
  4.     pstmt.setString(1, "Engineering");
  5.     ResultSet rs = pstmt.executeQuery();
  6.    
  7.     while (rs.next()) {
  8.         // 处理结果集
  9.     }
  10. } catch (SQLException e) {
  11.     e.printStackTrace();
  12. }
复制代码
6.6 资源管理

与 Statement 一样,利用 PreparedStatement 后,应该关闭它以开释数据库资源。利用 try-with-resources 语句可以主动关闭资源:
  1. try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
  2.     // 使用 pstmt 执行 SQL 语句
  3. }
  4. // pstmt 在 try 块结束时自动关闭
复制代码
6.7 总结

PreparedStatement 是处置惩罚数据库操纵时保举利用的接口,由于它提供了更好的安全性、性能和易用性。在处置惩罚用户输入和实行多次雷同的 SQL 语句时,它尤其有用。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表