Java之数据库连接桥梁JDBC学习笔记

JDBC调用

Java与数据库的连接桥梁是JDBC(Java Database Connectivity)。JDBC是Java编程语言中用于连接和执行数据库操作的API(应用程序编程接口)。它提供了一种标准的方法,允许Java程序与各种数据库(如MySQL、PostgreSQL、Oracle、SQL Server等)进行交互。

JDBC主要包括以下几个核心组件:

  1. JDBC Driver:JDBC驱动程序是Java应用程序与数据库之间的通信接口。不同的数据库有各自特定的JDBC驱动程序,例如MySQL有MySQL Connector/J,Oracle有Oracle JDBC驱动程序。

  2. DriverManager:DriverManager类用于管理一组JDBC驱动程序,使用这个类可以从已注册的驱动程序列表中选择合适的驱动程序。

  3. Connection:Connection对象表示与特定数据库的连接。通过这个对象可以创建SQL语句、执行SQL查询,并处理结果集。

  4. Statement:Statement对象用于执行SQL语句。它有几个子接口,如PreparedStatement(用于预编译SQL语句)和CallableStatement(用于调用存储过程)。

  5. ResultSet:ResultSet对象用于存储从数据库查询中获得的数据。它提供了各种方法来遍历和处理这些数据。

简单的JDBC使用示例

以下是一个简单的Java代码示例,展示如何使用JDBC连接到MySQL数据库并执行查询:

java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JdbcExample {
    public static void main(String[] args) {
        // 数据库URL,用户名和密码
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String user = "root";
        String password = "password";

        try {
            // 加载并注册JDBC驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立连接
            Connection connection = DriverManager.getConnection(url, user, password);

            // 创建Statement对象
            Statement statement = connection.createStatement();

            // 执行查询
            String query = "SELECT * FROM mytable";
            ResultSet resultSet = statement.executeQuery(query);

            // 处理结果集
            while (resultSet.next()) {
                System.out.println("Column1: " + resultSet.getString("column1"));
                System.out.println("Column2: " + resultSet.getInt("column2"));
            }

            // 关闭连接
            resultSet.close();
            statement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意事项

  • 确保你有合适的JDBC驱动程序,并将其包含在你的项目类路径中。
  • 根据具体的数据库类型,URL格式会有所不同。
  • 处理数据库连接时,应始终使用适当的异常处理和资源关闭方法,以确保资源正确释放。

SQL注入攻击

SQL注入漏洞(SQL Injection)是一种通过将恶意SQL代码插入到应用程序的输入字段中,并将其传递给后台数据库执行,从而在未经授权的情况下访问或操作数据库的攻击方式。这种漏洞通常出现在动态构建SQL查询的应用程序中,攻击者可以利用它来绕过身份验证、检索敏感数据、篡改数据,甚至在某些情况下执行系统命令。

SQL注入的工作原理

SQL注入攻击的基本原理是利用应用程序未正确处理用户输入的漏洞,将恶意的SQL代码注入到查询中。例如,假设有一个用户登录系统,其SQL查询如下:

sql 复制代码
SELECT * FROM users WHERE username = 'admin' AND password = 'password';

如果应用程序直接将用户输入的usernamepassword值拼接到查询字符串中,而没有进行适当的转义或参数化处理,攻击者可以输入如下内容:

  • 用户名:admin' --
  • 密码:anything

这将导致查询变成:

sql 复制代码
SELECT * FROM users WHERE username = 'admin' --' AND password = 'anything';

在SQL中,--表示注释,后面的内容会被忽略。这样,原本需要验证用户名和密码的查询被修改为只验证用户名,使得攻击者能够绕过密码验证。

SQL注入的类型

  1. 基于联合查询的SQL注入(Union-based SQL Injection) :利用UNION关键字将恶意查询与原始查询合并,以检索更多数据。
  2. 基于错误的SQL注入(Error-based SQL Injection):利用数据库错误消息来获取有关数据库结构的信息。
  3. 布尔盲注(Boolean-based Blind SQL Injection):通过观察应用程序行为的变化来推断查询结果的真伪。
  4. 时间盲注(Time-based Blind SQL Injection):通过引入时间延迟来推断查询的执行情况。

防范措施

  1. 使用参数化查询:确保应用程序使用参数化查询或预编译语句(如使用PDO或PreparedStatement),而不是直接拼接SQL字符串。
  2. 使用ORM框架:使用ORM(对象关系映射)框架,这些框架通常会自动处理输入的转义和参数化。
  3. 输入验证与转义:对用户输入进行严格的验证和转义,防止恶意代码注入。
  4. 最小权限原则:数据库用户应具有最小权限,防止因注入攻击导致的严重后果。
  5. 安全配置:禁用不必要的数据库功能和错误信息,减少攻击者获取数据库结构信息的机会。

防范SQL注入攻击更新JDBC调用

如何使用PreparedStatement

为了防范SQL注入攻击,可以通过使用PreparedStatement 代替Statement对象来执行SQL查询。PreparedStatement允许我们使用参数化查询,从而避免将用户输入直接拼接到SQL语句中,这大大减少了SQL注入攻击的风险。

下面是更新后的代码,展示了如何使用PreparedStatement:

java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JdbcExample {
    public static void main(String[] args) {
        // 数据库URL,用户名和密码
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String user = "root";
        String password = "password";

        try {
            // 加载并注册JDBC驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立连接
            Connection connection = DriverManager.getConnection(url, user, password);

            // 使用PreparedStatement对象
            String query = "SELECT * FROM mytable WHERE column1 = ?";
            PreparedStatement preparedStatement = connection.prepareStatement(query);

            // 设置参数值
            String column1Value = "someValue"; // 假设这是从用户输入获取的值
            preparedStatement.setString(1, column1Value);

            // 执行查询
            ResultSet resultSet = preparedStatement.executeQuery();

            // 处理结果集
            while (resultSet.next()) {
                System.out.println("Column1: " + resultSet.getString("column1"));
                System.out.println("Column2: " + resultSet.getInt("column2"));
            }

            // 关闭连接
            resultSet.close();
            preparedStatement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这段代码中:

  1. 使用PreparedStatement代替Statement对象。
  2. 使用?作为占位符,并通过preparedStatement.setString(1, column1Value)设置参数值。
  3. 这样可以确保用户输入的值被正确转义,防止恶意SQL代码注入。

更新或者删除数据

更新操作

我们将使用PreparedStatement来执行一个UPDATE语句,更新表中的某一列值。

java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class JdbcUpdateExample {
    public static void main(String[] args) {
        // 数据库URL,用户名和密码
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String user = "root";
        String password = "password";

        try {
            // 加载并注册JDBC驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立连接
            Connection connection = DriverManager.getConnection(url, user, password);

            // 更新操作
            String updateQuery = "UPDATE mytable SET column2 = ? WHERE column1 = ?";
            PreparedStatement updateStatement = connection.prepareStatement(updateQuery);

            // 设置参数值
            int newValue = 42; // 新的值
            String column1Value = "someValue"; // 需要更新的行的标识符

            updateStatement.setInt(1, newValue);
            updateStatement.setString(2, column1Value);

            // 执行更新
            int rowsUpdated = updateStatement.executeUpdate();
            System.out.println("Rows updated: " + rowsUpdated);

            // 关闭连接
            updateStatement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

删除操作

我们将使用PreparedStatement来执行一个DELETE语句,删除表中的某一行。

java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class JdbcDeleteExample {
    public static void main(String[] args) {
        // 数据库URL,用户名和密码
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String user = "root";
        String password = "password";

        try {
            // 加载并注册JDBC驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立连接
            Connection connection = DriverManager.getConnection(url, user, password);

            // 删除操作
            String deleteQuery = "DELETE FROM mytable WHERE column1 = ?";
            PreparedStatement deleteStatement = connection.prepareStatement(deleteQuery);

            // 设置参数值
            String column1Value = "someValue"; // 需要删除的行的标识符

            deleteStatement.setString(1, column1Value);

            // 执行删除
            int rowsDeleted = deleteStatement.executeUpdate();
            System.out.println("Rows deleted: " + rowsDeleted);

            // 关闭连接
            deleteStatement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意事项

  • 参数化查询 :在这两个操作中,我们都使用了参数化查询(?占位符),这样可以有效防止SQL注入。
  • 错误处理 :代码包含了基本的错误处理,使用try-catch块捕获并打印异常。
  • 资源管理:确保在操作完成后关闭PreparedStatement和Connection对象,以释放数据库资源。

通过以上代码示例,你可以安全地执行数据库的更新和删除操作,同时有效防止SQL注入攻击。

JDBC事务操作

在JDBC中,事务是一组要么全部执行,要么全部不执行的操作。事务有助于维护数据库的完整性和一致性。在JDBC中,我们主要使用两种事务模式:

  1. 自动提交模式(Auto-commit Mode)
  2. 手动提交模式(Manual-commit Mode)

1. 自动提交模式(Auto-commit Mode)

在自动提交模式下,每个独立的SQL语句都是一个事务,在语句执行完成后会自动提交。这是JDBC的默认事务模式。

示例代码
java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class AutoCommitExample {
    public static void main(String[] args) {
        // 数据库URL,用户名和密码
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String user = "root";
        String password = "password";

        try {
            // 加载并注册JDBC驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立连接
            Connection connection = DriverManager.getConnection(url, user, password);

            // 自动提交模式(默认开启)
            Statement statement = connection.createStatement();
            statement.executeUpdate("UPDATE mytable SET column2 = 10 WHERE column1 = 'value1'");
            statement.executeUpdate("DELETE FROM mytable WHERE column1 = 'value2'");

            // 自动提交模式下,不需要显式提交
            statement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. 手动提交模式(Manual-commit Mode)

在手动提交模式下,必须显式地启动和结束事务。这种模式适用于需要确保一组SQL语句要么全部成功要么全部失败的情况。

示例代码
java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class ManualCommitExample {
    public static void main(String[] args) {
        // 数据库URL,用户名和密码
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String user = "root";
        String password = "password";

        try {
            // 加载并注册JDBC驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立连接
            Connection connection = DriverManager.getConnection(url, user, password);

            // 关闭自动提交模式
            connection.setAutoCommit(false);

            // 执行一组操作
            String updateQuery = "UPDATE mytable SET column2 = ? WHERE column1 = ?";
            PreparedStatement updateStatement = connection.prepareStatement(updateQuery);
            updateStatement.setInt(1, 10);
            updateStatement.setString(2, "value1");
            updateStatement.executeUpdate();

            String deleteQuery = "DELETE FROM mytable WHERE column1 = ?";
            PreparedStatement deleteStatement = connection.prepareStatement(deleteQuery);
            deleteStatement.setString(1, "value2");
            deleteStatement.executeUpdate();

            // 提交事务
            connection.commit();

            // 关闭连接
            updateStatement.close();
            deleteStatement.close();
            connection.close();
        } catch (Exception e) {
            try {
                // 回滚事务
                connection.rollback();
            } catch (Exception rollbackEx) {
                rollbackEx.printStackTrace();
            }
            e.printStackTrace();
        }
    }
}

总结

  • 自动提交模式适用于简单的、无需事务控制的操作。
  • 手动提交模式适用于需要事务控制的复杂操作,确保一组SQL操作要么全部成功要么全部失败。

在实际应用中,选择哪种事务模式取决于具体的业务需求和操作的复杂性。使用手动提交模式时,一定要记得在操作成功时提交事务,在操作失败时进行回滚。

JDBC批处理

在JDBC中,批处理(Batch Processing)允许我们在一次数据库连接中执行多条SQL语句,从而提高性能和效率,特别是在需要执行大量重复性操作的情况下。批处理通常用于插入、更新或删除大量记录。

JDBC批处理的基本步骤

  1. 创建数据库连接。
  2. 关闭自动提交模式。
  3. 创建StatementPreparedStatement对象。
  4. 添加SQL命令到批处理中。
  5. 执行批处理。
  6. 提交事务。
  7. 处理异常并回滚事务(如果有)。
  8. 关闭资源。

使用PreparedStatement进行批处理

以下是一个使用PreparedStatement进行批处理的示例代码:

java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JdbcPreparedBatchExample {
    public static void main(String[] args) {
        // 数据库URL,用户名和密码
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String user = "root";
        String password = "password";

        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            // 加载并注册JDBC驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立连接
            connection = DriverManager.getConnection(url, user, password);

            // 关闭自动提交模式
            connection.setAutoCommit(false);

            // 创建PreparedStatement对象
            String insertQuery = "INSERT INTO mytable (column1, column2) VALUES (?, ?)";
            preparedStatement = connection.prepareStatement(insertQuery);

            // 添加SQL命令到批处理中
            preparedStatement.setString(1, "value1");
            preparedStatement.setInt(2, 1);
            preparedStatement.addBatch();

            preparedStatement.setString(1, "value2");
            preparedStatement.setInt(2, 2);
            preparedStatement.addBatch();

            // 执行批处理
            int[] updateCounts = preparedStatement.executeBatch();

            // 提交事务
            connection.commit();

            // 处理更新计数
            for (int count : updateCounts) {
                System.out.println("Update count: " + count);
            }
        } catch (Exception e) {
            try {
                if (connection != null) {
                    // 回滚事务
                    connection.rollback();
                }
            } catch (SQLException rollbackEx) {
                rollbackEx.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                // 关闭资源
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException closeEx) {
                closeEx.printStackTrace();
            }
        }
    }
}

注意事项

  • 关闭自动提交模式:批处理操作通常需要关闭自动提交模式,以便在一次事务中执行多条SQL语句。
  • 异常处理和回滚:确保在出现异常时正确回滚事务,以防止数据不一致。
  • 资源管理 :确保在操作完成后关闭StatementPreparedStatementConnection对象,以释放数据库资源。

通过使用批处理,可以显著提高执行大量SQL操作的性能和效率。

Ali的Druid连接池

数据库连接池是一种用于管理和优化数据库连接的技术,通过池化连接来减少连接创建和关闭的开销,从而提高应用程序的性能和可扩展性。Druid和C3P0是Java中常用的数据库连接池实现。下面分别介绍如何使用Druid和C3P0连接池。

Druid连接池

Druid是阿里巴巴开源的一个高性能数据库连接池,支持多种数据库,提供强大的监控和扩展功能。

添加依赖

如果使用Maven构建项目,可以在pom.xml中添加Druid依赖:

xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version> <!-- 请根据需要选择合适的版本 -->
</dependency>

配置Druid连接池

配置可以通过Java代码或配置文件来完成。以下是通过Java代码配置Druid连接池的示例:

java 复制代码
import com.alibaba.druid.pool.DruidDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class DruidExample {
    public static void main(String[] args) {
        DruidDataSource dataSource = new DruidDataSource();
        
        // 配置数据库连接池
        dataSource.setUrl("jdbc:mysql://localhost:3306/mydatabase");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        
        // 配置连接池大小
        dataSource.setInitialSize(5);
        dataSource.setMaxActive(10);
        dataSource.setMinIdle(5);
        dataSource.setMaxWait(60000);

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        
        try {
            // 获取连接
            connection = dataSource.getConnection();
            
            // 执行查询
            String query = "SELECT * FROM mytable WHERE column1 = ?";
            preparedStatement = connection.prepareStatement(query);
            preparedStatement.setString(1, "someValue");
            resultSet = preparedStatement.executeQuery();
            
            // 处理结果集
            while (resultSet.next()) {
                System.out.println("Column1: " + resultSet.getString("column1"));
                System.out.println("Column2: " + resultSet.getInt("column2"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try { if (resultSet != null) resultSet.close(); } catch (Exception e) { e.printStackTrace(); }
            try { if (preparedStatement != null) preparedStatement.close(); } catch (Exception e) { e.printStackTrace(); }
            try { if (connection != null) connection.close(); } catch (Exception e) { e.printStackTrace(); }
        }
    }
}
相关推荐
车载测试工程师12 分钟前
CAPL学习-SOME/IP交互层-符号数据库访问类函数
学习·tcp/ip·以太网·capl·canoe
摇滚侠14 分钟前
面试实战 问题三十四 对称加密 和 非对称加密 spring 拦截器 spring 过滤器
java·spring·面试
xqqxqxxq15 分钟前
Java 集合框架之线性表(List)实现技术笔记
java·笔记·python
L0CK23 分钟前
RESTful风格解析
java
程序员小假32 分钟前
我们来说说 ThreadLocal 的原理,使用场景及内存泄漏问题
java·后端
何中应35 分钟前
LinkedHashMap使用
java·后端·缓存
tryxr42 分钟前
Java 多线程标志位的使用
java·开发语言·volatile·内存可见性·标志位
暗然而日章1 小时前
C++基础:Stanford CS106L学习笔记 13 特殊成员函数(SMFs)
c++·笔记·学习
太行山有西瓜汁1 小时前
达梦DTS工具:批量导出与导入DDL脚本完整指南
运维·服务器·数据库
talenteddriver1 小时前
java: Java8以后hashmap扩容后根据高位确定元素新位置
java·算法·哈希算法